pax_global_header00006660000000000000000000000064135773744360014535gustar00rootroot0000000000000052 comment=077e652de43ad962be0454d7be347816f7482739 cppcheck-1.90/000077500000000000000000000000001357737443600132465ustar00rootroot00000000000000cppcheck-1.90/.astylerc000077500000000000000000000007741357737443600151100ustar00rootroot00000000000000############################################################## # Documentation is available from # # http://astyle.sourceforge.net/astyle.html # ############################################################## ### Options ### --pad-header --unpad-paren --suffix=none --convert-tabs --attach-inlines --attach-classes --attach-namespaces ### Settings ### --style=kr --indent=spaces=4 --indent-namespaces --lineend=linux --min-conditional-indent=0 cppcheck-1.90/.codacy.yml000066400000000000000000000003171357737443600153120ustar00rootroot00000000000000exclude_paths: - addons/test/** - addons/y2038/test/*.c - htmlreport/example.cc - samples/**/bad.c - samples/**/bad.cpp - test/test.cxx - test/cfg/*.c - test/cfg/*.cpp - test/synthetic/*.c cppcheck-1.90/.gitignore000066400000000000000000000025651357737443600152460ustar00rootroot00000000000000*.bak *.gcno *.o *.pyc cppcheck cppcheck.exe dmake dmake.exe reduce reduce.exe tags testrunner testrunner.exe tools/daca2*.html tools/dmake tools/errmsg tools/extracttests # dump files generated by Cppcheck *.*.dump # VS generated files *.aps *.idb *.ncb *.obj *.opensdf *.orig *.pdb *.sdf *.suo *.user .vs/ # VS build folders bin/ Build/ BuildTmp/ cli/temp/ ipch/ lib/temp/ test/temp/ # XCode build folders and files *.mode[0-9]v[0-9] *.pbxuser build/ # GUI build folders gui/debug/ gui/release/ gui/temp/ # Other (generated) GUI files gui/*.qm gui/cppcheck-gui gui/cppcheck-gui.exe gui/gui.sln gui/gui.vcproj gui/Makefile gui/Makefile.debug gui/Makefile.release gui/qrc_gui.cpp gui/test/Makefile gui/test/*/Makefile gui/test/*/*/Makefile gui/test/benchmark/simple/benchmark-simple gui/test/filelist/test-filelist gui/test/projectfile/test-projectfile gui/test/translationhandler/test-translationhandler gui/test/xmlreport/test-xmlreport gui/test/xmlreportv1/test-xmlreportv1 gui/test/xmlreportv2/test-xmlreportv2 # Doxygen output folder doxyoutput/ # qmake generated htmlreport/.tox/ htmlreport/MANIFEST # Backup files and stuff from patches *.orig *.rej *~ # kdevelop 4.x *.kdev4 # Common cmake build directories build* # Temporal files *.swp # Snapcraft build part prime parts stage *.snap snap/.snapcraft # Manual folder man/manual.log man/manual.tex # CLion .idea /.metadata/ cppcheck-1.90/.mailmap000066400000000000000000000054471357737443600147010ustar00rootroot00000000000000Andreas Bießmann Andrew Martin acm4me Ankita Gupta Ankita-gupta Benjamin Goose Daniel Marjamäki Daniel Marjamäki Daniel Marjamäki Daniel Marjam�ki Daniel Marjamäki Daniel Marjamäki Deepak Gupta deepak gupta Ettl Martin Martin Ettl Ettl Martin Ettl Martin Martin Ettl Frank Zingsheim Gianluca Scacco Gianluca Scacco Henrik Nilsson Kimmo Varis Kimmo varis Kimmo Varis Kimmo Varis Kimmo Varis Kimmo Varis Kimmo Varis Kimmo Varis Leandro Penz Leandro Lisboa Penz Leandro Penz Leandro Lisboa Penz makulik unknown Nicolas Le Cam Pete Johns PKEuS Philipp K PKEuS Philipp Kloke PKEuS Reijo Tomperi Robert Reif Ryan Pavlik Sébastien Debrard seb777 Sébastien Debrard S�bastien Debrard Sébastien Debrard Debrard Sébastien Stefan Weil Tim Gerundt Vesa Pikki XhmikosR Zachary Blair Zachary Blair Zachary Blair zblair cppcheck-1.90/.travis.yml000066400000000000000000000262671357737443600153740ustar00rootroot00000000000000language: cpp dist: xenial compiler: - gcc - clang env: global: - ORIGINAL_CXXFLAGS="-pedantic -Wall -Wextra -Wabi -Wcast-qual -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar -D_GLIBCXX_DEBUG -g " # unfortunately we need this to stay within 50min timelimit given by travis. - CXXFLAGS="${ORIGINAL_CXXFLAGS} -O2 -march=native -Wstrict-aliasing=2 -Werror=strict-aliasing" - CPPCHECK=${TRAVIS_BUILD_DIR}/cppcheck - ASAN_OPTIONS=detect_stack_use_after_return=1 - UBSAN_OPTIONS=print_stacktrace=1 matrix: - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" MAKEFLAGS="HAVE_RULES=yes" MATCHCOMPILER=yes VERIFY=1 before_install: # install needed deps - travis_retry sudo apt-get update -qq - travis_retry sudo apt-get install -qq python3-pip qt5-default qt5-qmake qtbase5-dev qtcreator libxml2-utils libpcre3 gdb unzip wx-common xmlstarlet python3-dev liblua5.3-dev libcurl3 libcairo2-dev libsigc++-2.0-dev tidy libopencv-dev # Python 2 modules - travis_retry python2 -m pip install --user pytest==4.6.4 - travis_retry python2 -m pip install --user pylint - travis_retry python2 -m pip install --user unittest2 - travis_retry python2 -m pip install --user pexpect # imported by tools/ci.py - travis_retry python2 -m pip install --user pygments - travis_retry python2 -m pip install --user semver # Python 3 modules - travis_retry python3 -m pip install --user setuptools --upgrade - travis_retry python3 -m pip install --user pytest - travis_retry python3 -m pip install --user pylint - travis_retry python3 -m pip install --user unittest2 - travis_retry python3 -m pip install --user pexpect # imported by tools/ci.py - travis_retry python3 -m pip install --user requests # imported by tools/pr.py - travis_retry python3 -m pip install --user pygments - travis_retry sudo python3 -m pip install demjson # installs jsonlint => sudo required - travis_retry python3 -m pip install --user semver matrix: # do notify immediately about it when a job of a build fails. fast_finish: true allow_failures: - name: "rerun dmake?" compiler: clang # 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 test checkcfg -j 2 # -fsanitize=address - name: "address sanitizers" compiler: gcc script: - CXXFLAGS="-fsanitize=address -Og -g3" make cppcheck test checkcfg -j 2 # check a lot of stuff that only needs to be checked in a single configuration - name: "misc" compiler: clang script: - make -j 2 # run extra tests - tools/generate_and_run_more_tests.sh # check for missing CWE entries - make checkCWEEntries # check cfg files - make checkcfg # check platform files - make validatePlatforms # Validate Result XML - make validateXML # Validate rule files - make validateRules # check htmlreport stuff - ./htmlreport/test_htmlreport.py - cd htmlreport - ./check.sh - cd ../ # check if DESTDIR works TODO: actually execute this - mkdir install_test - echo $CXXFLAGS - make -s DESTDIR=install_test FILESDIR=/usr/share/cppcheck install # rm everything - git clean -dfx # check what happens if we want to install it to some other dir, - echo $CXXFLAGS - make -s MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck -j 2 - sudo make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck install # check if it actually works: - /usr/bin/cppcheck ./cli # check if showtime=top5 works - ./tools/test_showtimetop5.sh # check matchcompiler - ./tools/test_matchcompiler.py # 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 - pylint --rcfile=pylintrc_travis addons/*.py - pylint --rcfile=pylintrc_travis htmlreport/cppcheck-htmlreport - pylint --rcfile=pylintrc_travis htmlreport/*.py - pylint --rcfile=pylintrc_travis --ignore=donate-cpu-server.py tools/*.py - python3 -m pylint --rcfile=pylintrc_travis addons/*.py - python3 -m pylint --rcfile=pylintrc_travis htmlreport/cppcheck-htmlreport - python3 -m pylint --rcfile=pylintrc_travis htmlreport/*.py - python3 -m pylint --rcfile=pylintrc_travis tools/*.py # check python syntax by compiling some selected scripts - python -m py_compile ./tools/donate-cpu.py - python3 -m py_compile ./tools/donate-cpu.py - python3 -m py_compile ./tools/donate-cpu-server.py # check addons/misc.py - cd addons/test - ${CPPCHECK} --dump misc-test.cpp - python3 ../misc.py -verify misc-test.cpp.dump - cd ../../ # check addons/cert.py - cd addons/test - ${CPPCHECK} --dump cert-test.c - python3 ../cert.py -verify cert-test.c.dump - ${CPPCHECK} --dump cert-test.cpp - python3 ../cert.py -verify cert-test.cpp.dump - cd ../../ # check addons/misra.py - cd addons/test - ${CPPCHECK} --dump misra/misra-test.c - python3 ../misra.py -verify misra/misra-test.c.dump - ${CPPCHECK} --dump misra/misra-test.cpp - python3 ../misra.py -verify misra/misra-test.cpp.dump - python ../misra.py --rule-texts=misra/misra2012_rules_dummy_ascii.txt -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_ascii.txt -verify misra/misra-test.cpp.dump - python ../misra.py --rule-texts=misra/misra2012_rules_dummy_utf8.txt -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_utf8.txt -verify misra/misra-test.cpp.dump - python ../misra.py --rule-texts=misra/misra2012_rules_dummy_windows1250.txt -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_windows1250.txt -verify misra/misra-test.cpp.dump - cd ../../ # check addons/naming.py - cd addons/test - ${CPPCHECK} --dump naming_test.c - python3 ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump - ${CPPCHECK} --dump naming_test.cpp - python3 ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump - cd ../.. # check addons/namingng.py - cd addons/test - ${CPPCHECK} --dump namingng_test.c - python3 ../namingng.py --configfile ../naming.json --verify namingng_test.c.dump - cd ../.. # try CMake - mkdir cmake.output - cd cmake.output - cmake -G "Unix Makefiles" -DBUILD_TESTS=On .. - make -j2 check - cd .. # -funsigned-char - make clean - make -j2 CXXFLAGS=-funsigned-char testrunner - ./testrunner TestSymbolDatabase # check .json files - find . -name '*.json' -not -path '*/\.*' | xargs jsonlint -s # check if dmake needs to be rerun (this job may fail) - name: "rerun dmake?" compiler: clang # we don't need to install any deps for dmake so skip it explicitly before_install: - true script: - echo "If the following command fails, run 'make dmake; make run-dmake' and commit the resulting change." - make -s dmake -j2 - make -s run-dmake - git diff --exit-code # check if cppcheck builds on trusty - name: "make ubuntu 14.04 trusty" compiler: gcc dist: trusty before_install: - travis_retry sudo apt-get update -qq - travis_retry sudo apt-get install libxml2-utils libpcre3 script: - CXX=g++ CXXFLAGS="${ORIGINAL_CXXFLAGS}" make cppcheck testrunner -j 2 - make clean - CXX=clang++ CXXFLAGS="${ORIGINAL_CXXFLAGS}" make cppcheck testrunner -j 2 # check if cppcheck builds on osx - name: "make osx" os: osx before_install: - true script: - CXX=g++ CXXFLAGS="${ORIGINAL_CXXFLAGS}" make cppcheck testrunner -j 2 - make clean - CXX=clang++ CXXFLAGS="${ORIGINAL_CXXFLAGS}" make cppcheck testrunner -j 2 script: # fail the entire job as soon as one of the subcommands exits non-zero to save time and resources - set -e # Determine CPU count. As of January 2019 it seems to be 2 - so use this for job count in suitable calls to make or cppcheck - cat /proc/cpuinfo # check with TEST_MATHLIB_VALUE enabled - touch lib/mathlib.cpp test/testmathlib.cpp - echo $CXXFLAGS - make -s test -j2 CPPFLAGS=-DTEST_MATHLIB_VALUE - touch lib/mathlib.cpp test/testmathlib.cpp # syntax checking of cppcheck source code with -DNONNEG - ls lib/*.cpp | xargs -n 1 -P 2 g++ -fsyntax-only -std=c++0x -Ilib -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml -DNONNEG # compile cppcheck, default build - echo $CXXFLAGS - make -s test -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} -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} -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} -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 && ./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 - python -m pytest test-*.py - cd - # Testing addons - python -m pytest addons/test/test-*.py - PYTHONPATH=./addons python3 -m pytest addons/test/test-*.py notifications: irc: channels: - "irc.freenode.org#cppcheck" template: - "[%{commit} : %{author}] %{message}" - "%{build_url}" skip_join: true cppcheck-1.90/.travis_llvmcheck_suppressions000066400000000000000000000001501357737443600214400ustar00rootroot00000000000000unreadVariable shadowVar shadowFunction unusedStructMember nullPointer uninitvar noExplicitConstructor cppcheck-1.90/.travis_suppressions000066400000000000000000000012771357737443600174230ustar00rootroot00000000000000unusedPrivateFunction:test/testbufferoverrun.cpp unusedPrivateFunction:test/testcmdlineparser.cpp redundantNextPrevious:test/testtoken.cpp simplePatternError:test/testtoken.cpp noValidConfiguration shadowFunction functionConst functionStatic bitwiseOnBoolean # temporary suppressions - fix the warnings! duplicateBranch:lib/checkunusedvar.cpp duplicateBranch:lib/tokenize.cpp missingOverride redundantAssignment:lib/tokenlist.cpp unreachableCode:lib/checkbufferoverrun.cpp unreachableCode:lib/checkclass.cpp unreachableCode:test/testother.cpp unusedPrivateFunction:lib/checkbufferoverrun.h unusedPrivateFunction:test/test*.cpp useStlAlgorithm *:gui/test/* *:test/test.cxx *:test/cfg/* *:externals/*/* cppcheck-1.90/AUTHORS000066400000000000000000000104501357737443600143160ustar00rootroot00000000000000The cppcheck team, in alphabetical order: Abhishek Bharadwaj Abigail Buccaneer Adam J Richter Adrien Chardon Ahti Legonkov Akhilesh Nema Akio Idehara Albert Aribaud Aleksandr Pikalev Aleksey Palazhchenko Alexander Alekseev Alexander Gushchin Alexander Mai Alexander Tkachev Alexey Eryomenko Alexey Zhikhartsev Ali Can Demiralp Ameen Ali Andreas Bacher Andreas Bießmann Andreas Pokorny Andreas Rönnquist Andreas Vollenweider Andrei Karas Andrew C. Martin Andy Maloney Aneesh Azhakesan S Ankita Gupta Antti Tuppurainen Anurag Garg Armin Müller Arpit Chaudhary August Sodora Ayaz Salikhov Balázs Tóth Baris Demiray Bartlomiej Grzeskowiak bbennetts Benjamin Bannier Benjamin Fovet Benjamin Goose Benjamin Kramer Benjamin Woester Benjamin Wolsey Ben T Bill Egert Björge Dijkstra Bo Rydberg booga Boris Barbulovski Boris Egorov Carlo Marcelo Arenas Belón Carlos Gomes Martinho Cary R Changkyoon Kim Christian Ehrlicher Christian Franke Christoph Schmidt Christoph Strehle Chuck Larson Colomban Wendling Conrado Gouvea daisuke-chiba Daniel Marjamäki David Hallas David Korth Debrard Sebastien Deepak Gupta dencat Diego de las Heras Dirk Jagdmann Dmitriy Dmitry Marakasov Dmitry-Me Duraffort Edoardo Prezioso Eivind Tagseth Elbert Pol Emmanuel Blot Eric Sesterhenn Erik Hovland Erik Lax Ettl Martin Evgeny Mandrikov Felipe Pena Felix Geyer Florin Iucha Francesc Elies Frank Zingsheim fu7mu4 Galimov Albert Garrett Bodily Gary Leutheuser gaurav kaushik Gennady Feldman Georgy Komarov Gerhard Zlabinger Gianfranco Costamagna Gianluca Scacco Gleydson Soares Goran Džaferi Graham Whitted Greg Hewgill Guillaume Miossec Gustav Palmqvist Günther Makulik Harald Scheidl Heiko Eißfeldt Heinrich Schuchardt Henrik Nilsson He Yuqi Hoang Tuan Su Igor Zhukov Ivan Maidanski Iván Matellanes Ivan Ryabov Jakub Melka Jan Hellwig János Maros Jay Sigbrandt Jens Bäckman Jérémy Lefaure Jes Ramsing Jim Zhou Johan Samuelson John Marshall John-Paul Ore John Smits Jonathan Clohessy Jonathan Neuschäfer Jonathan Thackray Jose Roquette Joshua Beck Joshua Rogers Julian Santander Jure Menart Jussi Lehtola Jørgen Kvalsvik Kamil Dudka Kartik Bajaj Ken-Patrick Lehrmann Kevin Christian Kevin Kendzia Kimmo Varis Konrad Grochowski Konrad Windszus Kyle Chisholm Kumar Ashwani larudwer Lau bakman Lauri Nurmi Leandro Lisboa Penz Lena Herscheid Lieven de Cock lioncash Lucas Manuel Rodriguez Luis Díaz Más Luxon Jean-Pierre Maksim Derbasov Malcolm Parsons Marc-Antoine Perennou Marcel Raad Marek Zmysłowski Marian Klymov Mark de Wever Markus Elfring Martin Ettl Martin Exner Martin Güthle Martin Herren Márton Csordás Masafumi Koba Massimo Paladin Mateusz Pusz Mathias De Maré Matthias Krüger Matthias Schmieder Mathias Schmid Matt Johnson Mavik Miika-Petteri Matikainen Mika Attila Mike Tzou Milhan Kim Mischa Aster Alff Mohit Mate Monika Lukow Moritz Barsnick Moritz Lipp Moshe Kaplan ms Neszt Tibor Nguyen Duong Tuan Ni2c2k Nick Ridgway Nicolás Alvarez Nicolas Le Cam Nilesh Kumar Ogawa KenIchi Oleksandr Redko Oliver Schode Oliver Stöneberg Olivier Croquette Paul Fultz II Pavel Bibergal Pavel Pimenov Pavel Roschin Pavol Misik Pete Johns Peter Pentchev Philipp Kloke Pierre Schweitzer Pino Toscano Pranav Khanna Ramzan Bekbulatov Raphael Geissert Reijo Tomperi Richard A. Smith Richard Quirk Rick van der Sluijs Rikard Falkeborn rivdsl Robert Habrich Robert Morin Roberto Martelloni Robert Reif rofl0r Roman Zaytsev Borisovich root Rosen Penev Rudolf Grauberger Ryan Pavlik Samir Aguiar Sam Truscott Samuel Degrande Sandeep Dutta Scott Furry Sebastian Held Sebastian Matuschka Sébastien Debrard Simon Cornell Simon Kagstrom Simon Martin Simon Shanks Slava Semushin Stas Cymbalov Stefan Beller Stefan Naewe Stefan Weil Stéphane Michel Steve Browne Steve Duan Steve Mokris Steven Cook Steven Myint Susi Lehtola Swasti Shrivastava Sylvain Joubert Tam Do Thanh Teddy Didé Thomas Arnhold Thomas Jarosch Thomas Niederberger Thomas Otto Thomas Sondergaard Thorsten Sick Tim Gerundt tititiou36 Tobias Weibel Tom Pollok Toralf Förster Troshin V.S. Tyson Nottingham Valerii Lashmanov Vasily Maslyukov Veli-Matti Visuri WenChung Chiu Vesa Pikki Ville-Pekka Vahteala Ville Skyttä Vincent Le Garrec Wolfgang Stöggl x29a XhmikosR Xuecheng Zhang Yurii Putin Zachary Blair Zhao Qifa Zhiyuan Zhang Дмитрий Старцев GUI graphics courtesy of Tango Desktop Project: http://tango.freedesktop.org cppcheck-1.90/CMakeLists.txt000066400000000000000000000015501357737443600160070ustar00rootroot00000000000000project(Cppcheck) cmake_minimum_required(VERSION 2.8.11) include(GNUInstallDirs) include(cmake/versions.cmake REQUIRED) include(cmake/options.cmake REQUIRED) include(cmake/findDependencies.cmake REQUIRED) include(cmake/compileroptions.cmake REQUIRED) include(cmake/compilerDefinitions.cmake REQUIRED) include(cmake/buildFiles.cmake REQUIRED) include(cmake/cxx11.cmake REQUIRED) use_cxx11() file(GLOB addons "addons/*.py") file(GLOB cfgs "cfg/*.cfg") file(GLOB platforms "platforms/*.xml") if (BUILD_TESTS) enable_testing() endif() add_subdirectory(externals/tinyxml) add_subdirectory(externals/simplecpp) add_subdirectory(lib) # CppCheck Library add_subdirectory(cli) # Client application add_subdirectory(test) # Tests ADD_SUBDIRECTORY(gui) # Graphical application ADD_SUBDIRECTORY(tools/triage) # Triage tool include(cmake/printInfo.cmake REQUIRED) cppcheck-1.90/COPYING000066400000000000000000001045131357737443600143050ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . cppcheck-1.90/Cppcheck.xcodeproj/000077500000000000000000000000001357737443600167625ustar00rootroot00000000000000cppcheck-1.90/Cppcheck.xcodeproj/project.pbxproj000066400000000000000000002331311357737443600220410ustar00rootroot00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 2D190F271C03A7A600C18478 /* astutils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D190F251C03A7A600C18478 /* astutils.cpp */; }; 39E60EB91270DE3A00AC0D02 /* checkautovariables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E8D1270DE3A00AC0D02 /* checkautovariables.cpp */; }; 39E60EBA1270DE3A00AC0D02 /* checkbufferoverrun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E8F1270DE3A00AC0D02 /* checkbufferoverrun.cpp */; }; 39E60EBB1270DE3A00AC0D02 /* checkclass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E911270DE3A00AC0D02 /* checkclass.cpp */; }; 39E60EBC1270DE3A00AC0D02 /* checkexceptionsafety.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E931270DE3A00AC0D02 /* checkexceptionsafety.cpp */; }; 39E60EBD1270DE3A00AC0D02 /* checkmemoryleak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E951270DE3A00AC0D02 /* checkmemoryleak.cpp */; }; 39E60EBF1270DE3A00AC0D02 /* checkother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E991270DE3A00AC0D02 /* checkother.cpp */; }; 39E60EC01270DE3A00AC0D02 /* checkpostfixoperator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E9B1270DE3A00AC0D02 /* checkpostfixoperator.cpp */; }; 39E60EC11270DE3A00AC0D02 /* checkstl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E9D1270DE3A00AC0D02 /* checkstl.cpp */; }; 39E60EC21270DE3A00AC0D02 /* checkunusedfunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E9F1270DE3A00AC0D02 /* checkunusedfunctions.cpp */; }; 39E60EC31270DE3A00AC0D02 /* cppcheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EA11270DE3A00AC0D02 /* cppcheck.cpp */; }; 39E60EC41270DE3A00AC0D02 /* errorlogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EA31270DE3A00AC0D02 /* errorlogger.cpp */; }; 39E60EC81270DE3A00AC0D02 /* mathlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EAB1270DE3A00AC0D02 /* mathlib.cpp */; }; 39E60EC91270DE3A00AC0D02 /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EAD1270DE3A00AC0D02 /* path.cpp */; }; 39E60ECA1270DE3A00AC0D02 /* preprocessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EAF1270DE3A00AC0D02 /* preprocessor.cpp */; }; 39E60ECB1270DE3A00AC0D02 /* settings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EB11270DE3A00AC0D02 /* settings.cpp */; }; 39E60ECC1270DE3A00AC0D02 /* timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EB31270DE3A00AC0D02 /* timer.cpp */; }; 39E60ECD1270DE3A00AC0D02 /* token.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EB51270DE3A00AC0D02 /* token.cpp */; }; 39E60ECE1270DE3A00AC0D02 /* tokenize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EB71270DE3A00AC0D02 /* tokenize.cpp */; }; 39E60ED61270DE5000AC0D02 /* cmdlineparser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60ECF1270DE5000AC0D02 /* cmdlineparser.cpp */; }; 39E60ED71270DE5000AC0D02 /* cppcheckexecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60ED11270DE5000AC0D02 /* cppcheckexecutor.cpp */; }; 39E60ED81270DE5000AC0D02 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60ED31270DE5000AC0D02 /* main.cpp */; }; 39E60ED91270DE5000AC0D02 /* threadexecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60ED41270DE5000AC0D02 /* threadexecutor.cpp */; }; F4043DB1177F08A800CD5A40 /* filelister.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DAD177F08A800CD5A40 /* filelister.cpp */; }; F4043DB2177F08A800CD5A40 /* pathmatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DAF177F08A800CD5A40 /* pathmatch.cpp */; }; F4043DD7177F093300CD5A40 /* check64bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DB3177F093300CD5A40 /* check64bit.cpp */; }; F4043DD8177F093300CD5A40 /* checkassert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DB5177F093300CD5A40 /* checkassert.cpp */; }; F4043DDA177F093300CD5A40 /* checkbool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DB9177F093300CD5A40 /* checkbool.cpp */; }; F4043DDB177F093300CD5A40 /* checkboost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DBB177F093300CD5A40 /* checkboost.cpp */; }; F4043DDC177F093300CD5A40 /* checkinternal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DBD177F093300CD5A40 /* checkinternal.cpp */; }; F4043DDD177F093300CD5A40 /* checkio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DBF177F093300CD5A40 /* checkio.cpp */; }; F4043DDE177F093300CD5A40 /* checkleakautovar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC1177F093300CD5A40 /* checkleakautovar.cpp */; }; F4043DE0177F093300CD5A40 /* checknullpointer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC5177F093300CD5A40 /* checknullpointer.cpp */; }; F4043DE1177F093300CD5A40 /* checksizeof.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC7177F093300CD5A40 /* checksizeof.cpp */; }; F4043DE2177F093300CD5A40 /* checkuninitvar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC9177F093300CD5A40 /* checkuninitvar.cpp */; }; F4043DE3177F093300CD5A40 /* checkunusedvar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DCB177F093300CD5A40 /* checkunusedvar.cpp */; }; F4043DE4177F093300CD5A40 /* suppressions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DCE177F093300CD5A40 /* suppressions.cpp */; }; F4043DE5177F093300CD5A40 /* symboldatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DD0177F093300CD5A40 /* symboldatabase.cpp */; }; F4043DE6177F093300CD5A40 /* templatesimplifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DD2177F093300CD5A40 /* templatesimplifier.cpp */; }; F4043DE7177F093300CD5A40 /* tokenlist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DD4177F093300CD5A40 /* tokenlist.cpp */; }; F47E508317896AEB00C684DC /* tinyxml2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F47E508117896AEB00C684DC /* tinyxml2.cpp */; }; F497C28C1AB41D5C003B96CF /* check.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F497C2831AB41D5C003B96CF /* check.cpp */; }; F497C28D1AB41D5C003B96CF /* checkcondition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F497C2841AB41D5C003B96CF /* checkcondition.cpp */; }; F497C28E1AB41D5C003B96CF /* checkstring.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F497C2861AB41D5C003B96CF /* checkstring.cpp */; }; F497C28F1AB41D5C003B96CF /* checktype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F497C2881AB41D5C003B96CF /* checktype.cpp */; }; F497C2901AB41D5C003B96CF /* checkvaarg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F497C2891AB41D5C003B96CF /* checkvaarg.cpp */; }; F4C348661825679E00521683 /* cmdlineparser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60ECF1270DE5000AC0D02 /* cmdlineparser.cpp */; }; F4C348671825679F00521683 /* cppcheckexecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60ED11270DE5000AC0D02 /* cppcheckexecutor.cpp */; }; F4C348681825679F00521683 /* filelister.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DAD177F08A800CD5A40 /* filelister.cpp */; }; F4C348691825679F00521683 /* pathmatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DAF177F08A800CD5A40 /* pathmatch.cpp */; }; F4C3486A1825679F00521683 /* threadexecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60ED41270DE5000AC0D02 /* threadexecutor.cpp */; }; F4C3486C1825692B00521683 /* options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD8717AB8511006C06AF /* options.cpp */; }; F4C3486D1825692B00521683 /* test64bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD8917AB8511006C06AF /* test64bit.cpp */; }; F4C3486E1825692B00521683 /* testassert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD8A17AB8511006C06AF /* testassert.cpp */; }; F4C3486F1825692B00521683 /* testassignif.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD8B17AB8511006C06AF /* testassignif.cpp */; }; F4C348701825692B00521683 /* testautovariables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD8C17AB8511006C06AF /* testautovariables.cpp */; }; F4C348711825692B00521683 /* testbool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD8D17AB8511006C06AF /* testbool.cpp */; }; F4C348721825692B00521683 /* testboost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD8E17AB8511006C06AF /* testboost.cpp */; }; F4C348731825692B00521683 /* testbufferoverrun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD8F17AB8511006C06AF /* testbufferoverrun.cpp */; }; F4C348741825692B00521683 /* testcharvar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9017AB8511006C06AF /* testcharvar.cpp */; }; F4C348751825692B00521683 /* testclass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9117AB8511006C06AF /* testclass.cpp */; }; F4C348761825692B00521683 /* testcmdlineparser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9217AB8511006C06AF /* testcmdlineparser.cpp */; }; F4C348771825692B00521683 /* testconstructors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9317AB8511006C06AF /* testconstructors.cpp */; }; F4C348781825692B00521683 /* testcppcheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9417AB8511006C06AF /* testcppcheck.cpp */; }; F4C348791825692B00521683 /* testdivision.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9517AB8511006C06AF /* testdivision.cpp */; }; F4C3487A1825692B00521683 /* testerrorlogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9617AB8511006C06AF /* testerrorlogger.cpp */; }; F4C3487B1825692B00521683 /* testexceptionsafety.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9717AB8511006C06AF /* testexceptionsafety.cpp */; }; F4C3487C1825692B00521683 /* testfilelister.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9817AB8511006C06AF /* testfilelister.cpp */; }; F4C3487D1825692B00521683 /* testincompletestatement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9917AB8511006C06AF /* testincompletestatement.cpp */; }; F4C3487E1825692B00521683 /* testinternal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9A17AB8511006C06AF /* testinternal.cpp */; }; F4C3487F1825692B00521683 /* testio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9B17AB8511006C06AF /* testio.cpp */; }; F4C348801825692B00521683 /* testleakautovar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9C17AB8511006C06AF /* testleakautovar.cpp */; }; F4C348811825692B00521683 /* testmathlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9D17AB8511006C06AF /* testmathlib.cpp */; }; F4C348821825692B00521683 /* testmemleak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9E17AB8511006C06AF /* testmemleak.cpp */; }; F4C348831825692B00521683 /* testnonreentrantfunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDD9F17AB8511006C06AF /* testnonreentrantfunctions.cpp */; }; F4C348841825692B00521683 /* testnullpointer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA017AB8511006C06AF /* testnullpointer.cpp */; }; F4C348851825692B00521683 /* testobsolescentfunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA117AB8511006C06AF /* testobsolescentfunctions.cpp */; }; F4C348861825692B00521683 /* testoptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA217AB8511006C06AF /* testoptions.cpp */; }; F4C348871825692B00521683 /* testother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA317AB8511006C06AF /* testother.cpp */; }; F4C348881825692B00521683 /* testpath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA417AB8511006C06AF /* testpath.cpp */; }; F4C348891825692B00521683 /* testpathmatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA517AB8511006C06AF /* testpathmatch.cpp */; }; F4C3488A1825692B00521683 /* testpostfixoperator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA617AB8511006C06AF /* testpostfixoperator.cpp */; }; F4C3488B1825692B00521683 /* testpreprocessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA717AB8511006C06AF /* testpreprocessor.cpp */; }; F4C3488C1825692B00521683 /* testrunner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA817AB8511006C06AF /* testrunner.cpp */; }; F4C3488D1825692B00521683 /* testsimplifytokens.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDA917AB8511006C06AF /* testsimplifytokens.cpp */; }; F4C3488E1825692B00521683 /* testsizeof.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDAA17AB8511006C06AF /* testsizeof.cpp */; }; F4C3488F1825692B00521683 /* teststl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDAB17AB8511006C06AF /* teststl.cpp */; }; F4C348901825692B00521683 /* testsuite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDAC17AB8511006C06AF /* testsuite.cpp */; }; F4C348911825692B00521683 /* testsuppressions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDAD17AB8511006C06AF /* testsuppressions.cpp */; }; F4C348921825692B00521683 /* testsymboldatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDAE17AB8511006C06AF /* testsymboldatabase.cpp */; }; F4C348931825692B00521683 /* testthreadexecutor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDAF17AB8511006C06AF /* testthreadexecutor.cpp */; }; F4C348941825692B00521683 /* testtimer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDB017AB8511006C06AF /* testtimer.cpp */; }; F4C348951825692B00521683 /* testtoken.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDB117AB8511006C06AF /* testtoken.cpp */; }; F4C348961825692B00521683 /* testtokenize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDB217AB8511006C06AF /* testtokenize.cpp */; }; F4C348971825692B00521683 /* testuninitvar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDB317AB8511006C06AF /* testuninitvar.cpp */; }; F4C348981825692B00521683 /* testunusedfunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDB417AB8511006C06AF /* testunusedfunctions.cpp */; }; F4C348991825692B00521683 /* testunusedprivfunc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDB517AB8511006C06AF /* testunusedprivfunc.cpp */; }; F4C3489A1825692B00521683 /* testunusedvar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45BDDB617AB8511006C06AF /* testunusedvar.cpp */; }; F4C3489B18256A4500521683 /* check64bit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DB3177F093300CD5A40 /* check64bit.cpp */; }; F4C3489C18256A4500521683 /* checkassert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DB5177F093300CD5A40 /* checkassert.cpp */; }; F4C3489E18256A4500521683 /* checkautovariables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E8D1270DE3A00AC0D02 /* checkautovariables.cpp */; }; F4C3489F18256A4500521683 /* checkbool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DB9177F093300CD5A40 /* checkbool.cpp */; }; F4C348A018256A4500521683 /* checkboost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DBB177F093300CD5A40 /* checkboost.cpp */; }; F4C348A118256A4500521683 /* checkbufferoverrun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E8F1270DE3A00AC0D02 /* checkbufferoverrun.cpp */; }; F4C348A218256A4500521683 /* checkclass.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E911270DE3A00AC0D02 /* checkclass.cpp */; }; F4C348A318256A4500521683 /* checkexceptionsafety.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E931270DE3A00AC0D02 /* checkexceptionsafety.cpp */; }; F4C348A418256A4500521683 /* checkinternal.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DBD177F093300CD5A40 /* checkinternal.cpp */; }; F4C348A518256A4500521683 /* checkio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DBF177F093300CD5A40 /* checkio.cpp */; }; F4C348A618256A4500521683 /* checkleakautovar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC1177F093300CD5A40 /* checkleakautovar.cpp */; }; F4C348A718256A4500521683 /* checkmemoryleak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E951270DE3A00AC0D02 /* checkmemoryleak.cpp */; }; F4C348A918256A4500521683 /* checknullpointer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC5177F093300CD5A40 /* checknullpointer.cpp */; }; F4C348AB18256A4500521683 /* checkother.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E991270DE3A00AC0D02 /* checkother.cpp */; }; F4C348AC18256A4500521683 /* checkpostfixoperator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E9B1270DE3A00AC0D02 /* checkpostfixoperator.cpp */; }; F4C348AD18256A4500521683 /* checksizeof.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC7177F093300CD5A40 /* checksizeof.cpp */; }; F4C348AE18256A4500521683 /* checkstl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E9D1270DE3A00AC0D02 /* checkstl.cpp */; }; F4C348AF18256A4500521683 /* checkuninitvar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DC9177F093300CD5A40 /* checkuninitvar.cpp */; }; F4C348B018256A4500521683 /* checkunusedfunctions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60E9F1270DE3A00AC0D02 /* checkunusedfunctions.cpp */; }; F4C348B118256A4500521683 /* checkunusedvar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DCB177F093300CD5A40 /* checkunusedvar.cpp */; }; F4C348B218256A4500521683 /* errorlogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EA31270DE3A00AC0D02 /* errorlogger.cpp */; }; F4C348B418256A4500521683 /* mathlib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EAB1270DE3A00AC0D02 /* mathlib.cpp */; }; F4C348B518256A4500521683 /* path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EAD1270DE3A00AC0D02 /* path.cpp */; }; F4C348B618256A4500521683 /* preprocessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EAF1270DE3A00AC0D02 /* preprocessor.cpp */; }; F4C348B718256A4500521683 /* settings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EB11270DE3A00AC0D02 /* settings.cpp */; }; F4C348B818256A4500521683 /* suppressions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DCE177F093300CD5A40 /* suppressions.cpp */; }; F4C348B918256A4500521683 /* symboldatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DD0177F093300CD5A40 /* symboldatabase.cpp */; }; F4C348BA18256A4500521683 /* templatesimplifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DD2177F093300CD5A40 /* templatesimplifier.cpp */; }; F4C348BB18256A4500521683 /* timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EB31270DE3A00AC0D02 /* timer.cpp */; }; F4C348BC18256A4500521683 /* token.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EB51270DE3A00AC0D02 /* token.cpp */; }; F4C348BD18256A4500521683 /* tokenize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EB71270DE3A00AC0D02 /* tokenize.cpp */; }; F4C348BE18256A4500521683 /* tokenlist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4043DD4177F093300CD5A40 /* tokenlist.cpp */; }; F4C348BF18256A8700521683 /* tinyxml2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F47E508117896AEB00C684DC /* tinyxml2.cpp */; }; F4C348C018256A8700521683 /* library.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4CF847C17B6504100522F24 /* library.cpp */; }; F4C348C118256A9900521683 /* cppcheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E60EA11270DE3A00AC0D02 /* cppcheck.cpp */; }; F4CDD6981880888F006CF685 /* valueflow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4CDD6961880888F006CF685 /* valueflow.cpp */; }; F4CDD69918808927006CF685 /* valueflow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4CDD6961880888F006CF685 /* valueflow.cpp */; }; F4CF847D17B6504100522F24 /* library.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4CF847C17B6504100522F24 /* library.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 8DD76F690486A84900D96B5E /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; F4C3485E182566E800521683 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/share/man/man1/; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 2D190F241C03A67A00C18478 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = utils.h; path = lib/utils.h; sourceTree = ""; }; 2D190F251C03A7A600C18478 /* astutils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = astutils.cpp; path = lib/astutils.cpp; sourceTree = ""; }; 2D190F261C03A7A600C18478 /* astutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = astutils.h; path = lib/astutils.h; sourceTree = ""; }; 39E60E8C1270DE3A00AC0D02 /* check.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = check.h; path = lib/check.h; sourceTree = ""; }; 39E60E8D1270DE3A00AC0D02 /* checkautovariables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkautovariables.cpp; path = lib/checkautovariables.cpp; sourceTree = ""; }; 39E60E8E1270DE3A00AC0D02 /* checkautovariables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkautovariables.h; path = lib/checkautovariables.h; sourceTree = ""; }; 39E60E8F1270DE3A00AC0D02 /* checkbufferoverrun.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkbufferoverrun.cpp; path = lib/checkbufferoverrun.cpp; sourceTree = ""; }; 39E60E901270DE3A00AC0D02 /* checkbufferoverrun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkbufferoverrun.h; path = lib/checkbufferoverrun.h; sourceTree = ""; }; 39E60E911270DE3A00AC0D02 /* checkclass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkclass.cpp; path = lib/checkclass.cpp; sourceTree = ""; }; 39E60E921270DE3A00AC0D02 /* checkclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkclass.h; path = lib/checkclass.h; sourceTree = ""; }; 39E60E931270DE3A00AC0D02 /* checkexceptionsafety.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkexceptionsafety.cpp; path = lib/checkexceptionsafety.cpp; sourceTree = ""; }; 39E60E941270DE3A00AC0D02 /* checkexceptionsafety.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkexceptionsafety.h; path = lib/checkexceptionsafety.h; sourceTree = ""; }; 39E60E951270DE3A00AC0D02 /* checkmemoryleak.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkmemoryleak.cpp; path = lib/checkmemoryleak.cpp; sourceTree = ""; }; 39E60E961270DE3A00AC0D02 /* checkmemoryleak.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkmemoryleak.h; path = lib/checkmemoryleak.h; sourceTree = ""; }; 39E60E991270DE3A00AC0D02 /* checkother.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkother.cpp; path = lib/checkother.cpp; sourceTree = ""; }; 39E60E9A1270DE3A00AC0D02 /* checkother.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkother.h; path = lib/checkother.h; sourceTree = ""; }; 39E60E9B1270DE3A00AC0D02 /* checkpostfixoperator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkpostfixoperator.cpp; path = lib/checkpostfixoperator.cpp; sourceTree = ""; }; 39E60E9C1270DE3A00AC0D02 /* checkpostfixoperator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkpostfixoperator.h; path = lib/checkpostfixoperator.h; sourceTree = ""; }; 39E60E9D1270DE3A00AC0D02 /* checkstl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkstl.cpp; path = lib/checkstl.cpp; sourceTree = ""; }; 39E60E9E1270DE3A00AC0D02 /* checkstl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkstl.h; path = lib/checkstl.h; sourceTree = ""; }; 39E60E9F1270DE3A00AC0D02 /* checkunusedfunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkunusedfunctions.cpp; path = lib/checkunusedfunctions.cpp; sourceTree = ""; }; 39E60EA01270DE3A00AC0D02 /* checkunusedfunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkunusedfunctions.h; path = lib/checkunusedfunctions.h; sourceTree = ""; }; 39E60EA11270DE3A00AC0D02 /* cppcheck.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cppcheck.cpp; path = lib/cppcheck.cpp; sourceTree = ""; }; 39E60EA21270DE3A00AC0D02 /* cppcheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cppcheck.h; path = lib/cppcheck.h; sourceTree = ""; }; 39E60EA31270DE3A00AC0D02 /* errorlogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = errorlogger.cpp; path = lib/errorlogger.cpp; sourceTree = ""; }; 39E60EA41270DE3A00AC0D02 /* errorlogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = errorlogger.h; path = lib/errorlogger.h; sourceTree = ""; }; 39E60EAB1270DE3A00AC0D02 /* mathlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = mathlib.cpp; path = lib/mathlib.cpp; sourceTree = ""; }; 39E60EAC1270DE3A00AC0D02 /* mathlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mathlib.h; path = lib/mathlib.h; sourceTree = ""; }; 39E60EAD1270DE3A00AC0D02 /* path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = path.cpp; path = lib/path.cpp; sourceTree = ""; }; 39E60EAE1270DE3A00AC0D02 /* path.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = path.h; path = lib/path.h; sourceTree = ""; }; 39E60EAF1270DE3A00AC0D02 /* preprocessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = preprocessor.cpp; path = lib/preprocessor.cpp; sourceTree = ""; }; 39E60EB01270DE3A00AC0D02 /* preprocessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = preprocessor.h; path = lib/preprocessor.h; sourceTree = ""; }; 39E60EB11270DE3A00AC0D02 /* settings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = settings.cpp; path = lib/settings.cpp; sourceTree = ""; }; 39E60EB21270DE3A00AC0D02 /* settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = settings.h; path = lib/settings.h; sourceTree = ""; }; 39E60EB31270DE3A00AC0D02 /* timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = timer.cpp; path = lib/timer.cpp; sourceTree = ""; }; 39E60EB41270DE3A00AC0D02 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = timer.h; path = lib/timer.h; sourceTree = ""; }; 39E60EB51270DE3A00AC0D02 /* token.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = token.cpp; path = lib/token.cpp; sourceTree = ""; }; 39E60EB61270DE3A00AC0D02 /* token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = token.h; path = lib/token.h; sourceTree = ""; }; 39E60EB71270DE3A00AC0D02 /* tokenize.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tokenize.cpp; path = lib/tokenize.cpp; sourceTree = ""; }; 39E60EB81270DE3A00AC0D02 /* tokenize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tokenize.h; path = lib/tokenize.h; sourceTree = ""; }; 39E60ECF1270DE5000AC0D02 /* cmdlineparser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cmdlineparser.cpp; path = cli/cmdlineparser.cpp; sourceTree = ""; }; 39E60ED01270DE5000AC0D02 /* cmdlineparser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cmdlineparser.h; path = cli/cmdlineparser.h; sourceTree = ""; }; 39E60ED11270DE5000AC0D02 /* cppcheckexecutor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cppcheckexecutor.cpp; path = cli/cppcheckexecutor.cpp; sourceTree = ""; }; 39E60ED21270DE5000AC0D02 /* cppcheckexecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cppcheckexecutor.h; path = cli/cppcheckexecutor.h; sourceTree = ""; }; 39E60ED31270DE5000AC0D02 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = cli/main.cpp; sourceTree = ""; }; 39E60ED41270DE5000AC0D02 /* threadexecutor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = threadexecutor.cpp; path = cli/threadexecutor.cpp; sourceTree = ""; }; 39E60ED51270DE5000AC0D02 /* threadexecutor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = threadexecutor.h; path = cli/threadexecutor.h; sourceTree = ""; }; 39E60EDE1270DEB100AC0D02 /* cppcheck */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cppcheck; sourceTree = BUILT_PRODUCTS_DIR; }; F4043DAD177F08A800CD5A40 /* filelister.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = filelister.cpp; path = cli/filelister.cpp; sourceTree = ""; }; F4043DAE177F08A800CD5A40 /* filelister.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = filelister.h; path = cli/filelister.h; sourceTree = ""; }; F4043DAF177F08A800CD5A40 /* pathmatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = pathmatch.cpp; path = cli/pathmatch.cpp; sourceTree = ""; }; F4043DB0177F08A800CD5A40 /* pathmatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pathmatch.h; path = cli/pathmatch.h; sourceTree = ""; }; F4043DB3177F093300CD5A40 /* check64bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check64bit.cpp; path = lib/check64bit.cpp; sourceTree = ""; }; F4043DB4177F093300CD5A40 /* check64bit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = check64bit.h; path = lib/check64bit.h; sourceTree = ""; }; F4043DB5177F093300CD5A40 /* checkassert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkassert.cpp; path = lib/checkassert.cpp; sourceTree = ""; }; F4043DB6177F093300CD5A40 /* checkassert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkassert.h; path = lib/checkassert.h; sourceTree = ""; }; F4043DB9177F093300CD5A40 /* checkbool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkbool.cpp; path = lib/checkbool.cpp; sourceTree = ""; }; F4043DBA177F093300CD5A40 /* checkbool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkbool.h; path = lib/checkbool.h; sourceTree = ""; }; F4043DBB177F093300CD5A40 /* checkboost.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkboost.cpp; path = lib/checkboost.cpp; sourceTree = ""; }; F4043DBC177F093300CD5A40 /* checkboost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkboost.h; path = lib/checkboost.h; sourceTree = ""; }; F4043DBD177F093300CD5A40 /* checkinternal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkinternal.cpp; path = lib/checkinternal.cpp; sourceTree = ""; }; F4043DBE177F093300CD5A40 /* checkinternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkinternal.h; path = lib/checkinternal.h; sourceTree = ""; }; F4043DBF177F093300CD5A40 /* checkio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkio.cpp; path = lib/checkio.cpp; sourceTree = ""; }; F4043DC0177F093300CD5A40 /* checkio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkio.h; path = lib/checkio.h; sourceTree = ""; }; F4043DC1177F093300CD5A40 /* checkleakautovar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkleakautovar.cpp; path = lib/checkleakautovar.cpp; sourceTree = ""; }; F4043DC2177F093300CD5A40 /* checkleakautovar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkleakautovar.h; path = lib/checkleakautovar.h; sourceTree = ""; }; F4043DC5177F093300CD5A40 /* checknullpointer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checknullpointer.cpp; path = lib/checknullpointer.cpp; sourceTree = ""; }; F4043DC6177F093300CD5A40 /* checknullpointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checknullpointer.h; path = lib/checknullpointer.h; sourceTree = ""; }; F4043DC7177F093300CD5A40 /* checksizeof.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checksizeof.cpp; path = lib/checksizeof.cpp; sourceTree = ""; }; F4043DC8177F093300CD5A40 /* checksizeof.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checksizeof.h; path = lib/checksizeof.h; sourceTree = ""; }; F4043DC9177F093300CD5A40 /* checkuninitvar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkuninitvar.cpp; path = lib/checkuninitvar.cpp; sourceTree = ""; }; F4043DCA177F093300CD5A40 /* checkuninitvar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkuninitvar.h; path = lib/checkuninitvar.h; sourceTree = ""; }; F4043DCB177F093300CD5A40 /* checkunusedvar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkunusedvar.cpp; path = lib/checkunusedvar.cpp; sourceTree = ""; }; F4043DCC177F093300CD5A40 /* checkunusedvar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkunusedvar.h; path = lib/checkunusedvar.h; sourceTree = ""; }; F4043DCD177F093300CD5A40 /* standards.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = standards.h; path = lib/standards.h; sourceTree = ""; }; F4043DCE177F093300CD5A40 /* suppressions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = suppressions.cpp; path = lib/suppressions.cpp; sourceTree = ""; }; F4043DCF177F093300CD5A40 /* suppressions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = suppressions.h; path = lib/suppressions.h; sourceTree = ""; }; F4043DD0177F093300CD5A40 /* symboldatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = symboldatabase.cpp; path = lib/symboldatabase.cpp; sourceTree = ""; }; F4043DD1177F093300CD5A40 /* symboldatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = symboldatabase.h; path = lib/symboldatabase.h; sourceTree = ""; }; F4043DD2177F093300CD5A40 /* templatesimplifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = templatesimplifier.cpp; path = lib/templatesimplifier.cpp; sourceTree = ""; }; F4043DD3177F093300CD5A40 /* templatesimplifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = templatesimplifier.h; path = lib/templatesimplifier.h; sourceTree = ""; }; F4043DD4177F093300CD5A40 /* tokenlist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tokenlist.cpp; path = lib/tokenlist.cpp; sourceTree = ""; }; F4043DD5177F093300CD5A40 /* tokenlist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tokenlist.h; path = lib/tokenlist.h; sourceTree = ""; }; F4043DD6177F093300CD5A40 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = version.h; path = lib/version.h; sourceTree = ""; }; F45BDD8317AB8511006C06AF /* options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = options.h; path = test/options.h; sourceTree = ""; }; F45BDD8417AB8511006C06AF /* redirect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = redirect.h; path = test/redirect.h; sourceTree = ""; }; F45BDD8517AB8511006C06AF /* testsuite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = testsuite.h; path = test/testsuite.h; sourceTree = ""; }; F45BDD8617AB8511006C06AF /* testutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = testutils.h; path = test/testutils.h; sourceTree = ""; }; F45BDD8717AB8511006C06AF /* options.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options.cpp; path = test/options.cpp; sourceTree = ""; }; F45BDD8917AB8511006C06AF /* test64bit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = test64bit.cpp; path = test/test64bit.cpp; sourceTree = ""; }; F45BDD8A17AB8511006C06AF /* testassert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testassert.cpp; path = test/testassert.cpp; sourceTree = ""; }; F45BDD8B17AB8511006C06AF /* testassignif.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testassignif.cpp; path = test/testassignif.cpp; sourceTree = ""; }; F45BDD8C17AB8511006C06AF /* testautovariables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testautovariables.cpp; path = test/testautovariables.cpp; sourceTree = ""; }; F45BDD8D17AB8511006C06AF /* testbool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testbool.cpp; path = test/testbool.cpp; sourceTree = ""; }; F45BDD8E17AB8511006C06AF /* testboost.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testboost.cpp; path = test/testboost.cpp; sourceTree = ""; }; F45BDD8F17AB8511006C06AF /* testbufferoverrun.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testbufferoverrun.cpp; path = test/testbufferoverrun.cpp; sourceTree = ""; }; F45BDD9017AB8511006C06AF /* testcharvar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testcharvar.cpp; path = test/testcharvar.cpp; sourceTree = ""; }; F45BDD9117AB8511006C06AF /* testclass.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testclass.cpp; path = test/testclass.cpp; sourceTree = ""; }; F45BDD9217AB8511006C06AF /* testcmdlineparser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testcmdlineparser.cpp; path = test/testcmdlineparser.cpp; sourceTree = ""; }; F45BDD9317AB8511006C06AF /* testconstructors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testconstructors.cpp; path = test/testconstructors.cpp; sourceTree = ""; }; F45BDD9417AB8511006C06AF /* testcppcheck.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testcppcheck.cpp; path = test/testcppcheck.cpp; sourceTree = ""; }; F45BDD9517AB8511006C06AF /* testdivision.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testdivision.cpp; path = test/testdivision.cpp; sourceTree = ""; }; F45BDD9617AB8511006C06AF /* testerrorlogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testerrorlogger.cpp; path = test/testerrorlogger.cpp; sourceTree = ""; }; F45BDD9717AB8511006C06AF /* testexceptionsafety.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testexceptionsafety.cpp; path = test/testexceptionsafety.cpp; sourceTree = ""; }; F45BDD9817AB8511006C06AF /* testfilelister.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testfilelister.cpp; path = test/testfilelister.cpp; sourceTree = ""; }; F45BDD9917AB8511006C06AF /* testincompletestatement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testincompletestatement.cpp; path = test/testincompletestatement.cpp; sourceTree = ""; }; F45BDD9A17AB8511006C06AF /* testinternal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testinternal.cpp; path = test/testinternal.cpp; sourceTree = ""; }; F45BDD9B17AB8511006C06AF /* testio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testio.cpp; path = test/testio.cpp; sourceTree = ""; }; F45BDD9C17AB8511006C06AF /* testleakautovar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testleakautovar.cpp; path = test/testleakautovar.cpp; sourceTree = ""; }; F45BDD9D17AB8511006C06AF /* testmathlib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testmathlib.cpp; path = test/testmathlib.cpp; sourceTree = ""; }; F45BDD9E17AB8511006C06AF /* testmemleak.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testmemleak.cpp; path = test/testmemleak.cpp; sourceTree = ""; }; F45BDD9F17AB8511006C06AF /* testnonreentrantfunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testnonreentrantfunctions.cpp; path = test/testnonreentrantfunctions.cpp; sourceTree = ""; }; F45BDDA017AB8511006C06AF /* testnullpointer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testnullpointer.cpp; path = test/testnullpointer.cpp; sourceTree = ""; }; F45BDDA117AB8511006C06AF /* testobsolescentfunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testobsolescentfunctions.cpp; path = test/testobsolescentfunctions.cpp; sourceTree = ""; }; F45BDDA217AB8511006C06AF /* testoptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testoptions.cpp; path = test/testoptions.cpp; sourceTree = ""; }; F45BDDA317AB8511006C06AF /* testother.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testother.cpp; path = test/testother.cpp; sourceTree = ""; }; F45BDDA417AB8511006C06AF /* testpath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testpath.cpp; path = test/testpath.cpp; sourceTree = ""; }; F45BDDA517AB8511006C06AF /* testpathmatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testpathmatch.cpp; path = test/testpathmatch.cpp; sourceTree = ""; }; F45BDDA617AB8511006C06AF /* testpostfixoperator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testpostfixoperator.cpp; path = test/testpostfixoperator.cpp; sourceTree = ""; }; F45BDDA717AB8511006C06AF /* testpreprocessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testpreprocessor.cpp; path = test/testpreprocessor.cpp; sourceTree = ""; }; F45BDDA817AB8511006C06AF /* testrunner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testrunner.cpp; path = test/testrunner.cpp; sourceTree = ""; }; F45BDDA917AB8511006C06AF /* testsimplifytokens.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testsimplifytokens.cpp; path = test/testsimplifytokens.cpp; sourceTree = ""; }; F45BDDAA17AB8511006C06AF /* testsizeof.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testsizeof.cpp; path = test/testsizeof.cpp; sourceTree = ""; }; F45BDDAB17AB8511006C06AF /* teststl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = teststl.cpp; path = test/teststl.cpp; sourceTree = ""; }; F45BDDAC17AB8511006C06AF /* testsuite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testsuite.cpp; path = test/testsuite.cpp; sourceTree = ""; }; F45BDDAD17AB8511006C06AF /* testsuppressions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testsuppressions.cpp; path = test/testsuppressions.cpp; sourceTree = ""; }; F45BDDAE17AB8511006C06AF /* testsymboldatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testsymboldatabase.cpp; path = test/testsymboldatabase.cpp; sourceTree = ""; }; F45BDDAF17AB8511006C06AF /* testthreadexecutor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testthreadexecutor.cpp; path = test/testthreadexecutor.cpp; sourceTree = ""; }; F45BDDB017AB8511006C06AF /* testtimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testtimer.cpp; path = test/testtimer.cpp; sourceTree = ""; }; F45BDDB117AB8511006C06AF /* testtoken.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testtoken.cpp; path = test/testtoken.cpp; sourceTree = ""; }; F45BDDB217AB8511006C06AF /* testtokenize.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testtokenize.cpp; path = test/testtokenize.cpp; sourceTree = ""; }; F45BDDB317AB8511006C06AF /* testuninitvar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testuninitvar.cpp; path = test/testuninitvar.cpp; sourceTree = ""; }; F45BDDB417AB8511006C06AF /* testunusedfunctions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testunusedfunctions.cpp; path = test/testunusedfunctions.cpp; sourceTree = ""; }; F45BDDB517AB8511006C06AF /* testunusedprivfunc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testunusedprivfunc.cpp; path = test/testunusedprivfunc.cpp; sourceTree = ""; }; F45BDDB617AB8511006C06AF /* testunusedvar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testunusedvar.cpp; path = test/testunusedvar.cpp; sourceTree = ""; }; F47E508117896AEB00C684DC /* tinyxml2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tinyxml2.cpp; path = externals/tinyxml/tinyxml2.cpp; sourceTree = ""; }; F47E508217896AEB00C684DC /* tinyxml2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tinyxml2.h; path = externals/tinyxml/tinyxml2.h; sourceTree = ""; }; F497C2831AB41D5C003B96CF /* check.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = check.cpp; path = lib/check.cpp; sourceTree = ""; }; F497C2841AB41D5C003B96CF /* checkcondition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkcondition.cpp; path = lib/checkcondition.cpp; sourceTree = ""; }; F497C2851AB41D5C003B96CF /* checkcondition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkcondition.h; path = lib/checkcondition.h; sourceTree = ""; }; F497C2861AB41D5C003B96CF /* checkstring.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkstring.cpp; path = lib/checkstring.cpp; sourceTree = ""; }; F497C2871AB41D5C003B96CF /* checkstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkstring.h; path = lib/checkstring.h; sourceTree = ""; }; F497C2881AB41D5C003B96CF /* checktype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checktype.cpp; path = lib/checktype.cpp; sourceTree = ""; }; F497C2891AB41D5C003B96CF /* checkvaarg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = checkvaarg.cpp; path = lib/checkvaarg.cpp; sourceTree = ""; }; F497C28A1AB41D5C003B96CF /* checkvaarg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = checkvaarg.h; path = lib/checkvaarg.h; sourceTree = ""; }; F497C28B1AB41D5C003B96CF /* cxx11emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cxx11emu.h; path = lib/cxx11emu.h; sourceTree = ""; }; F4C34860182566E800521683 /* testrunner */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testrunner; sourceTree = BUILT_PRODUCTS_DIR; }; F4CDD6961880888F006CF685 /* valueflow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = valueflow.cpp; path = lib/valueflow.cpp; sourceTree = ""; }; F4CDD6971880888F006CF685 /* valueflow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = valueflow.h; path = lib/valueflow.h; sourceTree = ""; }; F4CF847A17B6504000522F24 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = lib/config.h; sourceTree = ""; }; F4CF847B17B6504100522F24 /* library.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = library.h; path = lib/library.h; sourceTree = ""; }; F4CF847C17B6504100522F24 /* library.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = library.cpp; path = lib/library.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8DD76F660486A84900D96B5E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; F4C3485D182566E800521683 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 08FB7794FE84155DC02AAC07 /* cppcheck */ = { isa = PBXGroup; children = ( 08FB7795FE84155DC02AAC07 /* Source */, C6859E8C029090F304C91782 /* Documentation */, F4C34861182566E800521683 /* testrunner */, 1AB674ADFE9D54B511CA2CBB /* Products */, ); name = cppcheck; sourceTree = ""; }; 08FB7795FE84155DC02AAC07 /* Source */ = { isa = PBXGroup; children = ( F4C348651825673B00521683 /* Client */, F45BDD8217AB84D0006C06AF /* Tests */, F47E507F17896AC700C684DC /* Externals */, 2C21A45D1178BB0B00D35009 /* Library */, 2C21A45C1178BB0400D35009 /* Command */, ); name = Source; sourceTree = ""; }; 1AB674ADFE9D54B511CA2CBB /* Products */ = { isa = PBXGroup; children = ( 39E60EDE1270DEB100AC0D02 /* cppcheck */, F4C34860182566E800521683 /* testrunner */, ); name = Products; sourceTree = ""; }; 2C21A45C1178BB0400D35009 /* Command */ = { isa = PBXGroup; children = ( 39E60ECF1270DE5000AC0D02 /* cmdlineparser.cpp */, 39E60ED01270DE5000AC0D02 /* cmdlineparser.h */, 39E60ED11270DE5000AC0D02 /* cppcheckexecutor.cpp */, 2D190F241C03A67A00C18478 /* utils.h */, 39E60ED21270DE5000AC0D02 /* cppcheckexecutor.h */, F4043DAD177F08A800CD5A40 /* filelister.cpp */, F4043DAE177F08A800CD5A40 /* filelister.h */, 39E60ED31270DE5000AC0D02 /* main.cpp */, F4043DAF177F08A800CD5A40 /* pathmatch.cpp */, F4043DB0177F08A800CD5A40 /* pathmatch.h */, 39E60ED41270DE5000AC0D02 /* threadexecutor.cpp */, 39E60ED51270DE5000AC0D02 /* threadexecutor.h */, ); name = Command; sourceTree = ""; }; 2C21A45D1178BB0B00D35009 /* Library */ = { isa = PBXGroup; children = ( 2D190F251C03A7A600C18478 /* astutils.cpp */, 2D190F261C03A7A600C18478 /* astutils.h */, F497C2831AB41D5C003B96CF /* check.cpp */, F497C2841AB41D5C003B96CF /* checkcondition.cpp */, F497C2851AB41D5C003B96CF /* checkcondition.h */, F497C2861AB41D5C003B96CF /* checkstring.cpp */, F497C2871AB41D5C003B96CF /* checkstring.h */, F497C2881AB41D5C003B96CF /* checktype.cpp */, F497C2891AB41D5C003B96CF /* checkvaarg.cpp */, F497C28A1AB41D5C003B96CF /* checkvaarg.h */, F497C28B1AB41D5C003B96CF /* cxx11emu.h */, F4CDD6961880888F006CF685 /* valueflow.cpp */, F4CDD6971880888F006CF685 /* valueflow.h */, F4CF847A17B6504000522F24 /* config.h */, F4CF847B17B6504100522F24 /* library.h */, F4CF847C17B6504100522F24 /* library.cpp */, 39E60E8C1270DE3A00AC0D02 /* check.h */, F4043DB3177F093300CD5A40 /* check64bit.cpp */, F4043DB4177F093300CD5A40 /* check64bit.h */, F4043DB5177F093300CD5A40 /* checkassert.cpp */, F4043DB6177F093300CD5A40 /* checkassert.h */, 39E60E8D1270DE3A00AC0D02 /* checkautovariables.cpp */, 39E60E8E1270DE3A00AC0D02 /* checkautovariables.h */, F4043DB9177F093300CD5A40 /* checkbool.cpp */, F4043DBA177F093300CD5A40 /* checkbool.h */, F4043DBB177F093300CD5A40 /* checkboost.cpp */, F4043DBC177F093300CD5A40 /* checkboost.h */, 39E60E8F1270DE3A00AC0D02 /* checkbufferoverrun.cpp */, 39E60E901270DE3A00AC0D02 /* checkbufferoverrun.h */, 39E60E911270DE3A00AC0D02 /* checkclass.cpp */, 39E60E921270DE3A00AC0D02 /* checkclass.h */, 39E60E931270DE3A00AC0D02 /* checkexceptionsafety.cpp */, 39E60E941270DE3A00AC0D02 /* checkexceptionsafety.h */, F4043DBD177F093300CD5A40 /* checkinternal.cpp */, F4043DBE177F093300CD5A40 /* checkinternal.h */, F4043DBF177F093300CD5A40 /* checkio.cpp */, F4043DC0177F093300CD5A40 /* checkio.h */, F4043DC1177F093300CD5A40 /* checkleakautovar.cpp */, F4043DC2177F093300CD5A40 /* checkleakautovar.h */, 39E60E951270DE3A00AC0D02 /* checkmemoryleak.cpp */, 39E60E961270DE3A00AC0D02 /* checkmemoryleak.h */, F4043DC5177F093300CD5A40 /* checknullpointer.cpp */, F4043DC6177F093300CD5A40 /* checknullpointer.h */, 39E60E991270DE3A00AC0D02 /* checkother.cpp */, 39E60E9A1270DE3A00AC0D02 /* checkother.h */, 39E60E9B1270DE3A00AC0D02 /* checkpostfixoperator.cpp */, 39E60E9C1270DE3A00AC0D02 /* checkpostfixoperator.h */, F4043DC7177F093300CD5A40 /* checksizeof.cpp */, F4043DC8177F093300CD5A40 /* checksizeof.h */, 39E60E9D1270DE3A00AC0D02 /* checkstl.cpp */, 39E60E9E1270DE3A00AC0D02 /* checkstl.h */, F4043DC9177F093300CD5A40 /* checkuninitvar.cpp */, F4043DCA177F093300CD5A40 /* checkuninitvar.h */, 39E60E9F1270DE3A00AC0D02 /* checkunusedfunctions.cpp */, 39E60EA01270DE3A00AC0D02 /* checkunusedfunctions.h */, F4043DCB177F093300CD5A40 /* checkunusedvar.cpp */, F4043DCC177F093300CD5A40 /* checkunusedvar.h */, 39E60EA11270DE3A00AC0D02 /* cppcheck.cpp */, 39E60EA21270DE3A00AC0D02 /* cppcheck.h */, 39E60EA31270DE3A00AC0D02 /* errorlogger.cpp */, 39E60EA41270DE3A00AC0D02 /* errorlogger.h */, 39E60EAB1270DE3A00AC0D02 /* mathlib.cpp */, 39E60EAC1270DE3A00AC0D02 /* mathlib.h */, 39E60EAD1270DE3A00AC0D02 /* path.cpp */, 39E60EAE1270DE3A00AC0D02 /* path.h */, 39E60EAF1270DE3A00AC0D02 /* preprocessor.cpp */, 39E60EB01270DE3A00AC0D02 /* preprocessor.h */, 39E60EB11270DE3A00AC0D02 /* settings.cpp */, 39E60EB21270DE3A00AC0D02 /* settings.h */, F4043DCD177F093300CD5A40 /* standards.h */, F4043DCE177F093300CD5A40 /* suppressions.cpp */, F4043DCF177F093300CD5A40 /* suppressions.h */, F4043DD0177F093300CD5A40 /* symboldatabase.cpp */, F4043DD1177F093300CD5A40 /* symboldatabase.h */, F4043DD2177F093300CD5A40 /* templatesimplifier.cpp */, F4043DD3177F093300CD5A40 /* templatesimplifier.h */, 39E60EB31270DE3A00AC0D02 /* timer.cpp */, 39E60EB41270DE3A00AC0D02 /* timer.h */, 39E60EB51270DE3A00AC0D02 /* token.cpp */, 39E60EB61270DE3A00AC0D02 /* token.h */, 39E60EB71270DE3A00AC0D02 /* tokenize.cpp */, 39E60EB81270DE3A00AC0D02 /* tokenize.h */, F4043DD4177F093300CD5A40 /* tokenlist.cpp */, F4043DD5177F093300CD5A40 /* tokenlist.h */, F4043DD6177F093300CD5A40 /* version.h */, ); name = Library; sourceTree = ""; }; C6859E8C029090F304C91782 /* Documentation */ = { isa = PBXGroup; children = ( ); name = Documentation; sourceTree = ""; }; F45BDD8217AB84D0006C06AF /* Tests */ = { isa = PBXGroup; children = ( F45BDD8317AB8511006C06AF /* options.h */, F45BDD8417AB8511006C06AF /* redirect.h */, F45BDD8517AB8511006C06AF /* testsuite.h */, F45BDD8617AB8511006C06AF /* testutils.h */, F45BDD8717AB8511006C06AF /* options.cpp */, F45BDD8917AB8511006C06AF /* test64bit.cpp */, F45BDD8A17AB8511006C06AF /* testassert.cpp */, F45BDD8B17AB8511006C06AF /* testassignif.cpp */, F45BDD8C17AB8511006C06AF /* testautovariables.cpp */, F45BDD8D17AB8511006C06AF /* testbool.cpp */, F45BDD8E17AB8511006C06AF /* testboost.cpp */, F45BDD8F17AB8511006C06AF /* testbufferoverrun.cpp */, F45BDD9017AB8511006C06AF /* testcharvar.cpp */, F45BDD9117AB8511006C06AF /* testclass.cpp */, F45BDD9217AB8511006C06AF /* testcmdlineparser.cpp */, F45BDD9317AB8511006C06AF /* testconstructors.cpp */, F45BDD9417AB8511006C06AF /* testcppcheck.cpp */, F45BDD9517AB8511006C06AF /* testdivision.cpp */, F45BDD9617AB8511006C06AF /* testerrorlogger.cpp */, F45BDD9717AB8511006C06AF /* testexceptionsafety.cpp */, F45BDD9817AB8511006C06AF /* testfilelister.cpp */, F45BDD9917AB8511006C06AF /* testincompletestatement.cpp */, F45BDD9A17AB8511006C06AF /* testinternal.cpp */, F45BDD9B17AB8511006C06AF /* testio.cpp */, F45BDD9C17AB8511006C06AF /* testleakautovar.cpp */, F45BDD9D17AB8511006C06AF /* testmathlib.cpp */, F45BDD9E17AB8511006C06AF /* testmemleak.cpp */, F45BDD9F17AB8511006C06AF /* testnonreentrantfunctions.cpp */, F45BDDA017AB8511006C06AF /* testnullpointer.cpp */, F45BDDA117AB8511006C06AF /* testobsolescentfunctions.cpp */, F45BDDA217AB8511006C06AF /* testoptions.cpp */, F45BDDA317AB8511006C06AF /* testother.cpp */, F45BDDA417AB8511006C06AF /* testpath.cpp */, F45BDDA517AB8511006C06AF /* testpathmatch.cpp */, F45BDDA617AB8511006C06AF /* testpostfixoperator.cpp */, F45BDDA717AB8511006C06AF /* testpreprocessor.cpp */, F45BDDA817AB8511006C06AF /* testrunner.cpp */, F45BDDA917AB8511006C06AF /* testsimplifytokens.cpp */, F45BDDAA17AB8511006C06AF /* testsizeof.cpp */, F45BDDAB17AB8511006C06AF /* teststl.cpp */, F45BDDAC17AB8511006C06AF /* testsuite.cpp */, F45BDDAD17AB8511006C06AF /* testsuppressions.cpp */, F45BDDAE17AB8511006C06AF /* testsymboldatabase.cpp */, F45BDDAF17AB8511006C06AF /* testthreadexecutor.cpp */, F45BDDB017AB8511006C06AF /* testtimer.cpp */, F45BDDB117AB8511006C06AF /* testtoken.cpp */, F45BDDB217AB8511006C06AF /* testtokenize.cpp */, F45BDDB317AB8511006C06AF /* testuninitvar.cpp */, F45BDDB417AB8511006C06AF /* testunusedfunctions.cpp */, F45BDDB517AB8511006C06AF /* testunusedprivfunc.cpp */, F45BDDB617AB8511006C06AF /* testunusedvar.cpp */, ); name = Tests; sourceTree = ""; }; F47E507F17896AC700C684DC /* Externals */ = { isa = PBXGroup; children = ( F47E508017896AD200C684DC /* tinyxml */, ); name = Externals; sourceTree = ""; }; F47E508017896AD200C684DC /* tinyxml */ = { isa = PBXGroup; children = ( F47E508117896AEB00C684DC /* tinyxml2.cpp */, F47E508217896AEB00C684DC /* tinyxml2.h */, ); name = tinyxml; sourceTree = ""; }; F4C34861182566E800521683 /* testrunner */ = { isa = PBXGroup; children = ( ); path = testrunner; sourceTree = ""; }; F4C348651825673B00521683 /* Client */ = { isa = PBXGroup; children = ( ); name = Client; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 8DD76F620486A84900D96B5E /* cppcheck */ = { isa = PBXNativeTarget; buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "cppcheck" */; buildPhases = ( 8DD76F640486A84900D96B5E /* Sources */, 8DD76F660486A84900D96B5E /* Frameworks */, 8DD76F690486A84900D96B5E /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = cppcheck; productInstallPath = "$(HOME)/bin"; productName = cppcheck; productReference = 39E60EDE1270DEB100AC0D02 /* cppcheck */; productType = "com.apple.product-type.tool"; }; F4C3485F182566E800521683 /* testrunner */ = { isa = PBXNativeTarget; buildConfigurationList = F4C34864182566E800521683 /* Build configuration list for PBXNativeTarget "testrunner" */; buildPhases = ( F4C3485C182566E800521683 /* Sources */, F4C3485D182566E800521683 /* Frameworks */, F4C3485E182566E800521683 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = testrunner; productName = testrunner; productReference = F4C34860182566E800521683 /* testrunner */; productType = "com.apple.product-type.tool"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0620; }; buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "Cppcheck" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, Japanese, French, German, ); mainGroup = 08FB7794FE84155DC02AAC07 /* cppcheck */; projectDirPath = ""; projectRoot = ""; targets = ( 8DD76F620486A84900D96B5E /* cppcheck */, F4C3485F182566E800521683 /* testrunner */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 8DD76F640486A84900D96B5E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 39E60EB91270DE3A00AC0D02 /* checkautovariables.cpp in Sources */, 39E60EBA1270DE3A00AC0D02 /* checkbufferoverrun.cpp in Sources */, 39E60EBB1270DE3A00AC0D02 /* checkclass.cpp in Sources */, F4CDD6981880888F006CF685 /* valueflow.cpp in Sources */, 39E60EBC1270DE3A00AC0D02 /* checkexceptionsafety.cpp in Sources */, F497C28E1AB41D5C003B96CF /* checkstring.cpp in Sources */, 2D190F271C03A7A600C18478 /* astutils.cpp in Sources */, 39E60EBD1270DE3A00AC0D02 /* checkmemoryleak.cpp in Sources */, 39E60EBF1270DE3A00AC0D02 /* checkother.cpp in Sources */, 39E60EC01270DE3A00AC0D02 /* checkpostfixoperator.cpp in Sources */, 39E60EC11270DE3A00AC0D02 /* checkstl.cpp in Sources */, 39E60EC21270DE3A00AC0D02 /* checkunusedfunctions.cpp in Sources */, 39E60EC31270DE3A00AC0D02 /* cppcheck.cpp in Sources */, 39E60EC41270DE3A00AC0D02 /* errorlogger.cpp in Sources */, F497C28C1AB41D5C003B96CF /* check.cpp in Sources */, F497C28D1AB41D5C003B96CF /* checkcondition.cpp in Sources */, 39E60EC81270DE3A00AC0D02 /* mathlib.cpp in Sources */, 39E60EC91270DE3A00AC0D02 /* path.cpp in Sources */, 39E60ECA1270DE3A00AC0D02 /* preprocessor.cpp in Sources */, 39E60ECB1270DE3A00AC0D02 /* settings.cpp in Sources */, 39E60ECC1270DE3A00AC0D02 /* timer.cpp in Sources */, 39E60ECD1270DE3A00AC0D02 /* token.cpp in Sources */, 39E60ECE1270DE3A00AC0D02 /* tokenize.cpp in Sources */, 39E60ED61270DE5000AC0D02 /* cmdlineparser.cpp in Sources */, 39E60ED71270DE5000AC0D02 /* cppcheckexecutor.cpp in Sources */, 39E60ED81270DE5000AC0D02 /* main.cpp in Sources */, 39E60ED91270DE5000AC0D02 /* threadexecutor.cpp in Sources */, F4043DB1177F08A800CD5A40 /* filelister.cpp in Sources */, F4043DB2177F08A800CD5A40 /* pathmatch.cpp in Sources */, F4043DD7177F093300CD5A40 /* check64bit.cpp in Sources */, F4043DD8177F093300CD5A40 /* checkassert.cpp in Sources */, F497C2901AB41D5C003B96CF /* checkvaarg.cpp in Sources */, F4043DDA177F093300CD5A40 /* checkbool.cpp in Sources */, F4043DDB177F093300CD5A40 /* checkboost.cpp in Sources */, F4043DDC177F093300CD5A40 /* checkinternal.cpp in Sources */, F4043DDD177F093300CD5A40 /* checkio.cpp in Sources */, F4043DDE177F093300CD5A40 /* checkleakautovar.cpp in Sources */, F4043DE0177F093300CD5A40 /* checknullpointer.cpp in Sources */, F4043DE1177F093300CD5A40 /* checksizeof.cpp in Sources */, F4043DE2177F093300CD5A40 /* checkuninitvar.cpp in Sources */, F4043DE3177F093300CD5A40 /* checkunusedvar.cpp in Sources */, F4043DE4177F093300CD5A40 /* suppressions.cpp in Sources */, F4043DE5177F093300CD5A40 /* symboldatabase.cpp in Sources */, F497C28F1AB41D5C003B96CF /* checktype.cpp in Sources */, F4043DE6177F093300CD5A40 /* templatesimplifier.cpp in Sources */, F4043DE7177F093300CD5A40 /* tokenlist.cpp in Sources */, F47E508317896AEB00C684DC /* tinyxml2.cpp in Sources */, F4CF847D17B6504100522F24 /* library.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; F4C3485C182566E800521683 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( F4CDD69918808927006CF685 /* valueflow.cpp in Sources */, F4C348C118256A9900521683 /* cppcheck.cpp in Sources */, F4C348BF18256A8700521683 /* tinyxml2.cpp in Sources */, F4C348C018256A8700521683 /* library.cpp in Sources */, F4C3489B18256A4500521683 /* check64bit.cpp in Sources */, F4C3489C18256A4500521683 /* checkassert.cpp in Sources */, F4C3489E18256A4500521683 /* checkautovariables.cpp in Sources */, F4C3489F18256A4500521683 /* checkbool.cpp in Sources */, F4C348A018256A4500521683 /* checkboost.cpp in Sources */, F4C348A118256A4500521683 /* checkbufferoverrun.cpp in Sources */, F4C348A218256A4500521683 /* checkclass.cpp in Sources */, F4C348A318256A4500521683 /* checkexceptionsafety.cpp in Sources */, F4C348A418256A4500521683 /* checkinternal.cpp in Sources */, F4C348A518256A4500521683 /* checkio.cpp in Sources */, F4C348A618256A4500521683 /* checkleakautovar.cpp in Sources */, F4C348A718256A4500521683 /* checkmemoryleak.cpp in Sources */, F4C348A918256A4500521683 /* checknullpointer.cpp in Sources */, F4C348AB18256A4500521683 /* checkother.cpp in Sources */, F4C348AC18256A4500521683 /* checkpostfixoperator.cpp in Sources */, F4C348AD18256A4500521683 /* checksizeof.cpp in Sources */, F4C348AE18256A4500521683 /* checkstl.cpp in Sources */, F4C348AF18256A4500521683 /* checkuninitvar.cpp in Sources */, F4C348B018256A4500521683 /* checkunusedfunctions.cpp in Sources */, F4C348B118256A4500521683 /* checkunusedvar.cpp in Sources */, F4C348B218256A4500521683 /* errorlogger.cpp in Sources */, F4C348B418256A4500521683 /* mathlib.cpp in Sources */, F4C348B518256A4500521683 /* path.cpp in Sources */, F4C348B618256A4500521683 /* preprocessor.cpp in Sources */, F4C348B718256A4500521683 /* settings.cpp in Sources */, F4C348B818256A4500521683 /* suppressions.cpp in Sources */, F4C348B918256A4500521683 /* symboldatabase.cpp in Sources */, F4C348BA18256A4500521683 /* templatesimplifier.cpp in Sources */, F4C348BB18256A4500521683 /* timer.cpp in Sources */, F4C348BC18256A4500521683 /* token.cpp in Sources */, F4C348BD18256A4500521683 /* tokenize.cpp in Sources */, F4C348BE18256A4500521683 /* tokenlist.cpp in Sources */, F4C3486C1825692B00521683 /* options.cpp in Sources */, F4C3486D1825692B00521683 /* test64bit.cpp in Sources */, F4C3486E1825692B00521683 /* testassert.cpp in Sources */, F4C3486F1825692B00521683 /* testassignif.cpp in Sources */, F4C348701825692B00521683 /* testautovariables.cpp in Sources */, F4C348711825692B00521683 /* testbool.cpp in Sources */, F4C348721825692B00521683 /* testboost.cpp in Sources */, F4C348731825692B00521683 /* testbufferoverrun.cpp in Sources */, F4C348741825692B00521683 /* testcharvar.cpp in Sources */, F4C348751825692B00521683 /* testclass.cpp in Sources */, F4C348761825692B00521683 /* testcmdlineparser.cpp in Sources */, F4C348771825692B00521683 /* testconstructors.cpp in Sources */, F4C348781825692B00521683 /* testcppcheck.cpp in Sources */, F4C348791825692B00521683 /* testdivision.cpp in Sources */, F4C3487A1825692B00521683 /* testerrorlogger.cpp in Sources */, F4C3487B1825692B00521683 /* testexceptionsafety.cpp in Sources */, F4C3487C1825692B00521683 /* testfilelister.cpp in Sources */, F4C3487D1825692B00521683 /* testincompletestatement.cpp in Sources */, F4C3487E1825692B00521683 /* testinternal.cpp in Sources */, F4C3487F1825692B00521683 /* testio.cpp in Sources */, F4C348801825692B00521683 /* testleakautovar.cpp in Sources */, F4C348811825692B00521683 /* testmathlib.cpp in Sources */, F4C348821825692B00521683 /* testmemleak.cpp in Sources */, F4C348831825692B00521683 /* testnonreentrantfunctions.cpp in Sources */, F4C348841825692B00521683 /* testnullpointer.cpp in Sources */, F4C348851825692B00521683 /* testobsolescentfunctions.cpp in Sources */, F4C348861825692B00521683 /* testoptions.cpp in Sources */, F4C348871825692B00521683 /* testother.cpp in Sources */, F4C348881825692B00521683 /* testpath.cpp in Sources */, F4C348891825692B00521683 /* testpathmatch.cpp in Sources */, F4C3488A1825692B00521683 /* testpostfixoperator.cpp in Sources */, F4C3488B1825692B00521683 /* testpreprocessor.cpp in Sources */, F4C3488C1825692B00521683 /* testrunner.cpp in Sources */, F4C3488D1825692B00521683 /* testsimplifytokens.cpp in Sources */, F4C3488E1825692B00521683 /* testsizeof.cpp in Sources */, F4C3488F1825692B00521683 /* teststl.cpp in Sources */, F4C348901825692B00521683 /* testsuite.cpp in Sources */, F4C348911825692B00521683 /* testsuppressions.cpp in Sources */, F4C348921825692B00521683 /* testsymboldatabase.cpp in Sources */, F4C348931825692B00521683 /* testthreadexecutor.cpp in Sources */, F4C348941825692B00521683 /* testtimer.cpp in Sources */, F4C348951825692B00521683 /* testtoken.cpp in Sources */, F4C348961825692B00521683 /* testtokenize.cpp in Sources */, F4C348971825692B00521683 /* testuninitvar.cpp in Sources */, F4C348981825692B00521683 /* testunusedfunctions.cpp in Sources */, F4C348991825692B00521683 /* testunusedprivfunc.cpp in Sources */, F4C3489A1825692B00521683 /* testunusedvar.cpp in Sources */, F4C348661825679E00521683 /* cmdlineparser.cpp in Sources */, F4C348671825679F00521683 /* cppcheckexecutor.cpp in Sources */, F4C348681825679F00521683 /* filelister.cpp in Sources */, F4C348691825679F00521683 /* pathmatch.cpp in Sources */, F4C3486A1825679F00521683 /* threadexecutor.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 1DEB923208733DC60010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = ""; GCC_OPTIMIZATION_LEVEL = 0; "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = "CFGDIR=\\\"$SRCROOT/cfg/\\\""; GCC_VERSION = ""; HEADER_SEARCH_PATHS = externals/tinyxml; INSTALL_PATH = /usr/local/bin; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = cppcheck; }; name = Debug; }; 1DEB923308733DC60010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; DEBUG_INFORMATION_FORMAT = dwarf; GCC_MODEL_TUNING = ""; "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = "CFGDIR=\\\"$SRCROOT/cfg/\\\""; HEADER_SEARCH_PATHS = externals/tinyxml; INSTALL_PATH = /usr/local/bin; PRODUCT_NAME = cppcheck; }; name = Release; }; 1DEB923608733DC60010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 0; GCC_VERSION = ""; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; 1DEB923708733DC60010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; SDKROOT = macosx; }; name = Release; }; F4C34862182566E800521683 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libstdc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_VERSION = ""; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; HEADER_SEARCH_PATHS = ( externals/tinyxml, lib, ); MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; }; F4C34863182566E800521683 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libstdc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; HEADER_SEARCH_PATHS = ( externals/tinyxml, lib, ); MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "cppcheck" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB923208733DC60010E9CD /* Debug */, 1DEB923308733DC60010E9CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "Cppcheck" */ = { isa = XCConfigurationList; buildConfigurations = ( 1DEB923608733DC60010E9CD /* Debug */, 1DEB923708733DC60010E9CD /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; F4C34864182566E800521683 /* Build configuration list for PBXNativeTarget "testrunner" */ = { isa = XCConfigurationList; buildConfigurations = ( F4C34862182566E800521683 /* Debug */, F4C34863182566E800521683 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; } cppcheck-1.90/Makefile000066400000000000000000002025271357737443600147160ustar00rootroot00000000000000# This file is generated by tools/dmake, do not edit. # To compile with rules, use 'make HAVE_RULES=yes' ifndef HAVE_RULES HAVE_RULES=no endif ifndef USE_Z3 USE_Z3=no endif ifeq ($(USE_Z3),yes) CPPFLAGS += -DUSE_Z3 LIBS += -lz3 endif # use match compiler ifeq ($(SRCDIR),build) $(warning Usage of SRCDIR to activate match compiler is deprecated. Use MATCHCOMPILER=yes instead.) MATCHCOMPILER:=yes endif ifeq ($(MATCHCOMPILER),yes) # Find available Python interpreter PYTHON_INTERPRETER := $(shell which python) ifndef PYTHON_INTERPRETER PYTHON_INTERPRETER := $(shell which python3) endif ifndef PYTHON_INTERPRETER $(error Did not find a Python interpreter) endif ifdef VERIFY matchcompiler_S := $(shell $(PYTHON_INTERPRETER) tools/matchcompiler.py --verify) else matchcompiler_S := $(shell $(PYTHON_INTERPRETER) tools/matchcompiler.py) endif libcppdir:=build else libcppdir:=lib endif ifdef FILESDIR CPPFILESDIR=-DFILESDIR=\"$(FILESDIR)\" else CPPFILESDIR= endif RDYNAMIC=-rdynamic # Set the CPPCHK_GLIBCXX_DEBUG flag. This flag is not used in release Makefiles. # The _GLIBCXX_DEBUG define doesn't work in Cygwin or other Win32 systems. ifndef COMSPEC ifdef ComSpec #### ComSpec is defined on some WIN32's. COMSPEC=$(ComSpec) endif # ComSpec endif # COMSPEC ifdef COMSPEC #### Maybe Windows ifndef CPPCHK_GLIBCXX_DEBUG CPPCHK_GLIBCXX_DEBUG= endif # !CPPCHK_GLIBCXX_DEBUG ifeq ($(MSYSTEM),MINGW32 MINGW64) LDFLAGS=-lshlwapi else RDYNAMIC=-lshlwapi endif else # !COMSPEC uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') ifeq ($(uname_S),Linux) ifndef CPPCHK_GLIBCXX_DEBUG CPPCHK_GLIBCXX_DEBUG=-D_GLIBCXX_DEBUG endif # !CPPCHK_GLIBCXX_DEBUG endif # Linux ifeq ($(uname_S),GNU/kFreeBSD) ifndef CPPCHK_GLIBCXX_DEBUG CPPCHK_GLIBCXX_DEBUG=-D_GLIBCXX_DEBUG endif # !CPPCHK_GLIBCXX_DEBUG endif # GNU/kFreeBSD endif # COMSPEC # Set the UNDEF_STRICT_ANSI flag to address compile time warnings # with tinyxml2 and Cygwin. ifdef COMSPEC uname_S := $(shell uname -s) ifneq (,$(findstring CYGWIN,$(uname_S))) UNDEF_STRICT_ANSI=-U__STRICT_ANSI__ endif # CYGWIN endif # COMSPEC ifndef CXX CXX=g++ endif ifeq (clang++, $(findstring clang++,$(CXX))) CPPCHK_GLIBCXX_DEBUG= endif ifndef CXXFLAGS CXXFLAGS=-std=c++0x -O2 -DNDEBUG -Wall -Wno-sign-compare endif # Increase stack size for Cygwin builds to avoid segmentation fault in limited recursive tests. ifdef COMSPEC uname_S := $(shell uname -s) ifneq (,$(findstring CYGWIN,$(uname_S))) CXXFLAGS+=-Wl,--stack,8388608 endif # CYGWIN endif # COMSPEC ifeq (g++, $(findstring g++,$(CXX))) override CXXFLAGS += -std=c++0x else ifeq (clang++, $(findstring clang++,$(CXX))) override CXXFLAGS += -std=c++0x else ifeq ($(CXX), c++) ifeq ($(shell uname -s), Darwin) override CXXFLAGS += -std=c++0x endif endif ifeq ($(HAVE_RULES),yes) override CXXFLAGS += -DHAVE_RULES -DTIXML_USE_STL $(shell pcre-config --cflags) ifdef LIBS LIBS += $(shell pcre-config --libs) else LIBS=$(shell pcre-config --libs) endif endif ifndef PREFIX PREFIX=/usr endif ifndef INCLUDE_FOR_LIB INCLUDE_FOR_LIB=-Ilib -isystem externals -isystem externals/simplecpp -isystem externals/tinyxml endif ifndef INCLUDE_FOR_CLI INCLUDE_FOR_CLI=-Ilib -isystem externals/simplecpp -isystem externals/tinyxml endif ifndef INCLUDE_FOR_TEST INCLUDE_FOR_TEST=-Ilib -Icli -isystem externals/simplecpp -isystem externals/tinyxml endif BIN=$(DESTDIR)$(PREFIX)/bin # For 'make man': sudo apt-get install xsltproc docbook-xsl docbook-xml on Linux DB2MAN?=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl XP=xsltproc -''-nonet -''-param man.charmap.use.subset "0" MAN_SOURCE=man/cppcheck.1.xml ###### Object Files LIBOBJ = $(libcppdir)/analyzerinfo.o \ $(libcppdir)/astutils.o \ $(libcppdir)/check.o \ $(libcppdir)/check64bit.o \ $(libcppdir)/checkassert.o \ $(libcppdir)/checkautovariables.o \ $(libcppdir)/checkbool.o \ $(libcppdir)/checkboost.o \ $(libcppdir)/checkbufferoverrun.o \ $(libcppdir)/checkclass.o \ $(libcppdir)/checkcondition.o \ $(libcppdir)/checkexceptionsafety.o \ $(libcppdir)/checkfunctions.o \ $(libcppdir)/checkinternal.o \ $(libcppdir)/checkio.o \ $(libcppdir)/checkleakautovar.o \ $(libcppdir)/checkmemoryleak.o \ $(libcppdir)/checknullpointer.o \ $(libcppdir)/checkother.o \ $(libcppdir)/checkpostfixoperator.o \ $(libcppdir)/checksizeof.o \ $(libcppdir)/checkstl.o \ $(libcppdir)/checkstring.o \ $(libcppdir)/checktype.o \ $(libcppdir)/checkuninitvar.o \ $(libcppdir)/checkunusedfunctions.o \ $(libcppdir)/checkunusedvar.o \ $(libcppdir)/checkvaarg.o \ $(libcppdir)/cppcheck.o \ $(libcppdir)/ctu.o \ $(libcppdir)/errorlogger.o \ $(libcppdir)/exprengine.o \ $(libcppdir)/importproject.o \ $(libcppdir)/library.o \ $(libcppdir)/mathlib.o \ $(libcppdir)/path.o \ $(libcppdir)/pathanalysis.o \ $(libcppdir)/pathmatch.o \ $(libcppdir)/platform.o \ $(libcppdir)/preprocessor.o \ $(libcppdir)/programmemory.o \ $(libcppdir)/settings.o \ $(libcppdir)/suppressions.o \ $(libcppdir)/symboldatabase.o \ $(libcppdir)/templatesimplifier.o \ $(libcppdir)/timer.o \ $(libcppdir)/token.o \ $(libcppdir)/tokenize.o \ $(libcppdir)/tokenlist.o \ $(libcppdir)/valueflow.o EXTOBJ = externals/simplecpp/simplecpp.o \ externals/tinyxml/tinyxml2.o CLIOBJ = cli/cmdlineparser.o \ cli/cppcheckexecutor.o \ cli/filelister.o \ cli/main.o \ cli/threadexecutor.o TESTOBJ = test/options.o \ test/test64bit.o \ test/testassert.o \ test/testastutils.o \ test/testautovariables.o \ test/testbool.o \ test/testboost.o \ test/testbufferoverrun.o \ test/testcharvar.o \ test/testclass.o \ test/testcmdlineparser.o \ test/testcondition.o \ test/testconstructors.o \ test/testcppcheck.o \ test/testerrorlogger.o \ test/testexceptionsafety.o \ test/testexprengine.o \ test/testfilelister.o \ test/testfunctions.o \ test/testgarbage.o \ test/testimportproject.o \ test/testincompletestatement.o \ test/testinternal.o \ test/testio.o \ test/testleakautovar.o \ test/testlibrary.o \ test/testmathlib.o \ test/testmemleak.o \ test/testnullpointer.o \ test/testoptions.o \ test/testother.o \ test/testpath.o \ test/testpathmatch.o \ test/testplatform.o \ test/testpostfixoperator.o \ test/testpreprocessor.o \ test/testrunner.o \ test/testsamples.o \ test/testsimplifytemplate.o \ test/testsimplifytokens.o \ test/testsimplifytypedef.o \ test/testsimplifyusing.o \ test/testsizeof.o \ test/teststl.o \ test/teststring.o \ test/testsuite.o \ test/testsuppressions.o \ test/testsymboldatabase.o \ test/testthreadexecutor.o \ test/testtimer.o \ test/testtoken.o \ test/testtokenize.o \ test/testtokenlist.o \ test/testtype.o \ test/testuninitvar.o \ test/testunusedfunctions.o \ test/testunusedprivfunc.o \ test/testunusedvar.o \ test/testvaarg.o \ test/testvalueflow.o \ test/testvarid.o .PHONY: run-dmake tags ###### Targets cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) all: cppcheck testrunner testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) test: all ./testrunner check: all ./testrunner -q checkcfg: cppcheck validateCFG ./test/cfg/runtests.sh dmake: tools/dmake.o cli/filelister.o $(libcppdir)/pathmatch.o $(libcppdir)/path.o externals/simplecpp/simplecpp.o $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) run-dmake: dmake ./dmake generate_cfg_tests: tools/generate_cfg_tests.o $(EXTOBJ) g++ -isystem externals/tinyxml -o generate_cfg_tests tools/generate_cfg_tests.o $(EXTOBJ) clean: rm -f build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner dmake cppcheck cppcheck.exe cppcheck.1 man: man/cppcheck.1 man/cppcheck.1: $(MAN_SOURCE) $(XP) $(DB2MAN) $(MAN_SOURCE) tags: ctags -R --exclude=doxyoutput --exclude=test/cfg --exclude=test/synthetic cli externals gui lib test install: cppcheck install -d ${BIN} install cppcheck ${BIN} install htmlreport/cppcheck-htmlreport ${BIN} ifdef FILESDIR install -d ${DESTDIR}${FILESDIR} install -d ${DESTDIR}${FILESDIR}/addons install -m 644 addons/*.py ${DESTDIR}${FILESDIR}/addons install -d ${DESTDIR}${FILESDIR}/cfg install -m 644 cfg/*.cfg ${DESTDIR}${FILESDIR}/cfg install -d ${DESTDIR}${FILESDIR}/platforms install -m 644 platforms/*.xml ${DESTDIR}${FILESDIR}/platforms else $(error FILESDIR must be set!) endif uninstall: @if test -d ${BIN}; then \ files="cppcheck cppcheck-htmlreport"; \ echo '(' cd ${BIN} '&&' rm -f $$files ')'; \ ( cd ${BIN} && rm -f $$files ); \ fi ifdef FILESDIR @if test -d ${DESTDIR}${FILESDIR}; then \ echo rm -rf ${DESTDIR}${FILESDIR}; \ rm -rf ${DESTDIR}${FILESDIR}; \ fi endif ifdef CFGDIR @if test -d ${DESTDIR}${CFGDIR}; then \ files="`cd cfg 2>/dev/null && ls`"; \ if test -n "$$files"; then \ echo '(' cd ${DESTDIR}${CFGDIR} '&&' rm -f $$files ')'; \ ( cd ${DESTDIR}${CFGDIR} && rm -f $$files ); \ fi; \ fi endif # Validation of library files: ConfigFiles := $(wildcard cfg/*.cfg) ConfigFilesCHECKED := $(patsubst %.cfg,%.checked,$(ConfigFiles)) .PHONY: validateCFG %.checked:%.cfg xmllint --noout --relaxng cfg/cppcheck-cfg.rng $< validateCFG: ${ConfigFilesCHECKED} xmllint --noout cfg/cppcheck-cfg.rng # Validation of platforms files: PlatformFiles := $(wildcard platforms/*.xml) PlatformFilesCHECKED := $(patsubst %.xml,%.checked,$(PlatformFiles)) .PHONY: validatePlatforms %.checked:%.xml xmllint --noout --relaxng platforms/cppcheck-platforms.rng $< validatePlatforms: ${PlatformFilesCHECKED} # Validate XML output (to detect regressions) /tmp/errorlist.xml: cppcheck ./cppcheck --errorlist >$@ /tmp/example.xml: cppcheck ./cppcheck --xml --enable=all --inconclusive --suppress=operatorEqVarError:*check.h --max-configs=1 -j 4 cli externals gui lib test 2>/tmp/example.xml createXMLExamples:/tmp/errorlist.xml /tmp/example.xml .PHONY: validateXML validateXML: createXMLExamples xmllint --noout cppcheck-errors.rng xmllint --noout --relaxng cppcheck-errors.rng /tmp/errorlist.xml xmllint --noout --relaxng cppcheck-errors.rng /tmp/example.xml checkCWEEntries: /tmp/errorlist.xml ./tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml .PHONY: validateRules validateRules: xmllint --noout rules/*.xml ###### Build $(libcppdir)/analyzerinfo.o: lib/analyzerinfo.cpp externals/tinyxml/tinyxml2.h lib/analyzerinfo.h lib/config.h lib/errorlogger.h lib/importproject.h lib/path.h lib/platform.h lib/suppressions.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/analyzerinfo.o $(libcppdir)/analyzerinfo.cpp $(libcppdir)/astutils.o: lib/astutils.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/astutils.o $(libcppdir)/astutils.cpp $(libcppdir)/check.o: lib/check.cpp lib/check.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/check.o $(libcppdir)/check.cpp $(libcppdir)/check64bit.o: lib/check64bit.cpp lib/check.h lib/check64bit.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/check64bit.o $(libcppdir)/check64bit.cpp $(libcppdir)/checkassert.o: lib/checkassert.cpp lib/check.h lib/checkassert.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkassert.o $(libcppdir)/checkassert.cpp $(libcppdir)/checkautovariables.o: lib/checkautovariables.cpp lib/astutils.h lib/check.h lib/checkautovariables.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkautovariables.o $(libcppdir)/checkautovariables.cpp $(libcppdir)/checkbool.o: lib/checkbool.cpp lib/astutils.h lib/check.h lib/checkbool.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkbool.o $(libcppdir)/checkbool.cpp $(libcppdir)/checkboost.o: lib/checkboost.cpp lib/check.h lib/checkboost.h lib/config.h lib/errorlogger.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)/checkboost.o $(libcppdir)/checkboost.cpp $(libcppdir)/checkbufferoverrun.o: lib/checkbufferoverrun.cpp externals/tinyxml/tinyxml2.h lib/astutils.h lib/check.h lib/checkbufferoverrun.h lib/config.h lib/ctu.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkbufferoverrun.o $(libcppdir)/checkbufferoverrun.cpp $(libcppdir)/checkclass.o: lib/checkclass.cpp lib/astutils.h lib/check.h lib/checkclass.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkclass.o $(libcppdir)/checkclass.cpp $(libcppdir)/checkcondition.o: lib/checkcondition.cpp lib/astutils.h lib/check.h lib/checkcondition.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkcondition.o $(libcppdir)/checkcondition.cpp $(libcppdir)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/check.h lib/checkexceptionsafety.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkexceptionsafety.o $(libcppdir)/checkexceptionsafety.cpp $(libcppdir)/checkfunctions.o: lib/checkfunctions.cpp lib/astutils.h lib/check.h lib/checkfunctions.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkfunctions.o $(libcppdir)/checkfunctions.cpp $(libcppdir)/checkinternal.o: lib/checkinternal.cpp lib/astutils.h lib/check.h lib/checkinternal.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkinternal.o $(libcppdir)/checkinternal.cpp $(libcppdir)/checkio.o: lib/checkio.cpp lib/check.h lib/checkio.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkio.o $(libcppdir)/checkio.cpp $(libcppdir)/checkleakautovar.o: lib/checkleakautovar.cpp lib/astutils.h lib/check.h lib/checkleakautovar.h lib/checkmemoryleak.h lib/checknullpointer.h lib/config.h lib/ctu.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkleakautovar.o $(libcppdir)/checkleakautovar.cpp $(libcppdir)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/astutils.h lib/check.h lib/checkmemoryleak.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkmemoryleak.o $(libcppdir)/checkmemoryleak.cpp $(libcppdir)/checknullpointer.o: lib/checknullpointer.cpp lib/astutils.h lib/check.h lib/checknullpointer.h lib/config.h lib/ctu.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checknullpointer.o $(libcppdir)/checknullpointer.cpp $(libcppdir)/checkother.o: lib/checkother.cpp lib/astutils.h lib/check.h lib/checkother.h lib/checkuninitvar.h lib/config.h lib/ctu.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkother.o $(libcppdir)/checkother.cpp $(libcppdir)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/check.h lib/checkpostfixoperator.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkpostfixoperator.o $(libcppdir)/checkpostfixoperator.cpp $(libcppdir)/checksizeof.o: lib/checksizeof.cpp lib/check.h lib/checksizeof.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checksizeof.o $(libcppdir)/checksizeof.cpp $(libcppdir)/checkstl.o: lib/checkstl.cpp lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkstl.h lib/config.h lib/ctu.h lib/errorlogger.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/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkstring.o $(libcppdir)/checkstring.cpp $(libcppdir)/checktype.o: lib/checktype.cpp lib/check.h lib/checktype.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checktype.o $(libcppdir)/checktype.cpp $(libcppdir)/checkuninitvar.o: lib/checkuninitvar.cpp externals/tinyxml/tinyxml2.h lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkuninitvar.h lib/config.h lib/ctu.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkuninitvar.o $(libcppdir)/checkuninitvar.cpp $(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml/tinyxml2.h lib/astutils.h lib/check.h lib/checkunusedfunctions.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkunusedfunctions.o $(libcppdir)/checkunusedfunctions.cpp $(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp lib/astutils.h lib/check.h lib/checkunusedvar.h lib/config.h lib/errorlogger.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)/checkunusedvar.o $(libcppdir)/checkunusedvar.cpp $(libcppdir)/checkvaarg.o: lib/checkvaarg.cpp lib/astutils.h lib/check.h lib/checkvaarg.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkvaarg.o $(libcppdir)/checkvaarg.cpp $(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson.h externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/cppcheck.o $(libcppdir)/cppcheck.cpp $(libcppdir)/ctu.o: lib/ctu.cpp externals/tinyxml/tinyxml2.h lib/astutils.h lib/check.h lib/config.h lib/ctu.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/ctu.o $(libcppdir)/ctu.cpp $(libcppdir)/errorlogger.o: lib/errorlogger.cpp externals/tinyxml/tinyxml2.h lib/analyzerinfo.h lib/check.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/errorlogger.o $(libcppdir)/errorlogger.cpp $(libcppdir)/exprengine.o: lib/exprengine.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/exprengine.o $(libcppdir)/exprengine.cpp $(libcppdir)/importproject.o: lib/importproject.cpp externals/picojson.h externals/tinyxml/tinyxml2.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/importproject.o $(libcppdir)/importproject.cpp $(libcppdir)/library.o: lib/library.cpp externals/tinyxml/tinyxml2.h lib/astutils.h lib/config.h lib/errorlogger.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/library.o $(libcppdir)/library.cpp $(libcppdir)/mathlib.o: lib/mathlib.cpp lib/config.h lib/errorlogger.h lib/mathlib.h lib/suppressions.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/mathlib.o $(libcppdir)/mathlib.cpp $(libcppdir)/path.o: lib/path.cpp externals/simplecpp/simplecpp.h lib/config.h lib/path.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/path.o $(libcppdir)/path.cpp $(libcppdir)/pathanalysis.o: lib/pathanalysis.cpp lib/config.h lib/errorlogger.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/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/pathanalysis.o $(libcppdir)/pathanalysis.cpp $(libcppdir)/pathmatch.o: lib/pathmatch.cpp lib/config.h lib/path.h lib/pathmatch.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/pathmatch.o $(libcppdir)/pathmatch.cpp $(libcppdir)/platform.o: lib/platform.cpp externals/tinyxml/tinyxml2.h lib/config.h lib/path.h lib/platform.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/platform.o $(libcppdir)/platform.cpp $(libcppdir)/preprocessor.o: lib/preprocessor.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/preprocessor.o $(libcppdir)/preprocessor.cpp $(libcppdir)/programmemory.o: lib/programmemory.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/programmemory.o $(libcppdir)/programmemory.cpp $(libcppdir)/settings.o: lib/settings.cpp lib/config.h lib/errorlogger.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 lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/settings.o $(libcppdir)/settings.cpp $(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml/tinyxml2.h lib/config.h lib/errorlogger.h lib/mathlib.h lib/path.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/suppressions.o $(libcppdir)/suppressions.cpp $(libcppdir)/symboldatabase.o: lib/symboldatabase.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/symboldatabase.o $(libcppdir)/symboldatabase.cpp $(libcppdir)/templatesimplifier.o: lib/templatesimplifier.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/templatesimplifier.o $(libcppdir)/templatesimplifier.cpp $(libcppdir)/timer.o: lib/timer.cpp lib/config.h lib/timer.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/timer.o $(libcppdir)/timer.cpp $(libcppdir)/token.o: lib/token.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/token.o $(libcppdir)/token.cpp $(libcppdir)/tokenize.o: lib/tokenize.cpp lib/check.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/tokenize.o $(libcppdir)/tokenize.cpp $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/tokenlist.o $(libcppdir)/tokenlist.cpp $(libcppdir)/valueflow.o: lib/valueflow.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/valueflow.o $(libcppdir)/valueflow.cpp cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/tinyxml/tinyxml2.h lib/check.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cmdlineparser.o cli/cmdlineparser.cpp cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutor.o cli/cppcheckexecutor.cpp cli/filelister.o: cli/filelister.cpp cli/filelister.h lib/config.h lib/path.h lib/pathmatch.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/filelister.o cli/filelister.cpp cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/config.h lib/errorlogger.h lib/suppressions.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/main.o cli/main.cpp cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/threadexecutor.o cli/threadexecutor.cpp test/options.o: test/options.cpp test/options.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/options.o test/options.cpp test/test64bit.o: test/test64bit.cpp lib/check.h lib/check64bit.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/test64bit.o test/test64bit.cpp test/testassert.o: test/testassert.cpp lib/check.h lib/checkassert.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testassert.o test/testassert.cpp test/testastutils.o: test/testastutils.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testastutils.o test/testastutils.cpp test/testautovariables.o: test/testautovariables.cpp lib/check.h lib/checkautovariables.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testautovariables.o test/testautovariables.cpp test/testbool.o: test/testbool.cpp lib/check.h lib/checkbool.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testbool.o test/testbool.cpp test/testboost.o: test/testboost.cpp lib/check.h lib/checkboost.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testboost.o test/testboost.cpp test/testbufferoverrun.o: test/testbufferoverrun.cpp externals/tinyxml/tinyxml2.h lib/check.h lib/checkbufferoverrun.h lib/config.h lib/ctu.h lib/errorlogger.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/testbufferoverrun.o test/testbufferoverrun.cpp test/testcharvar.o: test/testcharvar.cpp lib/check.h lib/checkother.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcharvar.o test/testcharvar.cpp test/testclass.o: test/testclass.cpp externals/tinyxml/tinyxml2.h lib/check.h lib/checkclass.h lib/config.h lib/errorlogger.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/testclass.o test/testclass.cpp test/testcmdlineparser.o: test/testcmdlineparser.cpp cli/cmdlineparser.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/redirect.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcmdlineparser.o test/testcmdlineparser.cpp test/testcondition.o: test/testcondition.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checkcondition.h lib/config.h lib/errorlogger.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/testcondition.o test/testcondition.cpp test/testconstructors.o: test/testconstructors.cpp lib/check.h lib/checkclass.h lib/config.h lib/errorlogger.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/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/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/testcppcheck.o test/testcppcheck.cpp test/testerrorlogger.o: test/testerrorlogger.cpp lib/analyzerinfo.h lib/check.h lib/config.h lib/cppcheck.h lib/errorlogger.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/testerrorlogger.o test/testerrorlogger.cpp test/testexceptionsafety.o: test/testexceptionsafety.cpp lib/check.h lib/checkexceptionsafety.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testexceptionsafety.o test/testexceptionsafety.cpp test/testexprengine.o: test/testexprengine.cpp lib/config.h lib/errorlogger.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testexprengine.o test/testexprengine.cpp test/testfilelister.o: test/testfilelister.cpp cli/filelister.h lib/config.h lib/errorlogger.h lib/pathmatch.h lib/suppressions.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testfilelister.o test/testfilelister.cpp test/testfunctions.o: test/testfunctions.cpp externals/tinyxml/tinyxml2.h lib/check.h lib/checkfunctions.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testfunctions.o test/testfunctions.cpp test/testgarbage.o: test/testgarbage.cpp lib/check.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testgarbage.o test/testgarbage.cpp test/testimportproject.o: test/testimportproject.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testimportproject.o test/testimportproject.cpp test/testincompletestatement.o: test/testincompletestatement.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkother.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testincompletestatement.o test/testincompletestatement.cpp test/testinternal.o: test/testinternal.cpp lib/check.h lib/checkinternal.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testinternal.o test/testinternal.cpp test/testio.o: test/testio.cpp lib/check.h lib/checkio.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testio.o test/testio.cpp test/testleakautovar.o: test/testleakautovar.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checkleakautovar.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testleakautovar.o test/testleakautovar.cpp test/testlibrary.o: test/testlibrary.cpp externals/tinyxml/tinyxml2.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testlibrary.o test/testlibrary.cpp test/testmathlib.o: test/testmathlib.cpp lib/config.h lib/errorlogger.h lib/mathlib.h lib/suppressions.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testmathlib.o test/testmathlib.cpp test/testmemleak.o: test/testmemleak.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkmemoryleak.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testmemleak.o test/testmemleak.cpp test/testnullpointer.o: test/testnullpointer.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checknullpointer.h lib/checkuninitvar.h lib/config.h lib/ctu.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testnullpointer.o test/testnullpointer.cpp test/testoptions.o: test/testoptions.cpp lib/config.h lib/errorlogger.h lib/suppressions.h test/options.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testoptions.o test/testoptions.cpp test/testother.o: test/testother.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checkother.h lib/config.h lib/errorlogger.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/testother.o test/testother.cpp test/testpath.o: test/testpath.cpp lib/config.h lib/errorlogger.h lib/path.h lib/suppressions.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpath.o test/testpath.cpp test/testpathmatch.o: test/testpathmatch.cpp lib/config.h lib/errorlogger.h lib/pathmatch.h lib/suppressions.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpathmatch.o test/testpathmatch.cpp test/testplatform.o: test/testplatform.cpp externals/tinyxml/tinyxml2.h lib/config.h lib/errorlogger.h lib/platform.h lib/suppressions.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testplatform.o test/testplatform.cpp test/testpostfixoperator.o: test/testpostfixoperator.cpp lib/check.h lib/checkpostfixoperator.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpostfixoperator.o test/testpostfixoperator.cpp test/testpreprocessor.o: test/testpreprocessor.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpreprocessor.o test/testpreprocessor.cpp test/testrunner.o: test/testrunner.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errorlogger.h lib/preprocessor.h lib/suppressions.h test/options.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testrunner.o test/testrunner.cpp test/testsamples.o: test/testsamples.cpp cli/cppcheckexecutor.h cli/filelister.h lib/analyzerinfo.h lib/check.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.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/redirect.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsamples.o test/testsamples.cpp test/testsimplifytemplate.o: test/testsimplifytemplate.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytemplate.o test/testsimplifytemplate.cpp test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytokens.o test/testsimplifytokens.cpp test/testsimplifytypedef.o: test/testsimplifytypedef.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifytypedef.o test/testsimplifytypedef.cpp test/testsimplifyusing.o: test/testsimplifyusing.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsimplifyusing.o test/testsimplifyusing.cpp test/testsizeof.o: test/testsizeof.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checksizeof.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsizeof.o test/testsizeof.cpp test/teststl.o: test/teststl.cpp lib/check.h lib/checkstl.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/teststl.o test/teststl.cpp test/teststring.o: test/teststring.cpp lib/check.h lib/checkstring.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/teststring.o test/teststring.cpp test/testsuite.o: test/testsuite.cpp lib/config.h lib/errorlogger.h lib/suppressions.h test/options.h test/redirect.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsuite.o test/testsuite.cpp test/testsuppressions.o: test/testsuppressions.cpp cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsuppressions.o test/testsuppressions.cpp test/testsymboldatabase.o: test/testsymboldatabase.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsymboldatabase.o test/testsymboldatabase.cpp test/testthreadexecutor.o: test/testthreadexecutor.cpp cli/threadexecutor.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testthreadexecutor.o test/testthreadexecutor.cpp test/testtimer.o: test/testtimer.cpp lib/config.h lib/errorlogger.h lib/suppressions.h lib/timer.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtimer.o test/testtimer.cpp test/testtoken.o: test/testtoken.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtoken.o test/testtoken.cpp test/testtokenize.o: test/testtokenize.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtokenize.o test/testtokenize.cpp test/testtokenlist.o: test/testtokenlist.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtokenlist.o test/testtokenlist.cpp test/testtype.o: test/testtype.cpp lib/check.h lib/checktype.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testtype.o test/testtype.cpp test/testuninitvar.o: test/testuninitvar.cpp lib/check.h lib/checkuninitvar.h lib/config.h lib/ctu.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testuninitvar.o test/testuninitvar.cpp test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/check.h lib/checkunusedfunctions.h lib/config.h lib/errorlogger.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/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/testunusedprivfunc.o test/testunusedprivfunc.cpp test/testunusedvar.o: test/testunusedvar.cpp lib/check.h lib/checkunusedvar.h lib/config.h lib/errorlogger.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/testunusedvar.o test/testunusedvar.cpp test/testvaarg.o: test/testvaarg.cpp lib/check.h lib/checkvaarg.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvaarg.o test/testvaarg.cpp test/testvalueflow.o: test/testvalueflow.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvalueflow.o test/testvalueflow.cpp test/testvarid.o: test/testvarid.cpp lib/config.h lib/errorlogger.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testvarid.o test/testvarid.cpp externals/simplecpp/simplecpp.o: externals/simplecpp/simplecpp.cpp externals/simplecpp/simplecpp.h $(CXX) $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) -w $(UNDEF_STRICT_ANSI) -c -o externals/simplecpp/simplecpp.o externals/simplecpp/simplecpp.cpp externals/tinyxml/tinyxml2.o: externals/tinyxml/tinyxml2.cpp externals/tinyxml/tinyxml2.h $(CXX) $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) -w $(UNDEF_STRICT_ANSI) -c -o externals/tinyxml/tinyxml2.o externals/tinyxml/tinyxml2.cpp tools/clang-ast.o: tools/clang-ast.cpp $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o tools/clang-ast.o tools/clang-ast.cpp tools/dmake.o: tools/dmake.cpp cli/filelister.h lib/config.h lib/pathmatch.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o tools/dmake.o tools/dmake.cpp tools/generate_cfg_tests.o: tools/generate_cfg_tests.cpp externals/tinyxml/tinyxml2.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o tools/generate_cfg_tests.o tools/generate_cfg_tests.cpp cppcheck-1.90/addons/000077500000000000000000000000001357737443600145165ustar00rootroot00000000000000cppcheck-1.90/addons/README.md000066400000000000000000000045631357737443600160050ustar00rootroot00000000000000# Cppcheck addons Addons are scripts that analyses Cppcheck dump files to check compatibility with secure coding standards and to locate various issues. ## Supported addons + [cert.py](https://github.com/danmar/cppcheck/blob/master/addons/cert.py) Checks for compliance with the safe programming standard [CERT](http://www.cert.org/secure-coding/). + [misra.py](https://github.com/danmar/cppcheck/blob/master/addons/misra.py) Used to verify compliance with MISRA C 2012 - a proprietary set of guidelines to avoid such questionable code, developed for embedded systems. Since this standard is proprietary, cppcheck does not display error text by specifying only the number of violated rules (for example, [c2012-21.3]). If you want to display full texts for violated rules, you will need to create a text file containing MISRA rules, which you will have to pass when calling the script with `--rule-texts` key. Some examples of rule texts files available in [tests directory](https://github.com/danmar/cppcheck/blob/master/addons/test/misra/). + [y2038.py](https://github.com/danmar/cppcheck/blob/master/addons/y2038.py) Checks Linux system for [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) safety. This required [modified environment](https://github.com/3adev/y2038). See complete description [here](https://github.com/danmar/cppcheck/blob/master/addons/doc/y2038.txt). + [threadsafety.py](https://github.com/danmar/cppcheck/blob/master/addons/threadsafety.py) Analyse Cppcheck dump files to locate threadsafety issues like static local objects used by multiple threads. ## Usage ### Command line interface ```bash cppcheck --addon=cert --addon=y2038 src/test.c ``` It is also possible to call scripts as follows: ```bash cppcheck --dump --quiet src/test.c python cert.py src/test.c.dump python misra.py --rules-texts=~/misra_rules.txt src/test.c.dump ``` This allows you to add additional parameters when calling the script (for example, `--rule-tests` for `misra.py`). The full list of available parameters can be found by calling any script with the `--help` flag. ### GUI When using the graphical interface `cppcheck-gui`, the selection and configuration of addons is carried out on the tab `Addons and tools` in the project settings (`Edit Project File`): ![Screenshot](https://raw.githubusercontent.com/danmar/cppcheck/master/addons/doc/img/cppcheck-gui-addons.png) cppcheck-1.90/addons/ROS_naming.json000066400000000000000000000021641357737443600174100ustar00rootroot00000000000000{ "RE_FILE": {".*[A-Z]": [true, "under_scored"]}, "RE_NAMESPACE": {".*[A-Z]": [true, "under_scored"], ".*\\_$": [true, "under_scored"]}, "RE_FUNCTIONNAME": {".*\\_": [true, "camelCase"], ".*^[a-z]": [false, "camelCase"]}, "RE_CLASS_NAME": {".*^[A-Z]": [false, "CamelCase"], ".*\\_": [true, "CamelCase"]}, "RE_GLOBAL_VARNAME": {".*^([g]\\_)": [false, "g_under_scored"], ".*[A-Z]": [true, "g_under_scored"], ".*\\_$": [true, "g_under_scored"]}, "RE_VARNAME": {".*^([g]\\_)": [true, "under_scored"], ".*[A-Z]": [true, "under_scored"], ".*\\_$": [true, "under_scored"]}, "RE_PRIVATE_MEMBER_VARIABLE": {".*\\_$": [false, "under_scored_"], ".*[A-Z]": [true, "under_scored_"]}, "RE_PUBLIC_MEMBER_VARIABLE": {".*\\_$": [false, "under_scored_"], ".*[A-Z]": [true, "under_scored_"]}, "var_prefixes": {"uint32_t": "ui32", "int*": "intp"}, "function_prefixes": {"uint16_t": "ui16", "uint32_t": "ui32"}, "skip_one_char_variables": false }cppcheck-1.90/addons/__init__.py000066400000000000000000000000001357737443600166150ustar00rootroot00000000000000cppcheck-1.90/addons/cert.py000077500000000000000000000372751357737443600160460ustar00rootroot00000000000000#!/usr/bin/env python3 # # Cert: Some extra CERT checkers # # Cppcheck itself handles many CERT rules. Cppcheck warns when there is undefined behaviour. # CERT Homepage: https://www.cert.org/secure-coding/ # # Example usage of this addon (scan a sourcefile main.cpp) # cppcheck --dump main.cpp # python cert.py main.cpp.dump import argparse import cppcheckdata import sys import re VERIFY = ('-verify' in sys.argv) VERIFY_EXPECTED = [] VERIFY_ACTUAL = [] def reportError(token, severity, msg, id): if VERIFY: VERIFY_ACTUAL.append(str(token.linenr) + ':cert-' + id) else: cppcheckdata.reportError(token, severity, msg, 'cert', id) def simpleMatch(token, pattern): for p in pattern.split(' '): if not token or token.str != p: return False token = token.next return True def isUnpackedStruct(token): if token.valueType is None: return False if token.valueType.typeScope is None: return False if token.valueType.typeScope.type != "Struct": return False startToken = token.valueType.typeScope.bodyStart linenr = int(startToken.linenr) for line in open(startToken.file): linenr -= 1 if linenr == 0: return True if linenr < 3 and re.match(r'#pragma\s+pack\s*\(', line): return False return True def isLocalUnpackedStruct(arg): if arg and arg.str == '&' and not arg.astOperand2: arg = arg.astOperand1 return arg and arg.variable and (arg.variable.isLocal or arg.variable.isArgument) and isUnpackedStruct(arg) def isBitwiseOp(token): return token and (token.str in {'&', '|', '^'}) def isComparisonOp(token): return token and (token.str in {'==', '!=', '>', '>=', '<', '<='}) def isCast(expr): if not expr or expr.str != '(' or not expr.astOperand1 or expr.astOperand2: return False if simpleMatch(expr, '( )'): return False return True def isStandardFunction(token): if token.function: return False prev = token.previous if prev: if prev.str == '.': return False if prev.str == '::': prevprev = prev.previous if prevprev and not prevprev.str == 'std': return False return True # Is this a function call def isFunctionCall(token, function_names, number_of_arguments=None): if not token.isName: return False if token.str not in function_names: return False if (token.next is None) or token.next.str != '(' or token.next != token.astParent: return False if number_of_arguments is None: return True return len(cppcheckdata.getArguments(token)) == number_of_arguments # EXP05-C # do not attempt to cast away const def exp05(data): # TODO Reuse code in misra rule 11.8 for token in data.tokenlist: if isCast(token): # C-style cast if not token.valueType: continue if not token.astOperand1.valueType: continue if token.valueType.pointer == 0: continue if token.astOperand1.valueType.pointer == 0: continue const1 = token.valueType.constness const2 = token.astOperand1.valueType.constness if (const1 % 2) < (const2 % 2): reportError(token, 'style', "Attempt to cast away const", 'EXP05-C') elif token.str == '(' and token.astOperand1 and token.astOperand2 and token.astOperand1.function: function = token.astOperand1.function arguments = cppcheckdata.getArguments(token.previous) if not arguments: continue for argnr, argvar in function.argument.items(): if argnr < 1 or argnr > len(arguments): continue if not argvar.isPointer: continue if (argvar.constness % 2) == 1: # data is const continue argtok = arguments[argnr - 1] if not argtok.valueType: continue if argtok.valueType.pointer == 0: continue const2 = arguments[argnr - 1].valueType.constness if (const2 % 2) == 1: reportError(token, 'style', "Attempt to cast away const", 'EXP05-C') # EXP42-C # do not compare padding data def exp42(data): for token in data.tokenlist: if token.str != '(' or not token.astOperand1 or token.astOperand1.str != 'memcmp': continue arg1 = None arg2 = None if token.astOperand2 and token.astOperand2.str == ',': if token.astOperand2.astOperand1 and token.astOperand2.astOperand1.str == ',': arg1 = token.astOperand2.astOperand1.astOperand1 arg2 = token.astOperand2.astOperand1.astOperand2 if isLocalUnpackedStruct(arg1) or isLocalUnpackedStruct(arg2): reportError( token, 'style', "Comparison of struct padding data " + "(fix either by packing the struct using '#pragma pack' or by rewriting the comparison)", 'EXP42-C') # EXP15-C # Do not place a semicolon on the same line as an if, for or while statement def exp15(data): for scope in data.scopes: if scope.type in ('If', 'For', 'While'): token = scope.bodyStart.next if token.str==';' and token.linenr==scope.bodyStart.linenr: reportError(token, 'style', 'Do not place a semicolon on the same line as an IF, FOR or WHILE', 'EXP15-C') # EXP46-C # Do not use a bitwise operator with a Boolean-like operand # int x = (a == b) & c; def exp46(data): for token in data.tokenlist: if isBitwiseOp(token) and (isComparisonOp(token.astOperand1) or isComparisonOp(token.astOperand2)): reportError( token, 'style', 'Bitwise operator is used with a Boolean-like operand', 'EXP46-c') # INT31-C # Ensure that integer conversions do not result in lost or misinterpreted data def int31(data, platform): if not platform: return for token in data.tokenlist: if not isCast(token): continue if not token.valueType or not token.astOperand1.values: continue bits = None if token.valueType.type == 'char': bits = platform.char_bit elif token.valueType.type == 'short': bits = platform.short_bit elif token.valueType.type == 'int': bits = platform.int_bit elif token.valueType.type == 'long': bits = platform.long_bit elif token.valueType.type == 'long long': bits = platform.long_long_bit else: continue if token.valueType.sign == 'unsigned': found = False for value in token.astOperand1.values: if value.intvalue and value.intvalue < 0: found = True reportError( token, 'style', 'Ensure that integer conversions do not result in lost or misinterpreted data (casting ' + str(value.intvalue) + ' to unsigned ' + token.valueType.type + ')', 'INT31-c') break if found: continue if bits >= 64: continue minval = 0 maxval = 1 if token.valueType.sign == 'signed': minval = -(1 << (bits - 1)) maxval = ((1 << (bits - 1)) - 1) else: minval = 0 maxval = ((1 << bits) - 1) for value in token.astOperand1.values: if value.intvalue and (value.intvalue < minval or value.intvalue > maxval): destType = '' if token.valueType.sign: destType = token.valueType.sign + ' ' + token.valueType.type else: destType = token.valueType.type reportError( token, 'style', 'Ensure that integer conversions do not result in lost or misinterpreted data (casting ' + str(value.intvalue) + ' to ' + destType + ')', 'INT31-c') break # ENV33-C # Do not call system() def env33(data): for token in data.tokenlist: if isFunctionCall(token, ('system',), 1): # Invalid syntax if not token.next.astOperand2: continue # ENV33-C-EX1: It is permissible to call system() with a null # pointer argument to determine the presence of a command processor # for the system. argValue = token.next.astOperand2.getValue(0) if argValue and argValue.intvalue == 0 and argValue.isKnown(): continue reportError(token, 'style', 'Do not call system()', 'ENV33-C') # MSC24-C # Do not use deprecated or obsolescent functions def msc24(data): for token in data.tokenlist: if isFunctionCall(token, ('asctime',), 1): reportError(token,'style','Do not use asctime() better use asctime_s()', 'MSC24-C') elif isFunctionCall(token, ('atof',), 1): reportError(token,'style','Do not use atof() better use strtod()', 'MSC24-C') elif isFunctionCall(token, ('atoi',), 1): reportError(token,'style','Do not use atoi() better use strtol()', 'MSC24-C') elif isFunctionCall(token, ('atol',), 1): reportError(token,'style','Do not use atol() better use strtol()', 'MSC24-C') elif isFunctionCall(token, ('atoll',), 1): reportError(token,'style','Do not use atoll() better use strtoll()', 'MSC24-C') elif isFunctionCall(token, ('ctime',), 1): reportError(token,'style','Do not use ctime() better use ctime_s()', 'MSC24-C') elif isFunctionCall(token, ('fopen',), 2): reportError(token,'style','Do not use fopen() better use fopen_s()', 'MSC24-C') elif isFunctionCall(token, ('freopen',), 3): reportError(token,'style','Do not use freopen() better use freopen_s()', 'MSC24-C') elif isFunctionCall(token, ('rewind',), 1): reportError(token,'style','Do not use rewind() better use fseek()', 'MSC24-C') elif isFunctionCall(token, ('setbuf',), 2): reportError(token,'style','Do not use setbuf() better use setvbuf()', 'MSC24-C') # MSC30-C # Do not use the rand() function for generating pseudorandom numbers def msc30(data): for token in data.tokenlist: if simpleMatch(token, "rand ( )") and isStandardFunction(token): reportError(token, 'style', 'Do not use the rand() function for generating pseudorandom numbers', 'MSC30-c') # STR03-C # Do not inadvertently truncate a string def str03(data): for token in data.tokenlist: if not isFunctionCall(token, 'strncpy'): continue arguments = cppcheckdata.getArguments(token) if len(arguments)!=3: continue if arguments[2].str=='(' and arguments[2].astOperand1.str=='sizeof': reportError(token, 'style', 'Do not inadvertently truncate a string', 'STR03-C') # STR05-C # Use pointers to const when referring to string literals def str05(data): for token in data.tokenlist: if token.isString: parent = token.astParent if parent is None: continue parentOp1 = parent.astOperand1 if parent.isAssignmentOp and parentOp1.valueType: if (parentOp1.valueType.type in ('char', 'wchar_t')) and parentOp1.valueType.pointer and not parentOp1.valueType.constness: reportError(parentOp1, 'style', 'Use pointers to const when referring to string literals', 'STR05-C') # STR07-C # Use the bounds-checking interfaces for string manipulation def str07(data): for token in data.tokenlist: if not isFunctionCall(token, ('strcpy', 'strcat')): continue args = cppcheckdata.getArguments(token) if len(args)!=2: continue if args[1].isString: continue reportError(token, 'style', 'Use the bounds-checking interfaces %s_s()' % (token.str), 'STR07-C') # STR11-C # Do not specify the bound of a character array initialized with a string literal def str11(data): for token in data.tokenlist: if not token.isString: continue strlen = token.strlen parent = token.astParent if parent is None: continue parentOp1 = parent.astOperand1 if parentOp1 is None or parentOp1.str!='[': continue if not parent.isAssignmentOp: continue varToken = parentOp1.astOperand1 if varToken is None or not varToken.isName: continue if varToken.variable is None: continue if varToken != varToken.variable.nameToken: continue valueToken = parentOp1.astOperand2 if valueToken is None: continue if valueToken.isNumber and int(valueToken.str)==strlen: reportError(valueToken, 'style', 'Do not specify the bound of a character array initialized with a string literal', 'STR11-C') # API01-C # Avoid laying out strings in memory directly before sensitive data def api01(data): for scope in data.scopes: if scope.type!='Struct': continue token = scope.bodyStart arrayFound=False # loop through the complete struct while token != scope.bodyEnd: if token.isName and token.variable: if token.variable.isArray: arrayFound=True elif arrayFound and not token.variable.isArray and not token.variable.isConst: reportError(token, 'style', 'Avoid laying out strings in memory directly before sensitive data', 'API01-C') # reset flags to report other positions in the same struct arrayFound=False token = token.next def get_args(): parser = cppcheckdata.ArgumentParser() parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true") return parser.parse_args() if __name__ == '__main__': args = get_args() if args.verify: VERIFY = True if not args.dumpfile: if not args.quiet: print("no input files.") sys.exit(0) for dumpfile in args.dumpfile: if not args.quiet: print('Checking %s...' % dumpfile) data = cppcheckdata.parsedump(dumpfile) if VERIFY: VERIFY_ACTUAL = [] VERIFY_EXPECTED = [] for tok in data.rawTokens: if tok.str.startswith('//') and 'TODO' not in tok.str: for word in tok.str[2:].split(' '): if re.match(r'cert-[A-Z][A-Z][A-Z][0-9][0-9].*',word): VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word) for cfg in data.configurations: if (len(data.configurations) > 1) and (not args.quiet): print('Checking %s, config %s...' % (dumpfile, cfg.name)) exp05(cfg) exp42(cfg) exp46(cfg) exp15(cfg) int31(cfg, data.platform) str03(cfg) str05(cfg) str07(cfg) str11(cfg) env33(cfg) msc24(cfg) msc30(cfg) api01(cfg) if VERIFY: for expected in VERIFY_EXPECTED: if expected not in VERIFY_ACTUAL: print('Expected but not seen: ' + expected) sys.exit(1) for actual in VERIFY_ACTUAL: if actual not in VERIFY_EXPECTED: print('Not expected: ' + actual) sys.exit(1) cppcheck-1.90/addons/cppcheckdata.doxyfile000066400000000000000000002250471357737443600207070ustar00rootroot00000000000000# Doxyfile 1.8.1.2 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "cppcheckdata" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = cppcheckdata.py # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = NO # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = NO cppcheck-1.90/addons/cppcheckdata.py000077500000000000000000001017301357737443600175070ustar00rootroot00000000000000""" cppcheckdata This is a Python module that helps you access Cppcheck dump data. License: No restrictions, use this as you need. """ from xml.etree import ElementTree import argparse from fnmatch import fnmatch import json import sys class Directive: """ Directive class. Contains information about each preprocessor directive in the source code. Attributes: str The directive line, with all C or C++ comments removed file Name of (possibly included) file where directive is defined linenr Line number in (possibly included) file where directive is defined To iterate through all directives use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: for directive in cfg.directives: print(directive.str) @endcode """ str = None file = None linenr = None column = 0 def __init__(self, element): self.str = element.get('str') self.file = element.get('file') self.linenr = int(element.get('linenr')) def __repr__(self): attrs = ["str", "file", "linenr"] return "{}({})".format( "Directive", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class ValueType: """ ValueType class. Contains (promoted) type information for each node in the AST. """ type = None sign = None bits = 0 constness = 0 pointer = 0 typeScopeId = None typeScope = None originalTypeName = None def __init__(self, element): self.type = element.get('valueType-type') self.sign = element.get('valueType-sign') bits = element.get('valueType-bits') if bits: self.bits = int(bits) self.typeScopeId = element.get('valueType-typeScope') self.originalTypeName = element.get('valueType-originalTypeName') constness = element.get('valueType-constness') if constness: self.constness = int(constness) else: self.constness = 0 pointer = element.get('valueType-pointer') if pointer: self.pointer = int(pointer) else: self.pointer = 0 def __repr__(self): attrs = ["type", "sign", "bits", "typeScopeId", "originalTypeName", "constness", "constness", "pointer"] return "{}({})".format( "ValueType", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): self.typeScope = IdMap[self.typeScopeId] def isIntegral(self): return self.type in {'bool', 'char', 'short', 'int', 'long', 'long long'} def isFloat(self): return self.type in {'float', 'double', 'long double'} def isEnum(self): return self.typeScope and self.typeScope.type == "Enum" class Token: """ Token class. Contains information about each token in the source code. The CppcheckData.tokenlist is a list of Token items C++ class: http://cppcheck.net/devinfo/doxyoutput/classToken.html Attributes: str Token string next Next token in tokenlist. For last token, next is None. previous Previous token in tokenlist. For first token, previous is None. link Linked token in tokenlist. Each '(', '[' and '{' are linked to the corresponding '}', ']' and ')'. For templates, the '<' is linked to the corresponding '>'. scope Scope information for this token. See the Scope class. isName Is this token a symbol name isNumber Is this token a number, for example 123, 12.34 isInt Is this token a int value such as 1234 isFloat Is this token a int value such as 12.34 isString Is this token a string literal such as "hello" strlen string length for string literal isChar Is this token a char literal such as 'x' isOp Is this token a operator isArithmeticalOp Is this token a arithmetic operator isAssignmentOp Is this token a assignment operator isComparisonOp Is this token a comparison operator isLogicalOp Is this token a logical operator: && || isUnsigned Is this token a unsigned type isSigned Is this token a signed type isExpandedMacro Is this token a expanded macro token varId varId for token, each variable has a unique non-zero id variable Variable information for this token. See the Variable class. function If this token points at a function call, this attribute has the Function information. See the Function class. values Possible values of token valueType type information typeScope type scope (token->type()->classScope) astParent ast parent astOperand1 ast operand1 astOperand2 ast operand2 file file name linenr line number column column To iterate through all tokens use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: code = '' for token in cfg.tokenlist: code = code + token.str + ' ' print(code) @endcode """ Id = None str = None next = None previous = None linkId = None link = None scopeId = None scope = None isName = False isNumber = False isInt = False isFloat = False isString = False strlen = None isChar = False isOp = False isArithmeticalOp = False isAssignmentOp = False isComparisonOp = False isLogicalOp = False isUnsigned = False isSigned = False isExpandedMacro = False varId = None variableId = None variable = None functionId = None function = None valuesId = None values = None valueType = None typeScopeId = None typeScope = None astParentId = None astParent = None astOperand1Id = None astOperand1 = None astOperand2Id = None astOperand2 = None file = None linenr = None column = None def __init__(self, element): self.Id = element.get('id') self.str = element.get('str') self.next = None self.previous = None self.scopeId = element.get('scope') self.scope = None type = element.get('type') if type == 'name': self.isName = True if element.get('isUnsigned'): self.isUnsigned = True if element.get('isSigned'): self.isSigned = True elif type == 'number': self.isNumber = True if element.get('isInt'): self.isInt = True elif element.get('isFloat'): self.isFloat = True elif type == 'string': self.isString = True self.strlen = int(element.get('strlen')) elif type == 'char': self.isChar = True elif type == 'op': self.isOp = True if element.get('isArithmeticalOp'): self.isArithmeticalOp = True elif element.get('isAssignmentOp'): self.isAssignmentOp = True elif element.get('isComparisonOp'): self.isComparisonOp = True elif element.get('isLogicalOp'): self.isLogicalOp = True if element.get('isExpandedMacro'): self.isExpandedMacro = True self.linkId = element.get('link') self.link = None if element.get('varId'): self.varId = int(element.get('varId')) self.variableId = element.get('variable') self.variable = None self.functionId = element.get('function') self.function = None self.valuesId = element.get('values') self.values = None if element.get('valueType-type'): self.valueType = ValueType(element) else: self.valueType = None self.typeScopeId = element.get('type-scope') self.typeScope = None self.astParentId = element.get('astParent') self.astParent = None self.astOperand1Id = element.get('astOperand1') self.astOperand1 = None self.astOperand2Id = element.get('astOperand2') self.astOperand2 = None self.file = element.get('file') self.linenr = int(element.get('linenr')) self.column = int(element.get('column')) def __repr__(self): 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"] return "{}({})".format( "Token", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): self.scope = IdMap[self.scopeId] self.link = IdMap[self.linkId] self.variable = IdMap[self.variableId] self.function = IdMap[self.functionId] self.values = IdMap[self.valuesId] self.typeScope = IdMap[self.typeScopeId] self.astParent = IdMap[self.astParentId] self.astOperand1 = IdMap[self.astOperand1Id] self.astOperand2 = IdMap[self.astOperand2Id] if self.valueType: self.valueType.setId(IdMap) def getValue(self, v): """ Get value if it exists Returns None if it doesn't exist """ if not self.values: return None for value in self.values: if value.intvalue == v: return value return None class Scope: """ Scope. Information about global scope, function scopes, class scopes, inner scopes, etc. C++ class: http://cppcheck.net/devinfo/doxyoutput/classScope.html Attributes bodyStart The { Token for this scope bodyEnd The } Token for this scope className Name of this scope. For a function scope, this is the function name; For a class scope, this is the class name. function If this scope belongs at a function call, this attribute has the Function information. See the Function class. type Type of scope: Global, Function, Class, If, While """ Id = None bodyStartId = None bodyStart = None bodyEndId = None bodyEnd = None className = None functionId = None function = None nestedInId = None nestedIn = None type = None isExecutable = None def __init__(self, element): self.Id = element.get('id') self.className = element.get('className') self.functionId = element.get('function') self.function = None self.bodyStartId = element.get('bodyStart') self.bodyStart = None self.bodyEndId = element.get('bodyEnd') self.bodyEnd = None self.nestedInId = element.get('nestedIn') self.nestedIn = None self.type = element.get('type') self.isExecutable = (self.type in ('Function', 'If', 'Else', 'For', 'While', 'Do', 'Switch', 'Try', 'Catch', 'Unconditional', 'Lambda')) def __repr__(self): attrs = ["Id", "className", "functionId", "bodyStartId", "bodyEndId", "nestedInId", "nestedIn", "type", "isExecutable"] return "{}({})".format( "Scope", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): self.bodyStart = IdMap[self.bodyStartId] self.bodyEnd = IdMap[self.bodyEndId] self.nestedIn = IdMap[self.nestedInId] self.function = IdMap[self.functionId] class Function: """ Information about a function C++ class: http://cppcheck.net/devinfo/doxyoutput/classFunction.html Attributes argument Argument list tokenDef Token in function definition isVirtual Is this function is virtual isImplicitlyVirtual Is this function is virtual this in the base classes isStatic Is this function is static """ Id = None argument = None argumentId = None tokenDef = None tokenDefId = None name = None type = None isVirtual = None isImplicitlyVirtual = None isStatic = None def __init__(self, element): self.Id = element.get('id') self.tokenDefId = element.get('tokenDef') self.name = element.get('name') self.type = element.get('type') isVirtual = element.get('isVirtual') self.isVirtual = (isVirtual and isVirtual == 'true') isImplicitlyVirtual = element.get('isImplicitlyVirtual') self.isImplicitlyVirtual = (isImplicitlyVirtual and isImplicitlyVirtual == 'true') isStatic = element.get('isStatic') self.isStatic = (isStatic and isStatic == 'true') self.argument = {} self.argumentId = {} for arg in element: self.argumentId[int(arg.get('nr'))] = arg.get('variable') def __repr__(self): attrs = ["Id", "tokenDefId", "name", "type", "isVirtual", "isImplicitlyVirtual", "isStatic", "argumentId"] return "{}({})".format( "Function", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): for argnr, argid in self.argumentId.items(): self.argument[argnr] = IdMap[argid] self.tokenDef = IdMap[self.tokenDefId] class Variable: """ Information about a variable C++ class: http://cppcheck.net/devinfo/doxyoutput/classVariable.html Attributes: nameToken Name token in variable declaration typeStartToken Start token of variable declaration typeEndToken End token of variable declaration access Global/Local/Namespace/Public/Protected/Public/Throw/Argument scope Variable scope isArgument Is this variable a function argument? isArray Is this variable an array? isClass Is this variable a class or struct? isConst Is this variable a const variable? isGlobal Is this variable a global variable? isExtern Is this variable an extern variable? isLocal Is this variable a local variable? isPointer Is this variable a pointer isReference Is this variable a reference isStatic Is this variable static? constness Variable constness (same encoding as ValueType::constness) """ Id = None nameTokenId = None nameToken = None typeStartTokenId = None typeStartToken = None typeEndTokenId = None typeEndToken = None access = None scopeId = None scope = None isArgument = False isArray = False isClass = False isConst = False isExtern = False isGlobal = False isLocal = False isPointer = False isReference = False isStatic = False constness = 0 def __init__(self, element): self.Id = element.get('id') self.nameTokenId = element.get('nameToken') self.nameToken = None self.typeStartTokenId = element.get('typeStartToken') self.typeStartToken = None self.typeEndTokenId = element.get('typeEndToken') self.typeEndToken = None self.access = element.get('access') self.scopeId = element.get('scope') self.scope = None self.isArgument = element.get('isArgument') == 'true' self.isArray = element.get('isArray') == 'true' self.isClass = element.get('isClass') == 'true' self.isConst = element.get('isConst') == 'true' self.isGlobal = element.get('access') == 'Global' self.isExtern = element.get('isExtern') == 'true' self.isLocal = element.get('isLocal') == 'true' self.isPointer = element.get('isPointer') == 'true' self.isReference = element.get('isReference') == 'true' self.isStatic = element.get('isStatic') == 'true' self.constness = element.get('constness') if self.constness: self.constness = int(self.constness) def __repr__(self): attrs = ["Id", "nameTokenId", "typeStartTokenId", "typeEndTokenId", "access", "scopeId", "isArgument", "isArray", "isClass", "isConst", "isGlobal", "isExtern", "isLocal", "isPointer", "isReference", "isStatic", "constness", ] return "{}({})".format( "Variable", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def setId(self, IdMap): self.nameToken = IdMap[self.nameTokenId] self.typeStartToken = IdMap[self.typeStartTokenId] self.typeEndToken = IdMap[self.typeEndTokenId] self.scope = IdMap[self.scopeId] class ValueFlow: """ ValueFlow::Value class Each possible value has a ValueFlow::Value item. Each ValueFlow::Value either has a intvalue or tokvalue C++ class: http://cppcheck.net/devinfo/doxyoutput/classValueFlow_1_1Value.html Attributes: values Possible values """ Id = None values = None class Value: """ Value class Attributes: intvalue integer value tokvalue token value floatvalue float value containerSize container size condition condition where this Value comes from valueKind 'known' or 'possible' inconclusive Is value inconclusive? """ intvalue = None tokvalue = None floatvalue = None containerSize = None condition = None valueKind = None inconclusive = False def isKnown(self): return self.valueKind and self.valueKind == 'known' def isPossible(self): return self.valueKind and self.valueKind == 'possible' def __init__(self, element): self.intvalue = element.get('intvalue') if self.intvalue: self.intvalue = int(self.intvalue) self.tokvalue = element.get('tokvalue') self.floatvalue = element.get('floatvalue') self.containerSize = element.get('container-size') self.condition = element.get('condition-line') if self.condition: self.condition = int(self.condition) if element.get('known'): self.valueKind = 'known' elif element.get('possible'): self.valueKind = 'possible' if element.get('inconclusive'): self.inconclusive = True def __repr__(self): attrs = ["intvalue", "tokvalue", "floatvalue", "containerSize", "condition", "valueKind", "inconclusive"] return "{}({})".format( "Value", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def __init__(self, element): self.Id = element.get('id') self.values = [] for value in element: self.values.append(ValueFlow.Value(value)) def __repr__(self): attrs = ["Id", "values"] return "{}({})".format( "ValueFlow", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class Suppression: """ Suppression class This class contains a suppression entry to suppress a warning. Attributes errorId The id string of the error to suppress, can be a wildcard fileName The name of the file to suppress warnings for, can include wildcards lineNumber The number of the line to suppress warnings from, can be 0 to represent any line symbolName The name of the symbol to match warnings for, can include wildcards """ errorId = None fileName = None lineNumber = None symbolName = None def __init__(self, element): self.errorId = element.get('errorId') self.fileName = element.get('fileName') self.lineNumber = element.get('lineNumber') self.symbolName = element.get('symbolName') def __repr__(self): attrs = ['errorId' , "fileName", "lineNumber", "symbolName"] return "{}({})".format( "Suppression", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def isMatch(self, file, line, message, errorId): if ((self.fileName is None or fnmatch(file, self.fileName)) and (self.lineNumber is None or line == self.lineNumber) and (self.symbolName is None or fnmatch(message, '*'+self.symbolName+'*')) and fnmatch(errorId, self.errorId)): return True else: return False class Configuration: """ Configuration class This class contains the directives, tokens, scopes, functions, variables, value flows, and suppressions for one configuration. Attributes: name Name of the configuration, "" for default directives List of Directive items tokenlist List of Token items scopes List of Scope items functions List of Function items variables List of Variable items valueflow List of ValueFlow values """ name = '' directives = [] tokenlist = [] scopes = [] functions = [] variables = [] valueflow = [] def __init__(self, confignode): self.name = confignode.get('cfg') self.directives = [] self.tokenlist = [] self.scopes = [] self.functions = [] self.variables = [] self.valueflow = [] arguments = [] for element in confignode: if element.tag == "standards": self.standards = Standards(element) if element.tag == 'directivelist': for directive in element: self.directives.append(Directive(directive)) if element.tag == 'tokenlist': for token in element: self.tokenlist.append(Token(token)) # set next/previous.. prev = None for token in self.tokenlist: token.previous = prev if prev: prev.next = token prev = token if element.tag == 'scopes': for scope in element: self.scopes.append(Scope(scope)) for functionList in scope: if functionList.tag == 'functionList': for function in functionList: self.functions.append(Function(function)) if element.tag == 'variables': for variable in element: var = Variable(variable) if var.nameTokenId: self.variables.append(var) else: arguments.append(var) if element.tag == 'valueflow': for values in element: self.valueflow.append(ValueFlow(values)) IdMap = {None: None, '0': None, '00000000': None, '0000000000000000': None} for token in self.tokenlist: IdMap[token.Id] = token for scope in self.scopes: IdMap[scope.Id] = scope for function in self.functions: IdMap[function.Id] = function for variable in self.variables: IdMap[variable.Id] = variable for variable in arguments: IdMap[variable.Id] = variable for values in self.valueflow: IdMap[values.Id] = values.values for token in self.tokenlist: token.setId(IdMap) for scope in self.scopes: scope.setId(IdMap) for function in self.functions: function.setId(IdMap) for variable in self.variables: variable.setId(IdMap) for variable in arguments: variable.setId(IdMap) def __repr__(self): attrs = ["name"] return "{}({})".format( "Configuration", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class Platform: """ Platform class This class contains type sizes Attributes: name Name of the platform char_bit CHAR_BIT value short_bit SHORT_BIT value int_bit INT_BIT value long_bit LONG_BIT value long_long_bit LONG_LONG_BIT value pointer_bit POINTER_BIT value """ name = '' char_bit = 0 short_bit = 0 int_bit = 0 long_bit = 0 long_long_bit = 0 pointer_bit = 0 def __init__(self, platformnode): self.name = platformnode.get('name') self.char_bit = int(platformnode.get('char_bit')) self.short_bit = int(platformnode.get('short_bit')) self.int_bit = int(platformnode.get('int_bit')) self.long_bit = int(platformnode.get('long_bit')) self.long_long_bit = int(platformnode.get('long_long_bit')) self.pointer_bit = int(platformnode.get('pointer_bit')) def __repr__(self): attrs = ["name", "char_bit", "short_bit", "int_bit", "long_bit", "long_long_bit", "pointer_bit"] return "{}({})".format( "Platform", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class Standards: """ Standards class This class contains versions of standards that were used for the cppcheck Attributes: c C Standard used cpp C++ Standard used posix If Posix was used """ def __init__(self, standardsnode): self.c = standardsnode.find("c").get("version") self.cpp = standardsnode.find("cpp").get("version") self.posix = standardsnode.find("posix") is not None def __repr__(self): attrs = ["c", "cpp", "posix"] return "{}({})".format( "Standards", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class CppcheckData: """ Class that makes cppcheck dump data available Contains a list of Configuration instances Attributes: configurations List of Configurations To iterate through all configurations use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: print('cfg: ' + cfg.name) @endcode To iterate through all tokens in each configuration use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: print('cfg: ' + cfg.name) code = '' for token in cfg.tokenlist: code = code + token.str + ' ' print(' ' + code) @endcode To iterate through all scopes (functions, types, etc) use such code: @code data = cppcheckdata.parsedump(...) for cfg in data.configurations: print('cfg: ' + cfg.name) for scope in cfg.scopes: print(' type:' + scope.type + ' name:' + scope.className) @endcode """ rawTokens = [] platform = None configurations = [] suppressions = [] def __init__(self, filename): self.configurations = [] data = ElementTree.parse(filename) for platformNode in data.getroot(): if platformNode.tag == 'platform': self.platform = Platform(platformNode) for rawTokensNode in data.getroot(): if rawTokensNode.tag != 'rawtokens': continue files = [] for node in rawTokensNode: if node.tag == 'file': files.append(node.get('name')) elif node.tag == 'tok': tok = Token(node) tok.file = files[int(node.get('fileIndex'))] self.rawTokens.append(tok) for i in range(len(self.rawTokens) - 1): self.rawTokens[i + 1].previous = self.rawTokens[i] self.rawTokens[i].next = self.rawTokens[i + 1] for suppressionsNode in data.getroot(): if suppressionsNode.tag == "suppressions": for suppression in suppressionsNode: self.suppressions.append(Suppression(suppression)) # root is 'dumps' node, each config has its own 'dump' subnode. for cfgnode in data.getroot(): if cfgnode.tag == 'dump': self.configurations.append(Configuration(cfgnode)) def __repr__(self): attrs = ["configurations", "platform"] return "{}({})".format( "CppcheckData", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) # Get function arguments def getArgumentsRecursive(tok, arguments): if tok is None: return if tok.str == ',': getArgumentsRecursive(tok.astOperand1, arguments) getArgumentsRecursive(tok.astOperand2, arguments) else: arguments.append(tok) def getArguments(ftok): if (not ftok.isName) or (ftok.next is None) or ftok.next.str != '(': return None args = [] getArgumentsRecursive(ftok.next.astOperand2, args) return args def parsedump(filename): """ parse a cppcheck dump file """ return CppcheckData(filename) def astIsFloat(token): """ Check if type of ast node is float/double """ if not token: return False if token.str == '.': return astIsFloat(token.astOperand2) if token.str in '+-*/%': return astIsFloat(token.astOperand1) or astIsFloat(token.astOperand2) if not token.variable: # float literal? if token.str[0].isdigit(): for c in token.str: if c == 'f' or c == '.' or c == 'E': return True return False typeToken = token.variable.typeStartToken endToken = token.variable.typeEndToken while typeToken != endToken: if typeToken.str == 'float' or typeToken.str == 'double': return True typeToken = typeToken.next if typeToken.str == 'float' or typeToken.str == 'double': return True return False class CppCheckFormatter(argparse.HelpFormatter): """ Properly formats multiline argument helps """ def _split_lines(self, text, width): # this is the RawTextHelpFormatter._split_lines if text.startswith('R|'): return text[2:].splitlines() return argparse.HelpFormatter._split_lines(self, text, width) def ArgumentParser(): """ Returns an argparse argument parser with an already-added argument definition for -t/--template """ parser = argparse.ArgumentParser(formatter_class=CppCheckFormatter) parser.add_argument('-t', '--template', metavar='', default='{callstack}: ({severity}) {message}', help="R|Format the error messages. E.g.\n" "'{file}:{line},{severity},{id},{message}' or\n" "'{file}({line}):({severity}) {message}' or\n" "'{callstack} {message}'\n" "Pre-defined templates: gcc, vs, edit") parser.add_argument("dumpfile", nargs='*', help="Path of dump files from cppcheck.") parser.add_argument("--cli", help="Addon is executed from Cppcheck", action="store_true") parser.add_argument("-q", "--quiet", help='do not print "Checking ..." lines', action="store_true") return parser def simpleMatch(token, pattern): for p in pattern.split(' '): if not token or token.str != p: return False token = token.next return True def reportError(location, severity, message, addon, errorId, extra=''): if '--cli' in sys.argv: msg = { 'file': location.file, 'linenr': location.linenr, 'column': location.column, 'severity': severity, 'message': message, 'addon': addon, 'errorId': errorId, 'extra': extra} sys.stdout.write(json.dumps(msg) + '\n') else: loc = '[%s:%i]' % (location.file, location.linenr) if len(extra) > 0: message += ' (' + extra + ')' sys.stderr.write('%s (%s) %s [%s-%s]\n' % (loc, severity, message, addon, errorId)) cppcheck-1.90/addons/doc/000077500000000000000000000000001357737443600152635ustar00rootroot00000000000000cppcheck-1.90/addons/doc/img/000077500000000000000000000000001357737443600160375ustar00rootroot00000000000000cppcheck-1.90/addons/doc/img/cppcheck-gui-addons.png000066400000000000000000000561351357737443600223670ustar00rootroot00000000000000PNG  IHDR4觿 pHYs+ IDATxy\Lgi3մLMEP.QiB咾_7pzu* ݒ,mJ6?jܩ~yy99gg3||~Z[[;ځ!"""JJJȷ#@.D^^h2椥!&L0ځ|#TRR8aaaaaaYY1r"Ҍ0lu٣Xɓюo./))TRR"atcL&,--׿cm{4# 0>zo.onnޮݣ#''3F^\^?2юpFx~ B[[y9rxiFG; x%8 1v^\^|3;߿׻#[}V^o>6-F({zz:ujdB޽; SVVfii9iҤSNmڴѣOZQ8}tL87=G _0ntȟ3ghKKhaZ.733h4MUU}ռr劃?g̘cǎ~ǜ9sFOOÇfH ;;{'OVRRz3~k@~~~6l~FbH!֭[{<ܴi?b 6~q#'OH7ov,` y\~С{GL޾͛7AAAͫ~ii2lll1"aֻwשּׂǏΝݻw߸qcRWWy-rrr Nggg_O}.]]jŋ†*k͛7ofm&LB(//OLLl'N|–-[aÆYf͘1ٳgX >}PSSƏ܅ N4i?Pvcnn{' ]r{ OGѱw^mmm55M6577c׮]9s YxʹiLc_{Ç͛8qӖ-[8rc=h4Z[[BHKKٳ ~~~666uvvM4kDKKѣ344Guuumڴɩ~ҤIQQQ2%%%,{L<xÆ ?({{M>};vk2}M6-,,F555>NN!zzzϟ?G}򥱱qɒ%\,@Kׯ766>|_~<6o~9v؜9sOiLfDDÇcbb ݱFB/_믿Lʕ+Ϟ=LtwwTVVzիWG)A~:LWUUegQQђ%KvU]]dee؈5ӧOeeek֬.aqqq/ :u*//̙3xꕂB@@QZZӧOU^m۶>0 BCC?~!##ӣ'޻w/77wիWO =|0$$$!!AWWk}ܹ999k׮p,,,RRRB ǏܹsG&y!ssm۶M4u݄WWקOoܸ+\jF{ULLLxxB{3gNNNNff+mod2/^ZSmm߯[0((˗OKKkjj_Bqqq;v ͽr F[KJJZbEHHy{%Y~k8۷oݺrK.!Ly^|N}}}SSScbb믂,ѱf͚ .>N777--Cmٲ͛7d {ύX`{p[YYYT*_~=**ѣGIIIx#lN244ľ&''O>}C}P`_˖-{٪UPo.V}?vU\\x_&oݺuɤK.\;---88x =z\\\駟=0șKNN˳TQQ ʯ^ $$n:ƍg677'H[n¼UUU8plD$=/##$)==NTTtԩ=QUU5k//̆ rM lľZ[[zzzƲǒ>>>}}ocȹoԩ'Oܾ};Bh̙&MҲ²coZZZ|||ښ`0dee{xb)))"HP>|7MyYXX`MeffVWW_D"͜9s'c… V ]{iffVZZZ__/++?޺u ![XXX^^;mڴD5H---l2e//sII ŋ򋔔OLL B(,,C__`(**b-zyy?P=Y[[I$ {R ? ԩS322B˖-߲e իyyyyyyMMMìWWqXa3++LJW__ɒ%X122JJJs{{MRRRYYϟY{a{p޾}{ƌ3g$Hƍ*GGGZJVVV@@ :89ŋ:::__MИ1chCG_l{7nضm(NߴiSdd$BJFDڵkB4mʔ)؄%HʪQQQ>ŋB4{cccCCWyy9^H"sز4L9r~~~l+***::Z^^̬jUU'MeP{7W`gyy9^'i0SWW nGxbQQQc2 lWij 67y䀀_5//"00Iׯ:u TJb XSUUUt:Da}QYY)&&*))y%h?zBDTRRajjc]v?` 2\YYY|oc٨D"!d2ۻ~-VVVZZZlh48BN dk޼y;wĖMJJ [񃰫kwmjj"MMMا[hmmĽJiii`0 dhhx-EEE##P}}}|bAN"e썀- }֖///=tuuA:Z8Ѐ%d2&$%%4"$$xT/^ ޹s_ooގmi{{/_^zMP]TUUէCjlAFF?eSUU&++ݻmmmWa!!7o ^|رc}/,,6HVon ʞ;wd>~ܼϿÐƍ\Џ?dɒ/^zjѢEY/F_-[v/_*..=xׯ F?3h4!XSS3`׭ӧOGz{{?[ff&V>x X /_Ħpedd=gϞ7@ ̜93::]$ **zY,¤lYYY!!!*AAvlyH7222>>͛_{.wcǎK --]YYq/))>SRRR={fhh-X)--zNщ+))|kqqq؁ؗSbÇl1κggg"&W2}TTTsssӁ111>}"Ӷmbcc󛛛_|}nnn۷oOee%vE˟-((*_~ƍ|f5uT*Օ}C0uԔj?c+~UJJJBB!tzKct:6㗚tR)իWt[z,G}VVV`%::YhٳgZZZa}e```yĉ l?(8b0Ǟ+**9ϐ޽r vFL#Ĵbrą /_.///7oo˗/wtt8pFOOf…X}SN-Y,))IWW+?|ss3gd8q`HHHN;w 9s݀eld%((WGGJfddS^^ڵk۶mtRLL0k#״iӨTeΜ9#..>nrCc~hRRR7#yyS Դm۶>>>CCݻw>;eʔe˖3 55)Sٳg7mJII4FEDDDDDdannbw?sQQѸq㌍¹s [[['LuVPrrmZ[[%%%9r۷WKKˮ.eeNp`|||9xZgggsss33;vlܸ\FFIIk׮bA$Ϟ=O?>|X__ѢEy```O͛7o+:::ǛKII͝;Xti_IJJ۷r;e422:ۃ`M2]tinn9sdO?YFFF'O^[[>bA9onddD"lmmW^ jkkğ;szI&8qbFzc2ϟ߼y3@8qC8ؕ.llllllB"""cKBK,YdI–-[lZ8qDuuu/_{+j Yڵk׮]q",-----{39rȑ~b!`iiiZZZ$ED"͛7r~밾 4? Ν;A H|I Fƍ7,,,F;o8ŋةǧO>}tΜ9јѣ{.X`#!co0wB0گ?9v0?~\jU[[F;~x pc2AAA^^^حthG4Bcr1Řюb|۱LO_.䮳bVTy9rxiFOQmFX{9rxiuo`,Zh={W߸=G;!4A.rA.rA.rA.rA.rA.sm۶nڻΝ; rܹsTjKKpDZ./))y)D0$C塡:::+Wx"^{###111kk/_ѓ'Oܲe ^ظvZYYYyyy??NP^^o7iҤCaoݺ&..`0N86r.wrrrrr+//GuwwYYYlڴҥKX͢"WWא *?)((|YLLѣGz˗/w]PPZjٳgkkk322fΜɩmMSRR544&OzmEE/D2773gVƍg677'H[n/_{n11199m۶bD"qƍ! tDzm]])S8rŋh4Bf///#HX%%%lBQQ[&H !T__?~x\AA#eFPTTԭ[=XY-""]VV!˗ׯ_ZUUNGȼx/ Q(b Aaa!V/wikk vww`5qyLLLwwwzzzZZZZZZFFƬY.^)&&Ͽ}6V&666??!Wׯ_onn&T*`ŋ/_.//xyy[~8z-dqqqMMM|?DrvvްaC_=2̓'OY@ L<ߺ,66f,,,{w\p7w\p7wMMMM555D"H$_@%)))$$4˫( @znii}0yyy_M!2D""C==k R-\p7w\p7w\p7w\p7w\p!޾}0`5 ~AWWwj8B"""l gH/rppPSSkkkc[vDV^^~I}}}11'ݻdb>|0k, 2mڴիW3f̐srr,--Ǐ5 呑޽svv&l+?~ڵkBuڵ+NػwoNNιsBBB.\/--]vm]]BHZZSLYbʕ+Ǐ_\\ӘӧO&#fr9>ڵkO_i4ځ֬Y`0V\ڹsɒ%K^xJKK+((P(k֬uBz\g IDATNJJJJKK+((|kYHHHIIiΜ9ô|. brOOOZ...&Mruu=}ɓ'{|8222&&k.zaaal@ h4uuu__e˖566.\޾ 33300s a9_|rmm'N8::rPޗǏ8q55?`{ׯ_?}TUUUSS% @>|ߡa<<<Ɩ9(gԩSۭ[[[[;::B::: wnnn>w\UU… B7oLHHs^^^]]uɺuTTTɠСC""""""vvv!W&&&JKK9rڵkOдzyy)((̚5֭[H$^~t:}ڴiwF_ *%%%߽{Ƕɓ{:88888Fp NrQQѸT`\0nnnnnn*o߾mhhB`X\Р;`Tv qcCQQQww7ţ 0aBxxxH\\\tׯ_6iii'eeeOOO1lܸNYYK.=z(ޑ7Np*`!6(hڥK+++wڅ777z>}Zvmyyc\]]+**B&MJLLvvvnnnFEDD\tٳgwݹsZZZˏ?ǶS޹sGBB"777$$m ]b+!;;vspJJJ```MM`srr!nnn_7oތ"׮]6m=H466={{B4H$Q(#^)///$$׻;TSSC lbW ,XP\\tRg|.ڻwoCC?#t: ((_sNP墢T=˗/+)))++"***{wnff //WS]xx8ɼr劓ӷN_[|cN8qĉ2( @ iDDk 6ihh LTWWnL&ӦMa+1 xxW%WWW'''333dllm0,׾>|['MJJzjGGG[[[bbbqqqCC??2ByyyXe;;Ϸ ޹d2T*//o_M BHJJǏx5]]]͛7;99~Ẏdݺu*** %%ug2 %%wuuihh[[[?}R#,--ϟڻ~ARRRGGz…}55` !۷KIIXML`A/_ bccmllZ^^...N"9-`nn>`qqq&&&C ʕ+ǏOHH@|:;;kkk؊r|(~{w8)iii9y$vO<)l_ɓ'K,1557,ױfϞ8k$~r9  r9  r9  r9  r9ݾ\J&Bk׮=phGkCCCCC`|.u̙3EEEeeeᇤ8BpM{\~9wwwgg第+WSSiӦq6Q9!d.okk_ٳfYYYQQŋ?~!`0oߎ%9&(''7a„p|ٳVVV?F||>>l^8/_lll\ZZ7"---466644^WMMm߾}˗/f`p2kiiQHB&)$$DP": Ϥ+++ӧO~d2m6l8qD~~~nnF !aaa 7ruuMLL|}ss={@od2y޽ӧ+**o޼ZlΝ;7'''[ZZBǎd/T*СC]]]>]ƍ555ARRD"wtta땕[ZZΜ9llhhWVVF=x //!!!f'%%uvv7nܸq$'Z0p4ww3g\pAUUUUUٳ..._UVVvԩFFFsF988XZZ͟?_UUu]īWFEEhڒutttO:%...((뫯/%%ٳg;88#8544\]]>}v}Z[[cMMM6mVTTlnn޶m PllMUſA5kF;:;;kkk}bQ7\$)) ; `ю5 {rssdddΝ;`0F;"`pe.wsse+\p7w\p7w\p7w\p7NV۷oFP4559/0q2744X-55cUDDmyzz:lw]]]jjjmmmTu̙3EEEeeeᇤAEOcHw9;;;w9+++++kʕisgN$]v U_~ٳgϚ5kŋ/Z!jmm,$$GCCí[+**>IYYB 'Ώ˫lllؠӓFʛ7ojkkY w~g>|WW^]vm߾}&&&A)))555#>L&TjN>Ho~u͛7S>0fq~]HHh޽3g|͛#Tj[[ϟ{O>\H$"LfGGGww7BB qq℣〃rJd-d2T*ի<<<DZ`l,sal32w_~7oB^z˖-^]JJpњ[NEEeA9̙3.\PUUUUU={ Bh&&&sY ĉ9woIC EXN0q\\TT4..n08)0q2s5 \p7w\p7w\p7w\p7w\p7w\p7wVymCCÀ(&2N]]rS`d.G-OOOg0bbb#qAMM:zzzd29??/3gLNKKCm޼õDDD-ZTQQU d2Y\\׬HHH,X)ʄ <<}QQQ:::LfhhhnnˇB%%%...v***nnnX}AAUVܹJ--Geff*++/Zd W.dzTWW׮]|||_ aaaGׯ_,X@@@@FFf岲SD"_ҥKx^zuk׮͛7Jxyymذ#Gtuu%%%W\+PDDh```BBBQQBHOOqL77U222ܺvWW˗cBϊtÆ <q℣!qqӧXYKK:##PVV۷oB˗߿?x `0ɑegg}UZWWBCCIOMM-33[铚Z?1XZZOn 0ÒyxxCJHH}O.))ikk{Ett~:Xo߾MMMMMM}MGGǽ{X888>|˗/Ǐ?z(k+WVWW_p !dgg?c yyy~kk+v{kk+r7nlmmmmmsudݺu***c zVJJݻFFF...=.8'Om...O>??8==}Ŭ***\@DDDDDDd?~K.cEDD DDDLMMB<:t߰PllMU2..dcAgggmm-NNTT***7j89orA.rA.rA.rA.' Ivvv]]Y_OBBJ}Ye_UpT#ICyor@`{NUUGy׍ZJT#i{dҡ{o6lGG]]ݬYF; QT*ӧ˳>];FN[Õ'rYcO+B"Ǽ|c hr9|X{֮]{1<9?.WVVǖ ӦM!8ߡ{YZZy{tqqh#o,X設sθq_x 577q:oBHHhڵiaO#o8sϱ̞=FȬ^!G)) mmUVuvvh4kk뢢)**8p@SS3%%ECCÇ{^k333ٍ7)++?!t---))y eddRSSBUUUrrrO| #>>o޼P۷o9"###''wӖ+VPUU} (njj$ubbb,- =$H'O޾};V+111uuG!/^ǶyfOOOlΝ; , v<<<B?~;w˗ʍrrr۶mwEEEE'Ms.B...gYzqձQ;{ttɓEEEtzHHTTTN81n8Gu`/ܒ'N?gIDAT{=nf666 gϞ~|}}=BÇ4ƦMAAAAAa0S FKrĉű0u,XPYYYTT}"뛛_zuy&p2׳+>>>>>>33s>}deevcǎVTTV8|ldddnn+}:q℣cyyBW^ѦoaaS/^WUUu̙5k`;g֭V$''_S0o߾=}7o>|('';찰CԴO>¢v=uuu3g ccc޿=nܸ+W_v߿S6$##@_9v옜\TTTaaʕ+.]:k,///_\?hQQQd2pXgA2Yv kߜSRR( ۟/--7oތ"~~~Ϟ=cۑ7LkצMfooO$gϞ}޽~:ukڴiD"qeee!777555={^D"mmm2<`lϞ=SSS/1钒K,yB(,,lAAAo2 _p!DZv)vɓ'ׯ_M$?{!SNuwwǵFFFya!aQz{{#Ubbbp{---mmmW^qpp 3f077{oܸ'Hϟh۷|||NNNX ߳g/r%ֵH$RKK۷o F.\\\lmm-[1HoՉN:+"jjj<< MBYlYff-[/߀CayXrVO.--=x𠇇i: Ǧ[|!!!d}b0{CNN_RR 6mguE2ݻׯ߼y}mt6YYŶ*++ɶG}Ⱥ[[[--..ƿj-ǎ¿L4IYYΝ;=5k_{~ ٳKJJk[[ׯg}Etzyy9~AaaB*V# 77]v޽{>}3gVz;99}hٲeYaiXB(##ԩSNNN6lQ,;bc {СCx7nIIIO444HKKKJJ"~SRRjllBݺurIIIW^hkk2A:RRR4B#88855d644ܸq!uVŋ\d2T*__ WRRЀϻx񜜜f++ANc|ۊk׮uwwգv`ssSr۷gee;wnٲe}uvʕӶNNNǏv';;;233cbbcooxeUKHH^Y*j?iPSWbc"1X c,"#B-,Rb;jRmCQG*(XP).q{DJ}͛5 ^'7g˕]rٳR)}ք .666Ϟ=;|FF "66ѣGm!ݹs@Vg(ʬ,Rb $\\\\nYYYWǏ䔔~}˖-t+ٳkܹӿJU^^tWt2sgSo{~;N^[}{XYncc{|DP(o߮;[jG9yNB͛M6a„SN}D"9rHZZ۟^|yLLD"ٶm ,X`iiꚛp~dBȺu 鋬 KJJ,--=<<'Ȍ=q?xž666J2--B8qƍΝ۷o_ggCq8dɒ'O?_re :YYYaaa2L&aCBB󽼼/Quq9x~]xqȑ++ǏرJ&%$$צO;({.ǣOBf͚u~rK$3g&''ӝ'%%IҡC9rҤIyɒ%bX**ʘ6CtEQ)))RT,ٳ'55}Poo+Wv#~ǩSzzzCՌvbψ,f7X7"xҥKLfĈ>aT&B_NNҥKX[[YMbϯ-Y5|p L/s[ݳ0:#,/~,Z-h8皙ZA?,bʕ+ ]|Y,jk/7NztO2)j7;wN> _o.ŇÓ\]ՒOdYdԯ2,6vx-w)b`7d9! YnrvCݘe[/Ak4d9QQh5aZV*Eq8oz݄(VT*U* \"(J.pz9FhbBP(2c`7d9! YnrvCݐ,߻w|`zEv`2 ݽt׮]ڮ())tb+!d֭aaatـ=hαx<!SNߍ0aBQQъ+Μ9#!**&&O?mnn&YXXlذ5"""55o֬Ynnnӧ>srrD"\.߲eKs jmmrOjԨѣGB|}}KKK!fffSN={l_O&EGGB~gOOOp¾}ѣǍ7D"Ѱan*5k鲹9]pww2dHyyBhDT?3gΤS3VKӧjtx'ODK.޿ te͍AUTTB*++ڰ{~/=))ãK0;.si}7n߾vvv۩u+bqfffaaaaa۷+++ xBBB #effFGGK$={[EQ[nLOO߱c-8 t.;;Gkݐ,`7d91 usssMM 5@Gh4KKKFOfUUU}fff]Y (VR]]moo ž={2t Q*0^rq8`Tlf!_D-c`7d9! YnrvCݐ,`7wƍMMMVӧx2˛Fi˗/pPϱ}RVKu8$N=}?ywLq\ф8;;vP^^^"H*;wΨڽ{B1`,̼sNhh(Y;wFDDΚ5+==s;vM9"A+) fڵzZ|*Zo7oT*DSLINN֯s=ooo+++LhѢO퓒ƌ3lذYfT*z5kd2\< AαSOݷo]AAA˖-| UUU7n_ .zYׯN6McR[^^~k׮' N:uB2227o8q֭믿\ցSPP;vިP(N˅BaBBBSSS\\\W !b:NNNr|Ŋ!9]իWss3!R&ubsjeUVV4.Ϙ1###o>(?wwBA9!M,gffo]qEGG +WmmctYWşں.WWWݻuVnn;D&c,733r>jժ;vTVV666,ZHNcc!CAKKKjjj<|PT&&&D")++GO----[O2888< is Y2038-unsafe This reflects the fact that the user code is referring to a symbol which, when glibc defaults to 32-bit time support, might fail Y2038. General note: y2038.py will handle multiple configurations, and will emit diagnostics for each configuration in turn. 4. How to use the Y2038 cppcheck addon The Y2038 cppcheck addon is used like any other cppcheck addon: cppcheck --dump file1.c [ file2.c [...]]] y2038.py file1.c [ file2.c [...]]] Sample test C file is provided: test/y2038-test-1-bad-time-bits.c test/y2038-test-2-no-time-bits.c test/y2038-test-3-no-use-time-bits.c test/y2038-test-4-good.c These cover the cases described above. You can run them through cppcheck and y2038.py to see for yourself how the addon diagnostics look like. If this README is not outdated (and if it is, feel free to submit a patch), you can run cppcheck on these files as on any others: cppcheck --dump addons/y2038/test/y2038-*.c y2038.py addons/y2038/test/y2038-*.dump If you have not installed cppcheck yet, you will have to run these commands from the root of the cppcheck repository: make sudo make install ./cppcheck --dump addons/y2038/test/y2038-*.c PYTHONPATH=addons python addons/y2038/y2038.py addons/y2038/test/y2038-*.c.dump In both cases, y2038.py execution should result in the following: Checking addons/y2038/test/y2038-test-1-bad-time-bits.c.dump... Checking addons/y2038/test/y2038-test-1-bad-time-bits.c.dump, config ""... Checking addons/y2038/test/y2038-test-2-no-time-bits.c.dump... Checking addons/y2038/test/y2038-test-2-no-time-bits.c.dump, config ""... Checking addons/y2038/test/y2038-test-3-no-use-time-bits.c.dump... Checking addons/y2038/test/y2038-test-3-no-use-time-bits.c.dump, config ""... Checking addons/y2038/test/y2038-test-4-good.c.dump... Checking addons/y2038/test/y2038-test-4-good.c.dump, config ""... # Configuration "": # Configuration "": [addons/y2038/test/y2038-test-1-bad-time-bits.c:8]: (error) _TIME_BITS must be defined equal to 64 [addons/y2038/test/y2038-inc.h:9]: (warning) _USE_TIME_BITS64 is defined but _TIME_BITS was not [addons/y2038/test/y2038-test-1-bad-time-bits.c:10]: (information) addons/y2038/test/y2038-inc.h was included from here [addons/y2038/test/y2038-inc.h:9]: (warning) _USE_TIME_BITS64 is defined but _TIME_BITS was not [addons/y2038/test/y2038-test-2-no-time-bits.c:8]: (information) addons/y2038/test/y2038-inc.h was included from here [addons/y2038/test/y2038-test-3-no-use-time-bits.c:13]: (warning) timespec is Y2038-unsafe [addons/y2038/test/y2038-test-3-no-use-time-bits.c:15]: (warning) clock_gettime is Y2038-unsafe Note: y2038.py recognizes option --template as cppcheck does, including pre-defined templates 'gcc', 'vs' and 'edit'. The short form -t is also recognized. cppcheck-1.90/addons/findcasts.py000077500000000000000000000022711357737443600170530ustar00rootroot00000000000000#!/usr/bin/env python3 # # Locate casts in the code # import cppcheckdata import sys for arg in sys.argv[1:]: if arg.startswith('-'): continue print('Checking ' + arg + '...') data = cppcheckdata.parsedump(arg) for cfg in data.configurations: if len(data.configurations) > 1: print('Checking ' + arg + ', config "' + cfg.name + '"...') for token in cfg.tokenlist: if token.str != '(' or not token.astOperand1 or token.astOperand2: continue # Is it a lambda? if token.astOperand1.str == '{': continue # we probably have a cast.. if there is something inside the parentheses # there is a cast. Otherwise this is a function call. typetok = token.next if not typetok.isName: continue # cast number => skip output if token.astOperand1.isNumber: continue # void cast => often used to suppress compiler warnings if typetok.str == 'void': continue cppcheckdata.reportError(token, 'information', 'found a cast', 'findcasts', 'cast') cppcheck-1.90/addons/misc.py000066400000000000000000000137601357737443600160320ustar00rootroot00000000000000#!/usr/bin/env python3 # # Misc: Uncategorized checks that might be moved to some better addon later # # Example usage of this addon (scan a sourcefile main.cpp) # cppcheck --dump main.cpp # python misc.py main.cpp.dump import cppcheckdata import sys import re DEBUG = ('-debug' in sys.argv) VERIFY = ('-verify' in sys.argv) VERIFY_EXPECTED = [] VERIFY_ACTUAL = [] def reportError(token, severity, msg, id): if id == 'debug' and DEBUG == False: return if VERIFY: VERIFY_ACTUAL.append(str(token.linenr) + ':' + id) else: cppcheckdata.reportError(token, severity, msg, 'misc', id) def simpleMatch(token, pattern): for p in pattern.split(' '): if not token or token.str != p: return False token = token.next return True # Get function arguments def getArgumentsRecursive(tok, arguments): if tok is None: return if tok.str == ',': getArgumentsRecursive(tok.astOperand1, arguments) getArgumentsRecursive(tok.astOperand2, arguments) else: arguments.append(tok) def getArguments(ftok): arguments = [] getArgumentsRecursive(ftok.astOperand2, arguments) return arguments def isStringLiteral(tokenString): return tokenString.startswith('"') # check data def stringConcatInArrayInit(configurations, rawTokens): # Get all string macros stringMacros = [] for cfg in configurations: for directive in cfg.directives: res = re.match(r'#define[ ]+([A-Za-z0-9_]+)[ ]+".*', directive.str) if res: macroName = res.group(1) if macroName not in stringMacros: stringMacros.append(macroName) # Check code arrayInit = False for i in range(len(rawTokens)): if i < 2: continue tok1 = rawTokens[i-2].str tok2 = rawTokens[i-1].str tok3 = rawTokens[i-0].str if tok3 == '}': arrayInit = False elif tok1 == ']' and tok2 == '=' and tok3 == '{': arrayInit = True elif arrayInit and (tok1 in [',', '{']): isString2 = (isStringLiteral(tok2) or (tok2 in stringMacros)) isString3 = (isStringLiteral(tok3) or (tok3 in stringMacros)) if isString2 and isString3: reportError(rawTokens[i], 'style', 'String concatenation in array initialization, missing comma?', 'stringConcatInArrayInit') def implicitlyVirtual(data): for cfg in data.configurations: for function in cfg.functions: if function.isImplicitlyVirtual is None: continue if not function.isImplicitlyVirtual: continue reportError(function.tokenDef, 'style', 'Function \'' + function.name + '\' overrides base class function but is not marked with \'virtual\' keyword.', 'implicitlyVirtual') def ellipsisStructArg(data): for cfg in data.configurations: for tok in cfg.tokenlist: if tok.str != '(': continue if tok.astOperand1 is None or tok.astOperand2 is None: continue if tok.astOperand2.str != ',': continue if tok.scope.type in ['Global', 'Class']: continue if tok.astOperand1.function is None: continue for argnr, argvar in tok.astOperand1.function.argument.items(): if argnr < 1: continue if not simpleMatch(argvar.typeStartToken, '...'): continue callArgs = getArguments(tok) for i in range(argnr-1, len(callArgs)): valueType = callArgs[i].valueType if valueType is None: argStart = callArgs[i].previous while argStart.str != ',': if argStart.str == ')': argStart = argStart.link argStart = argStart.previous argEnd = callArgs[i] while argEnd.str != ',' and argEnd.str != ')': if argEnd.str == '(': argEnd = argEnd.link argEnd = argEnd.next expression = '' argStart = argStart.next while argStart != argEnd: expression = expression + argStart.str argStart = argStart.next reportError(tok, 'debug', 'Bailout, unknown argument type for argument \'' + expression + '\'.', 'debug') continue if valueType.pointer > 0: continue if valueType.type != 'record' and valueType.type != 'container': continue reportError(tok, 'style', 'Passing record to ellipsis function \'' + tok.astOperand1.function.name + '\'.', 'ellipsisStructArg') break for arg in sys.argv[1:]: if arg in ['-debug', '-verify', '--cli']: continue print('Checking ' + arg + '...') data = cppcheckdata.parsedump(arg) if VERIFY: VERIFY_ACTUAL = [] VERIFY_EXPECTED = [] for tok in data.rawTokens: if tok.str.startswith('//'): for word in tok.str[2:].split(' '): if word in ['stringConcatInArrayInit', 'implicitlyVirtual', 'ellipsisStructArg']: VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word) stringConcatInArrayInit(data.configurations, data.rawTokens) implicitlyVirtual(data) ellipsisStructArg(data) if VERIFY: for expected in VERIFY_EXPECTED: if expected not in VERIFY_ACTUAL: print('Expected but not seen: ' + expected) sys.exit(1) for actual in VERIFY_ACTUAL: if actual not in VERIFY_EXPECTED: print('Not expected: ' + actual) sys.exit(1) cppcheck-1.90/addons/misra.py000077500000000000000000003212031357737443600162070ustar00rootroot00000000000000#!/usr/bin/env python3 # # MISRA C 2012 checkers # # Example usage of this addon (scan a sourcefile main.cpp) # cppcheck --dump main.cpp # python misra.py --rule-texts= main.cpp.dump # # Limitations: This addon is released as open source. Rule texts can't be freely # distributed. https://www.misra.org.uk/forum/viewtopic.php?f=56&t=1189 # # The MISRA standard documents may be obtained from https://www.misra.org.uk # # Total number of rules: 143 from __future__ import print_function import cppcheckdata import itertools import sys import re import os import argparse import codecs import string try: from itertools import izip as zip except ImportError: pass def grouped(iterable, n): """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ...""" return zip(*[iter(iterable)]*n) typeBits = { 'CHAR': None, 'SHORT': None, 'INT': None, 'LONG': None, 'LONG_LONG': None, 'POINTER': None } def simpleMatch(token, pattern): for p in pattern.split(' '): if not token or token.str != p: return False token = token.next return True def rawlink(rawtoken): if rawtoken.str == '}': indent = 0 while rawtoken: if rawtoken.str == '}': indent = indent + 1 elif rawtoken.str == '{': indent = indent - 1 if indent == 0: break rawtoken = rawtoken.previous else: rawtoken = None return rawtoken KEYWORDS = { 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while' } def getEssentialTypeCategory(expr): if not expr: return None if expr.str == ',': return getEssentialTypeCategory(expr.astOperand2) if expr.str in ('<', '<=', '==', '!=', '>=', '>', '&&', '||', '!'): return 'bool' if expr.str in ('<<', '>>'): # TODO this is incomplete return getEssentialTypeCategory(expr.astOperand1) if len(expr.str) == 1 and expr.str in '+-*/%&|^': # TODO this is incomplete e1 = getEssentialTypeCategory(expr.astOperand1) e2 = getEssentialTypeCategory(expr.astOperand2) #print('{0}: {1} {2}'.format(expr.str, e1, e2)) if e1 and e2 and e1 == e2: return e1 if expr.valueType: return expr.valueType.sign if expr.valueType and expr.valueType.typeScope and expr.valueType.typeScope.className: return "enum<" + expr.valueType.typeScope.className + ">" if expr.variable: typeToken = expr.variable.typeStartToken while typeToken: if typeToken.valueType: if typeToken.valueType.type == 'bool': return typeToken.valueType.type if typeToken.valueType.type in ('float', 'double', 'long double'): return "float" if typeToken.valueType.sign: return typeToken.valueType.sign typeToken = typeToken.next if expr.valueType: return expr.valueType.sign return None def getEssentialCategorylist(operand1, operand2): if not operand1 or not operand2: return None, None if (operand1.str in ('++', '--') or operand2.str in ('++', '--')): return None, None if ((operand1.valueType and operand1.valueType.pointer) or (operand2.valueType and operand2.valueType.pointer)): return None, None e1 = getEssentialTypeCategory(operand1) e2 = getEssentialTypeCategory(operand2) return e1, e2 def getEssentialType(expr): if not expr: return None if expr.variable: typeToken = expr.variable.typeStartToken while typeToken and typeToken.isName: if typeToken.str in ('char', 'short', 'int', 'long', 'float', 'double'): return typeToken.str typeToken = typeToken.next elif expr.astOperand1 and expr.astOperand2 and expr.str in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":"): if expr.astOperand1.valueType and expr.astOperand1.valueType.pointer > 0: return None if expr.astOperand2.valueType and expr.astOperand2.valueType.pointer > 0: return None e1 = getEssentialType(expr.astOperand1) e2 = getEssentialType(expr.astOperand2) if not e1 or not e2: return None types = ['bool', 'char', 'short', 'int', 'long', 'long long'] try: i1 = types.index(e1) i2 = types.index(e2) if i2 >= i1: return types[i2] return types[i1] except ValueError: return None elif expr.str == "~": e1 = getEssentialType(expr.astOperand1) return e1 return None def bitsOfEssentialType(expr): type = getEssentialType(expr) if type is None: return 0 if type == 'char': return typeBits['CHAR'] if type == 'short': return typeBits['SHORT'] if type == 'int': return typeBits['INT'] if type == 'long': return typeBits['LONG'] if type == 'long long': return typeBits['LONG_LONG'] return 0 def isCast(expr): if not expr or expr.str != '(' or not expr.astOperand1 or expr.astOperand2: return False if simpleMatch(expr, '( )'): return False return True def isFunctionCall(expr): if not expr: return False if expr.str != '(' or not expr.astOperand1: return False if expr.astOperand1 != expr.previous: return False if expr.astOperand1.str in KEYWORDS: return False return True def hasExternalLinkage(var): return var.isGlobal and not var.isStatic def countSideEffects(expr): if not expr or expr.str in (',', ';'): return 0 ret = 0 if expr.str in ('++', '--', '='): ret = 1 return ret + countSideEffects(expr.astOperand1) + countSideEffects(expr.astOperand2) def getForLoopExpressions(forToken): if not forToken or forToken.str != 'for': return None lpar = forToken.next if not lpar or lpar.str != '(': return None if not lpar.astOperand2 or lpar.astOperand2.str != ';': return None if not lpar.astOperand2.astOperand2 or lpar.astOperand2.astOperand2.str != ';': return None return [lpar.astOperand2.astOperand1, lpar.astOperand2.astOperand2.astOperand1, lpar.astOperand2.astOperand2.astOperand2] def getForLoopCounterVariables(forToken): """ Return a set of Variable objects defined in ``for`` statement and satisfy requirements to loop counter term from section 8.14 of MISRA document. """ if not forToken or forToken.str != 'for': return None tn = forToken.next if not tn or tn.str != '(': return None vars_defined = set() vars_exit = set() vars_modified = set() cur_clause = 1 te = tn.link while tn and tn != te: if tn.variable: if cur_clause == 1 and tn.variable.nameToken == tn: vars_defined.add(tn.variable) elif cur_clause == 2: vars_exit.add(tn.variable) elif cur_clause == 3: if tn.next and hasSideEffectsRecursive(tn.next): vars_modified.add(tn.variable) elif tn.previous and tn.previous.str in ('++', '--'): vars_modified.add(tn.variable) if tn.str == ';': cur_clause += 1 tn = tn.next return vars_defined & vars_exit & vars_modified def findCounterTokens(cond): if not cond: return [] if cond.str in ['&&', '||']: c = findCounterTokens(cond.astOperand1) c.extend(findCounterTokens(cond.astOperand2)) return c ret = [] if ((cond.isArithmeticalOp and cond.astOperand1 and cond.astOperand2) or (cond.isComparisonOp and cond.astOperand1 and cond.astOperand2)): if cond.astOperand1.isName: ret.append(cond.astOperand1) if cond.astOperand2.isName: ret.append(cond.astOperand2) if cond.astOperand1.isOp: ret.extend(findCounterTokens(cond.astOperand1)) if cond.astOperand2.isOp: ret.extend(findCounterTokens(cond.astOperand2)) return ret def isFloatCounterInWhileLoop(whileToken): if not simpleMatch(whileToken, 'while ('): return False lpar = whileToken.next rpar = lpar.link counterTokens = findCounterTokens(lpar.astOperand2) whileBodyStart = None if simpleMatch(rpar, ') {'): whileBodyStart = rpar.next elif simpleMatch(whileToken.previous, '} while') and simpleMatch(whileToken.previous.link.previous, 'do {'): whileBodyStart = whileToken.previous.link else: return False token = whileBodyStart while token != whileBodyStart.link: token = token.next for counterToken in counterTokens: if not counterToken.valueType or not counterToken.valueType.isFloat(): continue if token.isAssignmentOp and token.astOperand1.str == counterToken.str: return True if token.str == counterToken.str and token.astParent and token.astParent.str in ('++', '--'): return True return False def hasSideEffectsRecursive(expr): if not expr or expr.str == ';': return False if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '[': prev = expr.astOperand1.previous if prev and (prev.str == '{' or prev.str == '{'): return hasSideEffectsRecursive(expr.astOperand2) if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '.': e = expr.astOperand1 while e and e.str == '.' and e.astOperand2: e = e.astOperand1 if e and e.str == '.': return False if expr.isAssignmentOp or expr.str in {'++', '--'}: return True # Todo: Check function calls return hasSideEffectsRecursive(expr.astOperand1) or hasSideEffectsRecursive(expr.astOperand2) def isBoolExpression(expr): if not expr: return False if expr.valueType and (expr.valueType.type == 'bool' or expr.valueType.bits == 1): return True return expr.str in ['!', '==', '!=', '<', '<=', '>', '>=', '&&', '||', '0', '1', 'true', 'false'] def isConstantExpression(expr): if expr.isNumber: return True if expr.isName: return False if simpleMatch(expr.previous, 'sizeof ('): return True if expr.astOperand1 and not isConstantExpression(expr.astOperand1): return False if expr.astOperand2 and not isConstantExpression(expr.astOperand2): return False return True def isUnsignedInt(expr): return expr and expr.valueType and expr.valueType.type in ('short', 'int') and expr.valueType.sign == 'unsigned' def getPrecedence(expr): if not expr: return 16 if not expr.astOperand1 or not expr.astOperand2: return 16 if expr.str in ('*', '/', '%'): return 12 if expr.str in ('+', '-'): return 11 if expr.str in ('<<', '>>'): return 10 if expr.str in ('<', '>', '<=', '>='): return 9 if expr.str in ('==', '!='): return 8 if expr.str == '&': return 7 if expr.str == '^': return 6 if expr.str == '|': return 5 if expr.str == '&&': return 4 if expr.str == '||': return 3 if expr.str in ('?', ':'): return 2 if expr.isAssignmentOp: return 1 if expr.str == ',': return 0 return -1 def findRawLink(token): tok1 = None tok2 = None forward = False if token.str in '{([': tok1 = token.str tok2 = '})]'['{(['.find(token.str)] forward = True elif token.str in '})]': tok1 = token.str tok2 = '{(['['})]'.find(token.str)] forward = False else: return None # try to find link indent = 0 while token: if token.str == tok1: indent = indent + 1 elif token.str == tok2: if indent <= 1: return token indent = indent - 1 if forward is True: token = token.next else: token = token.previous # raw link not found return None def numberOfParentheses(tok1, tok2): while tok1 and tok1 != tok2: if tok1.str == '(' or tok1.str == ')': return False tok1 = tok1.next return tok1 == tok2 def findGotoLabel(gotoToken): label = gotoToken.next.str tok = gotoToken.next.next while tok: if tok.str == '}' and tok.scope.type == 'Function': break if tok.str == label and tok.next.str == ':': return tok tok = tok.next return None def findInclude(directives, header): for directive in directives: if directive.str == '#include ' + header: return directive return None # Get function arguments def getArgumentsRecursive(tok, arguments): if tok is None: return if tok.str == ',': getArgumentsRecursive(tok.astOperand1, arguments) getArgumentsRecursive(tok.astOperand2, arguments) else: arguments.append(tok) def getArguments(ftok): arguments = [] getArgumentsRecursive(ftok.astOperand2, arguments) return arguments def isalnum(c): return c in string.digits or c in string.ascii_letters def isHexEscapeSequence(symbols): """Checks that given symbols are valid hex escape sequence. hexadecimal-escape-sequence: \\x hexadecimal-digit hexadecimal-escape-sequence hexadecimal-digit Reference: n1570 6.4.4.4""" if len(symbols) < 3 or symbols[:2] != '\\x': return False return all([s in string.hexdigits for s in symbols[2:]]) def isOctalEscapeSequence(symbols): r"""Checks that given symbols are valid octal escape sequence: octal-escape-sequence: \ octal-digit \ octal-digit octal-digit \ octal-digit octal-digit octal-digit Reference: n1570 6.4.4.4""" if len(symbols) not in range(2, 5) or symbols[0] != '\\': return False return all([s in string.octdigits for s in symbols[1:]]) def isSimpleEscapeSequence(symbols): """Checks that given symbols are simple escape sequence. Reference: n1570 6.4.4.4""" if len(symbols) != 2 or symbols[0] != '\\': return False return symbols[1] in ("'", '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v') def isTernaryOperator(token): if not token: return False if not token.astOperand2: return False return token.str == '?' and token.astOperand2.str == ':' def getTernaryOperandsRecursive(token): """Returns list of ternary operands including nested onces.""" if not isTernaryOperator(token): return [] result = [] result += getTernaryOperandsRecursive(token.astOperand2.astOperand1) if token.astOperand2.astOperand1 and not isTernaryOperator(token.astOperand2.astOperand1): result += [token.astOperand2.astOperand1] result += getTernaryOperandsRecursive(token.astOperand2.astOperand2) if token.astOperand2.astOperand2 and not isTernaryOperator(token.astOperand2.astOperand2): result += [token.astOperand2.astOperand2] return result def hasNumericEscapeSequence(symbols): """Check that given string contains octal or hexadecimal escape sequences.""" if '\\' not in symbols: return False for c, cn in grouped(symbols, 2): if c == '\\' and cn in ('x' + string.octdigits): return True return False def isNoReturnScope(tok): if tok is None or tok.str != '}': return False if tok.previous is None or tok.previous.str != ';': return False if simpleMatch(tok.previous.previous, 'break ;'): return True prev = tok.previous.previous while prev and prev.str not in ';{}': if prev.str in '])': prev = prev.link prev = prev.previous if prev and prev.next.str in ['throw', 'return']: return True return False class Define: def __init__(self, directive): self.args = [] self.expansionList = '' res = re.match(r'#define [A-Za-z0-9_]+\(([A-Za-z0-9_,]+)\)[ ]+(.*)', directive.str) if res is None: return self.args = res.group(1).split(',') self.expansionList = res.group(2) def __repr__(self): attrs = ["args", "expansionList"] return "{}({})".format( "Define", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def getAddonRules(): """Returns dict of MISRA rules handled by this addon.""" addon_rules = [] compiled = re.compile(r'.*def[ ]+misra_([0-9]+)_([0-9]+)[(].*') for line in open(__file__): res = compiled.match(line) if res is None: continue addon_rules.append(res.group(1) + '.' + res.group(2)) return addon_rules def getCppcheckRules(): """Returns list of rules handled by cppcheck.""" return ['1.3', '2.1', '2.2', '2.4', '2.6', '5.3', '8.3', '12.2', '13.2', '13.6', '14.3', '17.5', '18.1', '18.2', '18.3', '18.6', '20.6', '22.1', '22.2', '22.4', '22.6'] def generateTable(): # print table numberOfRules = {} numberOfRules[1] = 3 numberOfRules[2] = 7 numberOfRules[3] = 2 numberOfRules[4] = 2 numberOfRules[5] = 9 numberOfRules[6] = 2 numberOfRules[7] = 4 numberOfRules[8] = 14 numberOfRules[9] = 5 numberOfRules[10] = 8 numberOfRules[11] = 9 numberOfRules[12] = 4 numberOfRules[13] = 6 numberOfRules[14] = 4 numberOfRules[15] = 7 numberOfRules[16] = 7 numberOfRules[17] = 8 numberOfRules[18] = 8 numberOfRules[19] = 2 numberOfRules[20] = 14 numberOfRules[21] = 12 numberOfRules[22] = 6 # Rules that can be checked with compilers: # compiler = ['1.1', '1.2'] addon = getAddonRules() cppcheck = getCppcheckRules() for i1 in range(1, 23): for i2 in range(1, numberOfRules[i1] + 1): num = str(i1) + '.' + str(i2) s = '' if num in addon: s = 'X (Addon)' elif num in cppcheck: s = 'X (Cppcheck)' num = num + ' ' print(num[:8] + s) def remove_file_prefix(file_path, prefix): """ Remove a file path prefix from a give path. leftover directory separators at the beginning of a file after the removal are also stripped. Example: '/remove/this/path/file.c' with a prefix of: '/remove/this/path' becomes: file.c """ result = None if file_path.startswith(prefix): result = file_path[len(prefix):] # Remove any leftover directory separators at the # beginning result = result.lstrip('\\/') else: result = file_path return result class Rule(object): """Class to keep rule text and metadata""" MISRA_SEVERITY_LEVELS = ['Required', 'Mandatory', 'Advisory'] def __init__(self, num1, num2): self.num1 = num1 self.num2 = num2 self.text = '' self.misra_severity = '' @property def num(self): return self.num1 * 100 + self.num2 @property def misra_severity(self): return self._misra_severity @misra_severity.setter def misra_severity(self, val): if val in self.MISRA_SEVERITY_LEVELS: self._misra_severity = val else: self._misra_severity = '' @property def cppcheck_severity(self): return 'style' def __repr__(self): return "%d.%d (%s)" % (self.num1, self.num2, self.misra_severity) class MisraSettings(object): """Hold settings for misra.py script.""" __slots__ = ["verify", "quiet", "show_summary"] def __init__(self, args): """ :param args: Arguments given by argparse. """ self.verify = False self.quiet = False self.show_summary = True if args.verify: self.verify = True if args.cli: self.quiet = True self.show_summary = False if args.quiet: self.quiet = True if args.no_summary: self.show_summary = False def __repr__(self): attrs = ["verify", "quiet", "show_summary", "verify"] return "{}({})".format( "MisraSettings", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) class MisraChecker: def __init__(self, settings, stdversion="c90"): """ :param settings: misra.py script settings. """ self.settings = settings # Test validation rules lists self.verify_expected = list() self.verify_actual = list() # List of formatted violation messages self.violations = dict() # if --rule-texts is specified this dictionary # is loaded with descriptions of each rule # by rule number (in hundreds). # ie rule 1.2 becomes 102 self.ruleTexts = dict() # Dictionary of dictionaries for rules to suppress # Dict1 is keyed by rule number in the hundreds format of # Major * 100 + minor. ie Rule 5.2 = (5*100) + 2 # Dict 2 is keyed by filename. An entry of None means suppress globally. # Each file name entry contains a list of tuples of (lineNumber, symbolName) # or an item of None which indicates suppress rule for the entire file. # The line and symbol name tuple may have None as either of its elements but # should not be None for both. self.suppressedRules = dict() # List of suppression extracted from the dumpfile self.dumpfileSuppressions = None # Prefix to ignore when matching suppression files. self.filePrefix = None # Number of all violations suppressed per rule self.suppressionStats = dict() self.stdversion = stdversion def __repr__(self): attrs = ["settings", "verify_expected", "verify_actual", "violations", "ruleTexts", "suppressedRules", "dumpfileSuppressions", "filePrefix", "suppressionStats", "stdversion"] return "{}({})".format( "MisraChecker", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) ) def get_num_significant_naming_chars(self, cfg): if cfg.standards and cfg.standards.c == "c99": return 63 else: return 31 def misra_2_7(self, data): for func in data.functions: # Skip function with no parameter if (len(func.argument) == 0): continue # Setup list of function parameters func_param_list = list() for arg in func.argument: func_param_list.append(func.argument[arg]) # Search for scope of current function for scope in data.scopes: if (scope.type == "Function") and (scope.function == func): # Search function body: remove referenced function parameter from list token = scope.bodyStart while (token.next != None and token != scope.bodyEnd and len(func_param_list) > 0): if (token.variable != None and token.variable in func_param_list): func_param_list.remove(token.variable) token = token.next if (len(func_param_list) > 0): # At least one parameter has not been referenced in function body self.reportError(func.tokenDef, 2, 7) def misra_3_1(self, rawTokens): for token in rawTokens: starts_with_double_slash = token.str.startswith('//') if token.str.startswith('/*') or starts_with_double_slash: s = token.str.lstrip('/') if ((not starts_with_double_slash) and '//' in s) or '/*' in s: self.reportError(token, 3, 1) def misra_3_2(self, rawTokens): for token in rawTokens: if token.str.startswith('//'): # Check for comment ends with trigraph which might be replaced # by a backslash. if token.str.endswith('??/'): self.reportError(token, 3, 2) # Check for comment which has been merged with subsequent line # because it ends with backslash. # The last backslash is no more part of the comment token thus # check if next token exists and compare line numbers. elif (token.next is not None) and (token.linenr == token.next.linenr): self.reportError(token, 3, 2) def misra_4_1(self, rawTokens): for token in rawTokens: if (token.str[0] != '"') and (token.str[0] != '\''): continue if len(token.str) < 3: continue delimiter = token.str[0] symbols = token.str[1:-1] # No closing delimiter. This will not compile. if token.str[-1] != delimiter: continue if len(symbols) < 2: continue if not hasNumericEscapeSequence(symbols): continue # String literals that contains one or more escape sequences. All of them should be # terminated. for sequence in ['\\' + t for t in symbols.split('\\')][1:]: if (isHexEscapeSequence(sequence) or isOctalEscapeSequence(sequence) or isSimpleEscapeSequence(sequence)): continue else: self.reportError(token, 4, 1) def misra_4_2(self, rawTokens): for token in rawTokens: if (token.str[0] != '"') or (token.str[-1] != '"'): continue # Check for trigraph sequence as defined by ISO/IEC 9899:1999 for sequence in ['??=', '??(', '??/', '??)', '??\'', '??<', '??!', '??>', '??-']: if sequence in token.str[1:-1]: # First trigraph sequence match, report error and leave loop. self.reportError(token, 4, 2) break def misra_5_1(self, data): long_vars = {} for var in data.variables: if var.nameToken is None: continue if len(var.nameToken.str) <= 31: continue if not hasExternalLinkage(var): continue long_vars.setdefault(var.nameToken.str[:31], []).append(var.nameToken) for name_prefix in long_vars: tokens = long_vars[name_prefix] if len(tokens) < 2: continue for tok in sorted(tokens, key=lambda t: (t.linenr, t.column))[1:]: self.reportError(tok, 5, 1) def misra_5_2(self, data): scopeVars = {} for var in data.variables: if var.nameToken is None: continue if len(var.nameToken.str) <= 31: continue if var.nameToken.scope not in scopeVars: scopeVars.setdefault(var.nameToken.scope, {})["varlist"] = [] scopeVars.setdefault(var.nameToken.scope, {})["scopelist"] = [] scopeVars[var.nameToken.scope]["varlist"].append(var) for scope in data.scopes: if scope.nestedIn and scope.className: if scope.nestedIn not in scopeVars: scopeVars.setdefault(scope.nestedIn, {})["varlist"] = [] scopeVars.setdefault(scope.nestedIn, {})["scopelist"] = [] scopeVars[scope.nestedIn]["scopelist"].append(scope) for scope in scopeVars: if len(scopeVars[scope]["varlist"]) <= 1: continue for i, variable1 in enumerate(scopeVars[scope]["varlist"]): for variable2 in scopeVars[scope]["varlist"][i + 1:]: if variable1.isArgument and variable2.isArgument: continue if hasExternalLinkage(variable1) or hasExternalLinkage(variable2): continue if (variable1.nameToken.str[:31] == variable2.nameToken.str[:31] and variable1.Id != variable2.Id): if int(variable1.nameToken.linenr) > int(variable2.nameToken.linenr): self.reportError(variable1.nameToken, 5, 2) else: self.reportError(variable2.nameToken, 5, 2) for innerscope in scopeVars[scope]["scopelist"]: if variable1.nameToken.str[:31] == innerscope.className[:31]: if int(variable1.nameToken.linenr) > int(innerscope.bodyStart.linenr): self.reportError(variable1.nameToken, 5, 2) else: self.reportError(innerscope.bodyStart, 5, 2) if len(scopeVars[scope]["scopelist"]) <= 1: continue for i, scopename1 in enumerate(scopeVars[scope]["scopelist"]): for scopename2 in scopeVars[scope]["scopelist"][i + 1:]: if scopename1.className[:31] == scopename2.className[:31]: if int(scopename1.bodyStart.linenr) > int(scopename2.bodyStart.linenr): self.reportError(scopename1.bodyStart, 5, 2) else: self.reportError(scopename2.bodyStart, 5, 2) def misra_5_4(self, data): num_sign_chars = self.get_num_significant_naming_chars(data) macro = {} compile_name = re.compile(r'#define ([a-zA-Z0-9_]+)') compile_param = re.compile(r'#define ([a-zA-Z0-9_]+)[(]([a-zA-Z0-9_, ]+)[)]') for dir in data.directives: res1 = compile_name.match(dir.str) if res1: if dir not in macro: macro.setdefault(dir, {})["name"] = [] macro.setdefault(dir, {})["params"] = [] macro[dir]["name"] = res1.group(1) res2 = compile_param.match(dir.str) if res2: res_gp2 = res2.group(2).split(",") res_gp2 = [macroname.replace(" ", "") for macroname in res_gp2] macro[dir]["params"].extend(res_gp2) for mvar in macro: if len(macro[mvar]["params"]) > 0: for i, macroparam1 in enumerate(macro[mvar]["params"]): for j, macroparam2 in enumerate(macro[mvar]["params"]): if j > i and macroparam1[:num_sign_chars] == macroparam2[:num_sign_chars]: self.reportError(mvar, 5, 4) for x, m_var1 in enumerate(macro): for y, m_var2 in enumerate(macro): if x < y and macro[m_var1]["name"] != macro[m_var2]["name"] and \ macro[m_var1]["name"][:num_sign_chars] == macro[m_var2]["name"][:num_sign_chars]: if m_var1.linenr > m_var2.linenr: self.reportError(m_var1, 5, 4) else: self.reportError(m_var2, 5, 4) for param in macro[m_var2]["params"]: if macro[m_var1]["name"][:num_sign_chars] == param[:num_sign_chars]: if m_var1.linenr > m_var2.linenr: self.reportError(m_var1, 5, 4) else: self.reportError(m_var2, 5, 4) def misra_5_5(self, data): num_sign_chars = self.get_num_significant_naming_chars(data) macroNames = [] compiled = re.compile(r'#define ([A-Za-z0-9_]+)') for dir in data.directives: res = compiled.match(dir.str) if res: macroNames.append(res.group(1)) for var in data.variables: for macro in macroNames: if var.nameToken is not None: if var.nameToken.str[:num_sign_chars] == macro[:num_sign_chars]: self.reportError(var.nameToken, 5, 5) for scope in data.scopes: for macro in macroNames: if scope.className and scope.className[:num_sign_chars] == macro[:num_sign_chars]: self.reportError(scope.bodyStart, 5, 5) def misra_7_1(self, rawTokens): compiled = re.compile(r'^0[0-7]+$') for tok in rawTokens: if compiled.match(tok.str): self.reportError(tok, 7, 1) def misra_7_3(self, rawTokens): compiled = re.compile(r'^[0-9.uU]+l') for tok in rawTokens: if compiled.match(tok.str): self.reportError(tok, 7, 3) def misra_8_11(self, data): for var in data.variables: if var.isExtern and simpleMatch(var.nameToken.next, '[ ]') and var.nameToken.scope.type == 'Global': self.reportError(var.nameToken, 8, 11) def misra_8_12(self, data): for scope in data.scopes: if scope.type != 'Enum': continue enum_values = [] implicit_enum_values = [] e_token = scope.bodyStart.next while e_token != scope.bodyEnd: if e_token.str == '(': e_token = e_token.link continue if e_token.previous.str not in ',{': e_token = e_token.next continue if e_token.isName and e_token.values and e_token.valueType and e_token.valueType.typeScope == scope: token_values = [v.intvalue for v in e_token.values] enum_values += token_values if e_token.next.str != "=": implicit_enum_values += token_values e_token = e_token.next for implicit_enum_value in implicit_enum_values: if enum_values.count(implicit_enum_value) != 1: self.reportError(scope.bodyStart, 8, 12) def misra_8_14(self, rawTokens): for token in rawTokens: if token.str == 'restrict': self.reportError(token, 8, 14) def misra_9_5(self, rawTokens): for token in rawTokens: if simpleMatch(token, '[ ] = { ['): self.reportError(token, 9, 5) def misra_10_1(self, data): for token in data.tokenlist: if not token.isOp: continue for t1, t2 in itertools.product( list(getTernaryOperandsRecursive(token.astOperand1) or [token.astOperand1]), list(getTernaryOperandsRecursive(token.astOperand2) or [token.astOperand2]), ): e1 = getEssentialTypeCategory(t1) e2 = getEssentialTypeCategory(t2) if not e1 or not e2: continue if token.str in ('<<', '>>'): if e1 != 'unsigned': self.reportError(token, 10, 1) elif e2 != 'unsigned' and not token.astOperand2.isNumber: self.reportError(token, 10, 1) elif token.str in ('~', '&', '|', '^'): e1_et = getEssentialType(token.astOperand1) e2_et = getEssentialType(token.astOperand2) if e1_et == 'char' and e2_et == 'char': self.reportError(token, 10, 1) def misra_10_4(self, data): op = {'+', '-', '*', '/', '%', '&', '|', '^', '+=', '-=', ':'} for token in data.tokenlist: if token.str not in op and not token.isComparisonOp: continue if not token.astOperand1 or not token.astOperand2: continue if not token.astOperand1.valueType or not token.astOperand2.valueType: continue if ((token.astOperand1.str in op or token.astOperand1.isComparisonOp) and (token.astOperand2.str in op or token.astOperand1.isComparisonOp)): e1, e2 = getEssentialCategorylist(token.astOperand1.astOperand2, token.astOperand2.astOperand1) elif token.astOperand1.str in op or token.astOperand1.isComparisonOp: e1, e2 = getEssentialCategorylist(token.astOperand1.astOperand2, token.astOperand2) elif token.astOperand2.str in op or token.astOperand2.isComparisonOp: e1, e2 = getEssentialCategorylist(token.astOperand1, token.astOperand2.astOperand1) else: e1, e2 = getEssentialCategorylist(token.astOperand1, token.astOperand2) if token.str == "+=" or token.str == "+": if e1 == "char" and (e2 == "signed" or e2 == "unsigned"): continue if e2 == "char" and (e1 == "signed" or e1 == "unsigned"): continue if token.str == "-=" or token.str == "-": if e1 == "char" and (e2 == "signed" or e2 == "unsigned"): continue if e1 and e2 and (e1.find('Anonymous') != -1 and (e2 == "signed" or e2 == "unsigned")): continue if e1 and e2 and (e2.find('Anonymous') != -1 and (e1 == "signed" or e1 == "unsigned")): continue if e1 and e2 and e1 != e2: self.reportError(token, 10, 4) def misra_10_6(self, data): for token in data.tokenlist: if token.str != '=' or not token.astOperand1 or not token.astOperand2: continue if (token.astOperand2.str not in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~') and not isCast(token.astOperand2)): continue vt1 = token.astOperand1.valueType vt2 = token.astOperand2.valueType if not vt1 or vt1.pointer > 0: continue if not vt2 or vt2.pointer > 0: continue try: intTypes = ['char', 'short', 'int', 'long', 'long long'] index1 = intTypes.index(vt1.type) if isCast(token.astOperand2): e = vt2.type else: e = getEssentialType(token.astOperand2) if not e: continue index2 = intTypes.index(e) if index1 > index2: self.reportError(token, 10, 6) except ValueError: pass def misra_10_8(self, data): for token in data.tokenlist: if not isCast(token): continue if not token.valueType or token.valueType.pointer > 0: continue if not token.astOperand1.valueType or token.astOperand1.valueType.pointer > 0: continue if not token.astOperand1.astOperand1: continue if token.astOperand1.str not in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~'): continue if token.astOperand1.str != '~' and not token.astOperand1.astOperand2: continue if token.astOperand1.str == '~': e2 = getEssentialTypeCategory(token.astOperand1.astOperand1) else: e2, e3 = getEssentialCategorylist(token.astOperand1.astOperand1, token.astOperand1.astOperand2) if e2 != e3: continue e1 = getEssentialTypeCategory(token) if e1 != e2: self.reportError(token, 10, 8) else: try: intTypes = ['char', 'short', 'int', 'long', 'long long'] index1 = intTypes.index(token.valueType.type) e = getEssentialType(token.astOperand1) if not e: continue index2 = intTypes.index(e) if index1 > index2: self.reportError(token, 10, 8) except ValueError: pass def misra_11_3(self, data): for token in data.tokenlist: if not isCast(token): continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if vt1.type == 'void' or vt2.type == 'void': continue if (vt1.pointer > 0 and vt1.type == 'record' and vt2.pointer > 0 and vt2.type == 'record' and vt1.typeScopeId != vt2.typeScopeId): self.reportError(token, 11, 3) elif (vt1.pointer == vt2.pointer and vt1.pointer > 0 and vt1.type != vt2.type and vt1.type != 'char'): self.reportError(token, 11, 3) def misra_11_4(self, data): for token in data.tokenlist: if not isCast(token): continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if vt2.pointer > 0 and vt1.pointer == 0 and (vt1.isIntegral() or vt1.isEnum()) and vt2.type != 'void': self.reportError(token, 11, 4) elif vt1.pointer > 0 and vt2.pointer == 0 and (vt2.isIntegral() or vt2.isEnum())and vt1.type != 'void': self.reportError(token, 11, 4) def misra_11_5(self, data): for token in data.tokenlist: if not isCast(token): if token.astOperand1 and token.astOperand2 and token.str == "=" and token.next.str != "(": vt1 = token.astOperand1.valueType vt2 = token.astOperand2.valueType if not vt1 or not vt2: continue if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void': self.reportError(token, 11, 5) continue if token.astOperand1.astOperand1 and token.astOperand1.astOperand1.str in ('malloc', 'calloc', 'realloc', 'free'): continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void': self.reportError(token, 11, 5) def misra_11_6(self, data): for token in data.tokenlist: if not isCast(token): continue if token.astOperand1.astOperand1: continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if vt1.pointer == 1 and vt1.type == 'void' and vt2.pointer == 0 and token.astOperand1.str != "0": self.reportError(token, 11, 6) elif vt1.pointer == 0 and vt1.type != 'void' and vt2.pointer == 1 and vt2.type == 'void': self.reportError(token, 11, 6) def misra_11_7(self, data): for token in data.tokenlist: if not isCast(token): continue vt1 = token.valueType vt2 = token.astOperand1.valueType if not vt1 or not vt2: continue if token.astOperand1.astOperand1: continue if (vt2.pointer > 0 and vt1.pointer == 0 and not vt1.isIntegral() and not vt1.isEnum() and vt1.type != 'void'): self.reportError(token, 11, 7) elif (vt1.pointer > 0 and vt2.pointer == 0 and not vt2.isIntegral() and not vt2.isEnum() and vt1.type != 'void'): self.reportError(token, 11, 7) def misra_11_8(self, data): # TODO: reuse code in CERT-EXP05 for token in data.tokenlist: if isCast(token): # C-style cast if not token.valueType: continue if not token.astOperand1.valueType: continue if token.valueType.pointer == 0: continue if token.astOperand1.valueType.pointer == 0: continue const1 = token.valueType.constness const2 = token.astOperand1.valueType.constness if (const1 % 2) < (const2 % 2): self.reportError(token, 11, 8) elif token.str == '(' and token.astOperand1 and token.astOperand2 and token.astOperand1.function: # Function call function = token.astOperand1.function arguments = getArguments(token) for argnr, argvar in function.argument.items(): if argnr < 1 or argnr > len(arguments): continue if not argvar.isPointer: continue argtok = arguments[argnr - 1] if not argtok.valueType: continue if argtok.valueType.pointer == 0: continue const1 = argvar.constness const2 = arguments[argnr - 1].valueType.constness if (const1 % 2) < (const2 % 2): self.reportError(token, 11, 8) def misra_11_9(self, data): for token in data.tokenlist: if token.astOperand1 and token.astOperand2 and token.str in ["=", "==", "!=", "?", ":"]: vt1 = token.astOperand1.valueType vt2 = token.astOperand2.valueType if not vt1 or not vt2: continue if vt1.pointer > 0 and vt2.pointer == 0 and token.astOperand2.str == "NULL": continue if (token.astOperand2.values and vt1.pointer > 0 and vt2.pointer == 0 and token.astOperand2.values): for val in token.astOperand2.values: if val.intvalue == 0: self.reportError(token, 11, 9) def misra_12_1_sizeof(self, rawTokens): state = 0 compiled = re.compile(r'^[a-zA-Z_]') for tok in rawTokens: if tok.str.startswith('//') or tok.str.startswith('/*'): continue if tok.str == 'sizeof': state = 1 elif state == 1: if compiled.match(tok.str): state = 2 else: state = 0 elif state == 2: if tok.str in ('+', '-', '*', '/', '%'): self.reportError(tok, 12, 1) else: state = 0 def misra_12_1(self, data): for token in data.tokenlist: p = getPrecedence(token) if p < 2 or p > 12: continue p1 = getPrecedence(token.astOperand1) if p < p1 <= 12 and numberOfParentheses(token.astOperand1, token): self.reportError(token, 12, 1) continue p2 = getPrecedence(token.astOperand2) if p < p2 <= 12 and numberOfParentheses(token, token.astOperand2): self.reportError(token, 12, 1) continue def misra_12_2(self, data): for token in data.tokenlist: if not (token.str in ('<<', '>>')): continue if (not token.astOperand2) or (not token.astOperand2.values): continue maxval = 0 for val in token.astOperand2.values: if val.intvalue and val.intvalue > maxval: maxval = val.intvalue if maxval == 0: continue sz = bitsOfEssentialType(token.astOperand1) if sz <= 0: continue 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 = {} skip_to = None for token in data.tokenlist: # Skip tokens in function call body if skip_to: if token == skip_to: skip_to = None else: continue if token.function and token.next and token.next.str == "(": skip_to = token.next.link if token.scope.type in ('Enum', 'Class', 'Struct', 'Global'): continue # Save end tokens from function calls in initialization if simpleMatch(token, ') ;'): if (token.isExpandedMacro): end_tokens_map.setdefault(token.next.linenr, set()) end_tokens_map[token.linenr].add(token.next.column) else: end_tokens_map.setdefault(token.linenr, set()) 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.setdefault(end_token.linenr, set()) 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: break cur_line = min(maybe_map) if state == STATE_CHECK and tok.str == ',': self.reportError(tok, 12, 3) def misra_12_4(self, data): if typeBits['INT'] == 16: max_uint = 0xffff elif typeBits['INT'] == 32: max_uint = 0xffffffff else: return for token in data.tokenlist: if not token.values: continue if (not isConstantExpression(token)) or (not isUnsignedInt(token)): continue for value in token.values: if value.intvalue < 0 or value.intvalue > max_uint: self.reportError(token, 12, 4) break def misra_13_1(self, data): for token in data.tokenlist: if not simpleMatch(token, '= {'): continue init = token.next end = init.link if not end: continue # syntax is broken tn = init while tn and tn != end: if tn.str == '[' and tn.link: tn = tn.link if tn and tn.next and tn.next.str == '=': tn = tn.next.next continue else: break if tn.str == '.' and tn.next and tn.next.isName: tn = tn.next if tn.next and tn.next.str == '=': tn = tn.next.next continue if tn.str in {'++', '--'} or tn.isAssignmentOp: self.reportError(init, 13, 1) tn = tn.next def misra_13_3(self, data): for token in data.tokenlist: if token.str not in ('++', '--'): continue astTop = token while astTop.astParent and astTop.astParent.str not in (',', ';'): astTop = astTop.astParent if countSideEffects(astTop) >= 2: self.reportError(astTop, 13, 3) def misra_13_4(self, data): for token in data.tokenlist: if token.str != '=': continue if not token.astParent: continue if token.astOperand1.str == '[' and token.astOperand1.previous.str in ('{', ','): continue if not (token.astParent.str in [',', ';', '{']): self.reportError(token, 13, 4) def misra_13_5(self, data): for token in data.tokenlist: if token.isLogicalOp and hasSideEffectsRecursive(token.astOperand2): self.reportError(token, 13, 5) def misra_13_6(self, data): for token in data.tokenlist: if token.str == 'sizeof' and hasSideEffectsRecursive(token.next): self.reportError(token, 13, 6) def misra_14_1(self, data): for token in data.tokenlist: if token.str == 'for': exprs = getForLoopExpressions(token) if not exprs: continue for counter in findCounterTokens(exprs[1]): if counter.valueType and counter.valueType.isFloat(): self.reportError(token, 14, 1) elif token.str == 'while': if isFloatCounterInWhileLoop(token): self.reportError(token, 14, 1) def misra_14_2(self, data): for token in data.tokenlist: expressions = getForLoopExpressions(token) if not expressions: continue if expressions[0] and not expressions[0].isAssignmentOp: self.reportError(token, 14, 2) elif hasSideEffectsRecursive(expressions[1]): self.reportError(token, 14, 2) # Inspect modification of loop counter in loop body counter_vars = getForLoopCounterVariables(token) outer_scope = token.scope body_scope = None tn = token.next while tn and tn.next != outer_scope.bodyEnd: if tn.scope and tn.scope.nestedIn == outer_scope: body_scope = tn.scope break tn = tn.next if not body_scope: continue tn = body_scope.bodyStart while tn and tn != body_scope.bodyEnd: if tn.variable and tn.variable in counter_vars: if tn.next: # TODO: Check modifications in function calls if hasSideEffectsRecursive(tn.next): self.reportError(tn, 14, 2) tn = tn.next def misra_14_4(self, data): for token in data.tokenlist: if token.str != '(': continue if not token.astOperand1 or not (token.astOperand1.str in ['if', 'while']): continue if not isBoolExpression(token.astOperand2): self.reportError(token, 14, 4) def misra_15_1(self, data): for token in data.tokenlist: if token.str == "goto": self.reportError(token, 15, 1) def misra_15_2(self, data): for token in data.tokenlist: if token.str != 'goto': continue if (not token.next) or (not token.next.isName): continue if not findGotoLabel(token): self.reportError(token, 15, 2) def misra_15_3(self, data): for token in data.tokenlist: if token.str != 'goto': continue if (not token.next) or (not token.next.isName): continue tok = findGotoLabel(token) if not tok: continue scope = token.scope while scope and scope != tok.scope: scope = scope.nestedIn if not scope: self.reportError(token, 15, 3) def misra_15_5(self, data): for token in data.tokenlist: if token.str == 'return' and token.scope.type != 'Function': self.reportError(token, 15, 5) def misra_15_6(self, rawTokens): state = 0 indent = 0 tok1 = None for token in rawTokens: if token.str in ['if', 'for', 'while']: if simpleMatch(token.previous, '# if'): continue if simpleMatch(token.previous, "} while"): # is there a 'do { .. } while'? start = rawlink(token.previous) if start and simpleMatch(start.previous, 'do {'): continue if state == 2: self.reportError(tok1, 15, 6) state = 1 indent = 0 tok1 = token elif token.str == 'else': if simpleMatch(token.previous, '# else'): continue if simpleMatch(token, 'else if'): continue if state == 2: self.reportError(tok1, 15, 6) state = 2 indent = 0 tok1 = token elif state == 1: if indent == 0 and token.str != '(': state = 0 continue if token.str == '(': indent = indent + 1 elif token.str == ')': if indent == 0: state = 0 elif indent == 1: state = 2 indent = indent - 1 elif state == 2: if token.str.startswith('//') or token.str.startswith('/*'): continue state = 0 if token.str != '{': self.reportError(tok1, 15, 6) def misra_15_7(self, data): for scope in data.scopes: if scope.type != 'Else': continue if not simpleMatch(scope.bodyStart, '{ if ('): continue if scope.bodyStart.column > 0: continue tok = scope.bodyStart.next.next.link if not simpleMatch(tok, ') {'): continue tok = tok.next.link if not simpleMatch(tok, '} else'): self.reportError(tok, 15, 7) # TODO add 16.1 rule def misra_16_2(self, data): for token in data.tokenlist: if token.str == 'case' and token.scope.type != 'Switch': self.reportError(token, 16, 2) def misra_16_3(self, rawTokens): STATE_NONE = 0 # default state, not in switch case/default block STATE_BREAK = 1 # break/comment is seen but not its ';' STATE_OK = 2 # a case/default is allowed (we have seen 'break;'/'comment'/'{'/attribute) STATE_SWITCH = 3 # walking through switch statement scope state = STATE_NONE end_swtich_token = None # end '}' for the switch scope for token in rawTokens: # Find switch scope borders if token.str == 'switch': state = STATE_SWITCH if state == STATE_SWITCH: if token.str == '{': end_swtich_token = findRawLink(token) else: continue if token.str == 'break' or token.str == 'return' or token.str == 'throw': state = STATE_BREAK elif token.str == ';': if state == STATE_BREAK: state = STATE_OK elif token.next and token.next == end_swtich_token: self.reportError(token.next, 16, 3) else: state = STATE_NONE elif token.str.startswith('/*') or token.str.startswith('//'): if 'fallthrough' in token.str.lower(): state = STATE_OK elif simpleMatch(token, '[ [ fallthrough ] ] ;'): state = STATE_BREAK elif token.str == '{': state = STATE_OK elif token.str == '}' and state == STATE_OK: # is this {} an unconditional block of code? prev = findRawLink(token) if prev: prev = prev.previous while prev and prev.str[:2] in ('//', '/*'): prev = prev.previous if (prev is None) or (prev.str not in ':;{}'): state = STATE_NONE elif token.str == 'case' or token.str == 'default': if state != STATE_OK: self.reportError(token, 16, 3) state = STATE_OK def misra_16_4(self, data): for token in data.tokenlist: if token.str != 'switch': continue if not simpleMatch(token, 'switch ('): continue if not simpleMatch(token.next.link, ') {'): continue startTok = token.next.link.next tok = startTok.next while tok and tok.str != '}': if tok.str == '{': tok = tok.link elif tok.str == 'default': break tok = tok.next if tok and tok.str != 'default': self.reportError(token, 16, 4) def misra_16_5(self, data): for token in data.tokenlist: if token.str != 'default': continue if token.previous and token.previous.str == '{': continue tok2 = token while tok2: if tok2.str in ('}', 'case'): break if tok2.str == '{': tok2 = tok2.link tok2 = tok2.next if tok2 and tok2.str == 'case': self.reportError(token, 16, 5) def misra_16_6(self, data): for token in data.tokenlist: if not (simpleMatch(token, 'switch (') and simpleMatch(token.next.link, ') {')): continue tok = token.next.link.next.next count = 0 while tok: if tok.str in ['break', 'return', 'throw']: count = count + 1 elif tok.str == '{': tok = tok.link if isNoReturnScope(tok): count = count + 1 elif tok.str == '}': break tok = tok.next if count < 2: self.reportError(token, 16, 6) def misra_16_7(self, data): for token in data.tokenlist: if simpleMatch(token, 'switch (') and isBoolExpression(token.next.astOperand2): self.reportError(token, 16, 7) def misra_17_1(self, data): for token in data.tokenlist: if isFunctionCall(token) and token.astOperand1.str in ('va_list', 'va_arg', 'va_start', 'va_end', 'va_copy'): self.reportError(token, 17, 1) elif token.str == 'va_list': self.reportError(token, 17, 1) def misra_17_2(self, data): # find recursions.. def find_recursive_call(search_for_function, direct_call, calls_map, visited=None): if visited is None: visited = set() if direct_call == search_for_function: return True for indirect_call in calls_map.get(direct_call, []): if indirect_call == search_for_function: return True if indirect_call in visited: # This has already been handled continue visited.add(indirect_call) if find_recursive_call(search_for_function, indirect_call, calls_map, visited): return True return False # List functions called in each function function_calls = {} for scope in data.scopes: if scope.type != 'Function': continue calls = [] tok = scope.bodyStart while tok != scope.bodyEnd: tok = tok.next if not isFunctionCall(tok): continue f = tok.astOperand1.function if f is not None and f not in calls: calls.append(f) function_calls[scope.function] = calls # Report warnings for all recursions.. for func in function_calls: for call in function_calls[func]: if not find_recursive_call(func, call, function_calls): # Function call is not recursive continue # Warn about all functions calls.. for scope in data.scopes: if scope.type != 'Function' or scope.function != func: continue tok = scope.bodyStart while tok != scope.bodyEnd: if tok.function and tok.function == call: self.reportError(tok, 17, 2) tok = tok.next def misra_17_6(self, rawTokens): for token in rawTokens: if simpleMatch(token, '[ static'): self.reportError(token, 17, 6) def misra_17_7(self, data): for token in data.tokenlist: if not token.scope.isExecutable: continue if token.str != '(' or token.astParent: continue if not token.previous.isName or token.previous.varId: continue if token.valueType is None: continue if token.valueType.type == 'void' and token.valueType.pointer == 0: continue self.reportError(token, 17, 7) def misra_17_8(self, data): for token in data.tokenlist: if not (token.isAssignmentOp or (token.str in ('++', '--'))): continue if not token.astOperand1: continue var = token.astOperand1.variable if var and var.isArgument: self.reportError(token, 17, 8) def misra_18_4(self, data): for token in data.tokenlist: if token.str not in ('+', '-', '+=', '-='): continue if token.astOperand1 is None or token.astOperand2 is None: continue vt1 = token.astOperand1.valueType vt2 = token.astOperand2.valueType if vt1 and vt1.pointer > 0: self.reportError(token, 18, 4) elif vt2 and vt2.pointer > 0: self.reportError(token, 18, 4) def misra_18_5(self, data): for var in data.variables: if not var.isPointer: continue typetok = var.nameToken count = 0 while typetok: if typetok.str == '*': count = count + 1 elif not typetok.isName: break typetok = typetok.previous if count > 2: self.reportError(var.nameToken, 18, 5) def misra_18_7(self, data): for scope in data.scopes: if scope.type != 'Struct': continue token = scope.bodyStart.next while token != scope.bodyEnd and token is not None: # Handle nested structures to not duplicate an error. if token.str == '{': token = token.link if cppcheckdata.simpleMatch(token, "[ ]"): self.reportError(token, 18, 7) break token = token.next def misra_18_8(self, data): for var in data.variables: if not var.isArray or not var.isLocal: continue # TODO Array dimensions are not available in dump, must look in tokens typetok = var.nameToken.next if not typetok or typetok.str != '[': continue # Unknown define or syntax error if not typetok.astOperand2: continue if not isConstantExpression(typetok.astOperand2): self.reportError(var.nameToken, 18, 8) def misra_19_2(self, data): for token in data.tokenlist: if token.str == 'union': self.reportError(token, 19, 2) def misra_20_1(self, data): for directive in data.directives: if not directive.str.startswith('#include'): continue for token in data.tokenlist: if token.file != directive.file: continue if int(token.linenr) < int(directive.linenr): self.reportError(directive, 20, 1) break def misra_20_2(self, data): for directive in data.directives: if not directive.str.startswith('#include '): continue for pattern in ('\\', '//', '/*', "'"): if pattern in directive.str: self.reportError(directive, 20, 2) break def misra_20_3(self, rawTokens): linenr = -1 for token in rawTokens: if token.str.startswith('/') or token.linenr == linenr: continue linenr = token.linenr if not simpleMatch(token, '# include'): continue headerToken = token.next.next num = 0 while headerToken and headerToken.linenr == linenr: if not headerToken.str.startswith('/*') and not headerToken.str.startswith('//'): num += 1 headerToken = headerToken.next if num != 1: self.reportError(token, 20, 3) def misra_20_4(self, data): for directive in data.directives: res = re.search(r'#define ([a-z][a-z0-9_]+)', directive.str) if res and (res.group(1) in KEYWORDS): self.reportError(directive, 20, 4) def misra_20_5(self, data): for directive in data.directives: if directive.str.startswith('#undef '): self.reportError(directive, 20, 5) def misra_20_7(self, data): for directive in data.directives: d = Define(directive) exp = '(' + d.expansionList + ')' for arg in d.args: pos = 0 while pos < len(exp): pos = exp.find(arg, pos) if pos < 0: break # is 'arg' used at position pos pos1 = pos - 1 pos2 = pos + len(arg) pos = pos2 if pos1 >= 0 and (isalnum(exp[pos1]) or exp[pos1] == '_'): continue if pos2 < len(exp) and (isalnum(exp[pos2]) or exp[pos2] == '_'): continue while pos1 >= 0 and exp[pos1] == ' ': pos1 -= 1 if exp[pos1] not in '([#,': self.reportError(directive, 20, 7) break while pos2 < len(exp) and exp[pos2] == ' ': pos2 += 1 if pos2 < len(exp) and exp[pos2] not in ')]#,': self.reportError(directive, 20, 7) break def misra_20_10(self, data): for directive in data.directives: d = Define(directive) if d.expansionList.find('#') >= 0: self.reportError(directive, 20, 10) def misra_20_13(self, data): dir_pattern = re.compile(r'#[ ]*([^ (<]*)') for directive in data.directives: dir = directive.str mo = dir_pattern.match(dir) if mo: dir = mo.group(1) if dir not in ['define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', 'include', 'pragma', 'undef', 'warning']: self.reportError(directive, 20, 13) def misra_20_14(self, data): # stack for #if blocks. contains the #if directive until the corresponding #endif is seen. # the size increases when there are inner #if directives. ifStack = [] for directive in data.directives: if directive.str.startswith('#if ') or directive.str.startswith('#ifdef ') or directive.str.startswith('#ifndef '): ifStack.append(directive) elif directive.str == '#else' or directive.str.startswith('#elif '): if len(ifStack) == 0: self.reportError(directive, 20, 14) ifStack.append(directive) elif directive.file != ifStack[-1].file: self.reportError(directive, 20, 14) elif directive.str == '#endif': if len(ifStack) == 0: self.reportError(directive, 20, 14) elif directive.file != ifStack[-1].file: self.reportError(directive, 20, 14) ifStack.pop() def misra_21_1(self, data): # Reference: n1570 7.1.3 - Reserved identifiers re_forbidden_macro = re.compile(r'#define (errno|_[_A-Z]+)') # Search for forbidden identifiers in macro names for directive in data.directives: res = re.search(re_forbidden_macro, directive.str) if res: self.reportError(directive, 21, 1) type_name_tokens = (t for t in data.tokenlist if t.typeScopeId) type_fields_tokens = (t for t in data.tokenlist if t.valueType and t.valueType.typeScopeId) # Search for forbidden identifiers for i in itertools.chain(data.variables, data.functions, type_name_tokens, type_fields_tokens): token = i if isinstance(i, cppcheckdata.Variable): token = i.nameToken elif isinstance(i, cppcheckdata.Function): token = i.tokenDef if not token: continue if len(token.str) < 2: continue if token.str == 'errno': self.reportError(token, 21, 1) if token.str[0] == '_': if (token.str[1] in string.ascii_uppercase) or (token.str[1] == '_'): self.reportError(token, 21, 1) # Allow identifiers with file scope visibility (static) if token.scope.type == 'Global': if token.variable and token.variable.isStatic: continue if token.function and token.function.isStatic: continue self.reportError(token, 21, 1) def misra_21_3(self, data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in ('malloc', 'calloc', 'realloc', 'free')): self.reportError(token, 21, 3) def misra_21_4(self, data): directive = findInclude(data.directives, '') if directive: self.reportError(directive, 21, 4) def misra_21_5(self, data): directive = findInclude(data.directives, '') if directive: self.reportError(directive, 21, 5) def misra_21_6(self, data): dir_stdio = findInclude(data.directives, '') dir_wchar = findInclude(data.directives, '') if dir_stdio: self.reportError(dir_stdio, 21, 6) if dir_wchar: self.reportError(dir_wchar, 21, 6) def misra_21_7(self, data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in ('atof', 'atoi', 'atol', 'atoll')): self.reportError(token, 21, 7) def misra_21_8(self, data): for token in data.tokenlist: if isFunctionCall(token) and (token.astOperand1.str in ('abort', 'exit', 'getenv', 'system')): self.reportError(token, 21, 8) def misra_21_9(self, data): for token in data.tokenlist: if (token.str in ('bsearch', 'qsort')) and token.next and token.next.str == '(': self.reportError(token, 21, 9) def misra_21_10(self, data): directive = findInclude(data.directives, '') if directive: self.reportError(directive, 21, 10) for token in data.tokenlist: if (token.str == 'wcsftime') and token.next and token.next.str == '(': self.reportError(token, 21, 10) def misra_21_11(self, data): directive = findInclude(data.directives, '') if directive: self.reportError(directive, 21, 11) def misra_21_12(self, data): if findInclude(data.directives, ''): for token in data.tokenlist: if token.str == 'fexcept_t' and token.isName: self.reportError(token, 21, 12) if isFunctionCall(token) and (token.astOperand1.str in ( 'feclearexcept', 'fegetexceptflag', 'feraiseexcept', 'fesetexceptflag', 'fetestexcept')): self.reportError(token, 21, 12) def get_verify_expected(self): """Return the list of expected violations in the verify test""" return self.verify_expected def get_verify_actual(self): """Return the list of actual violations in for the verify test""" return self.verify_actual def get_violations(self, violation_type=None): """Return the list of violations for a normal checker run""" if violation_type is None: return self.violations.items() else: return self.violations[violation_type] def get_violation_types(self): """Return the list of violations for a normal checker run""" return self.violations.keys() def addSuppressedRule(self, ruleNum, fileName = None, lineNumber = None, symbolName = None): """ Add a suppression to the suppressions data structure Suppressions are stored in a dictionary of dictionaries that contains a list of tuples. The first dictionary is keyed by the MISRA rule in hundreds format. The value of that dictionary is a dictionary of filenames. If the value is None then the rule is assumed to be suppressed for all files. If the filename exists then the value of that dictionary contains a list with the scope of the suppression. If the list contains an item of None then the rule is assumed to be suppressed for the entire file. Otherwise the list contains line number, symbol name tuples. For each tuple either line number or symbol name can can be none. """ normalized_filename = None if fileName is not None: normalized_filename = os.path.expanduser(fileName) normalized_filename = os.path.normpath(normalized_filename) if lineNumber is not None or symbolName is not None: line_symbol = (lineNumber, symbolName) else: line_symbol = None # If the rule is not in the dict already then add it if ruleNum not in self.suppressedRules: ruleItemList = list() ruleItemList.append(line_symbol) fileDict = dict() fileDict[normalized_filename] = ruleItemList self.suppressedRules[ruleNum] = fileDict # Rule is added. Done. return # Rule existed in the dictionary. Check for # filename entries. # Get the dictionary for the rule number fileDict = self.suppressedRules[ruleNum] # If the filename is not in the dict already add it if normalized_filename not in fileDict: ruleItemList = list() ruleItemList.append(line_symbol) fileDict[normalized_filename] = ruleItemList # Rule is added with a file scope. Done return # Rule has a matching filename. Get the rule item list. # Check the lists of rule items # to see if this (lineNumber, symbolName) combination # or None already exists. ruleItemList = fileDict[normalized_filename] if line_symbol is None: # is it already in the list? if line_symbol not in ruleItemList: ruleItemList.append(line_symbol) else: # Check the list looking for matches matched = False for each in ruleItemList: if each is not None: if (each[0] == line_symbol[0]) and (each[1] == line_symbol[1]): matched = True # Append the rule item if it was not already found if not matched: ruleItemList.append(line_symbol) def isRuleSuppressed(self, file_path, linenr, ruleNum): """ Check to see if a rule is suppressed. :param ruleNum: is the rule number in hundreds format :param file_path: File path of checked location :param linenr: Line number of checked location If the rule exists in the dict then check for a filename If the filename is None then rule is suppressed globally for all files. If the filename exists then look for list of line number, symbol name tuples. If the list is None then the rule is suppressed for the entire file If the list of tuples exists then search the list looking for matching line numbers. Symbol names are currently ignored because they can include regular expressions. TODO: Support symbol names and expression matching. """ ruleIsSuppressed = False # Remove any prefix listed in command arguments from the filename. filename = None if file_path is not None: if self.filePrefix is not None: filename = remove_file_prefix(file_path, self.filePrefix) else: filename = os.path.basename(file_path) if ruleNum in self.suppressedRules: fileDict = self.suppressedRules[ruleNum] # a file name entry of None means that the rule is suppressed # globally if None in fileDict: ruleIsSuppressed = True else: # Does the filename match one of the names in # the file list if filename in fileDict: # Get the list of ruleItems ruleItemList = fileDict[filename] if None in ruleItemList: # Entry of None in the ruleItemList means the rule is # suppressed for all lines in the filename ruleIsSuppressed = True else: # Iterate though the the list of line numbers # and symbols looking for a match of the line # number. Matching the symbol is a TODO: for each in ruleItemList: if each is not None: if each[0] == linenr: ruleIsSuppressed = True return ruleIsSuppressed def isRuleGloballySuppressed(self, rule_num): """ Check to see if a rule is globally suppressed. :param rule_num: is the rule number in hundreds format """ if rule_num not in self.suppressedRules: return False return None in self.suppressedRules[rule_num] def parseSuppressions(self): """ Parse the suppression list provided by cppcheck looking for rules that start with 'misra' or MISRA. The MISRA rule number follows using either '_' or '.' to separate the numbers. Examples: misra_6.0 misra_7_0 misra.21.11 """ rule_pattern = re.compile(r'^(misra|MISRA)[_.]([0-9]+)[_.]([0-9]+)') for each in self.dumpfileSuppressions: res = rule_pattern.match(each.errorId) if res: num1 = int(res.group(2)) * 100 ruleNum = num1 + int(res.group(3)) linenr = None if each.lineNumber: linenr = int(each.lineNumber) self.addSuppressedRule(ruleNum, each.fileName, linenr, each.symbolName) def showSuppressedRules(self): """ Print out rules in suppression list sorted by Rule Number """ print("Suppressed Rules List:") outlist = list() for ruleNum in self.suppressedRules: fileDict = self.suppressedRules[ruleNum] for fname in fileDict: ruleItemList = fileDict[fname] for item in ruleItemList: if item is None: item_str = "None" else: item_str = str(item[0]) outlist.append("%s: %s: %s (%d locations suppressed)" % (float(ruleNum)/100, fname, item_str, self.suppressionStats.get(ruleNum, 0))) for line in sorted(outlist, reverse=True): print(" %s" % line) def setFilePrefix(self, prefix): """ Set the file prefix to ignore from files when matching suppression files """ self.filePrefix = prefix def setSuppressionList(self, suppressionlist): num1 = 0 num2 = 0 rule_pattern = re.compile(r'([0-9]+).([0-9]+)') strlist = suppressionlist.split(",") # build ignore list for item in strlist: res = rule_pattern.match(item) if res: num1 = int(res.group(1)) num2 = int(res.group(2)) ruleNum = (num1*100)+num2 self.addSuppressedRule(ruleNum) def reportError(self, location, num1, num2): ruleNum = num1 * 100 + num2 if self.settings.verify: self.verify_actual.append(str(location.linenr) + ':' + str(num1) + '.' + str(num2)) elif self.isRuleSuppressed(location.file, location.linenr, ruleNum): # Error is suppressed. Ignore self.suppressionStats.setdefault(ruleNum, 0) self.suppressionStats[ruleNum] += 1 return else: errorId = 'c2012-' + str(num1) + '.' + str(num2) misra_severity = 'Undefined' cppcheck_severity = 'style' if ruleNum in self.ruleTexts: errmsg = self.ruleTexts[ruleNum].text if self.ruleTexts[ruleNum].misra_severity: misra_severity = self.ruleTexts[ruleNum].misra_severity cppcheck_severity = self.ruleTexts[ruleNum].cppcheck_severity elif len(self.ruleTexts) == 0: errmsg = 'misra violation (use --rule-texts= to get proper output)' else: return cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId, misra_severity) if misra_severity not in self.violations: self.violations[misra_severity] = [] self.violations[misra_severity].append('misra-' + errorId) def loadRuleTexts(self, filename): num1 = 0 num2 = 0 appendixA = False ruleText = False expect_more = False Rule_pattern = re.compile(r'^Rule ([0-9]+).([0-9]+)') severity_pattern = re.compile(r'.*[ ]*(Advisory|Required|Mandatory)$') xA_Z_pattern = re.compile(r'^[#A-Z].*') a_z_pattern = re.compile(r'^[a-z].*') # Try to detect the file encoding file_stream = None encodings = ['ascii', 'utf-8', 'windows-1250', 'windows-1252'] for e in encodings: try: file_stream = codecs.open(filename, 'r', encoding=e) file_stream.readlines() file_stream.seek(0) except UnicodeDecodeError: file_stream = None else: break if not file_stream: print('Could not find a suitable codec for "' + filename + '".') print('If you know the codec please report it to the developers so the list can be enhanced.') print('Trying with default codec now and ignoring errors if possible ...') try: file_stream = open(filename, 'rt', errors='ignore') except TypeError: # Python 2 does not support the errors parameter file_stream = open(filename, 'rt') rule = None have_severity = False severity_loc = 0 for line in file_stream: line = line.replace('\r', '').replace('\n', '') if not appendixA: if line.find('Appendix A') >= 0 and line.find('Summary of guidelines') >= 10: appendixA = True continue if line.find('Appendix B') >= 0: break if len(line) == 0: continue # Parse rule declaration. res = Rule_pattern.match(line) if res: have_severity = False expect_more = False severity_loc = 0 num1 = int(res.group(1)) num2 = int(res.group(2)) rule = Rule(num1, num2) if not have_severity and rule is not None: res = severity_pattern.match(line) if res: rule.misra_severity = res.group(1) have_severity = True else: severity_loc += 1 # Only look for severity on the Rule line # or the next non-blank line after # If it's not in either of those locations then # assume a severity was not provided. if severity_loc < 2: continue else: rule.misra_severity = '' have_severity = True if rule is None: continue # Parse continuing of rule text. if expect_more: if a_z_pattern.match(line): self.ruleTexts[rule.num].text += ' ' + line continue expect_more = False continue # Parse beginning of rule text. if xA_Z_pattern.match(line): rule.text = line self.ruleTexts[rule.num] = rule expect_more = True def verifyRuleTexts(self): """Prints rule numbers without rule text.""" rule_texts_rules = [] for rule_num in self.ruleTexts: rule = self.ruleTexts[rule_num] rule_texts_rules.append(str(rule.num1) + '.' + str(rule.num2)) all_rules = list(getAddonRules() + getCppcheckRules()) missing_rules = list(set(all_rules) - set(rule_texts_rules)) if len(missing_rules) == 0: print("Rule texts are correct.") else: print("Missing rule texts: " + ', '.join(missing_rules)) def printStatus(self, *args, **kwargs): if not self.settings.quiet: print(*args, **kwargs) def executeCheck(self, rule_num, check_function, *args): """Execute check function for a single MISRA rule. :param rule_num: Number of rule in hundreds format :param check_function: Check function to execute :param args: Check function arguments """ if not self.isRuleGloballySuppressed(rule_num): check_function(*args) def parseDump(self, dumpfile): filename = '.'.join(dumpfile.split('.')[:-1]) data = cppcheckdata.parsedump(dumpfile) self.dumpfileSuppressions = data.suppressions self.parseSuppressions() typeBits['CHAR'] = data.platform.char_bit typeBits['SHORT'] = data.platform.short_bit typeBits['INT'] = data.platform.int_bit typeBits['LONG'] = data.platform.long_bit typeBits['LONG_LONG'] = data.platform.long_long_bit typeBits['POINTER'] = data.platform.pointer_bit if self.settings.verify: for tok in data.rawTokens: if tok.str.startswith('//') and 'TODO' not in tok.str: compiled = re.compile(r'[0-9]+\.[0-9]+') for word in tok.str[2:].split(' '): if compiled.match(word): self.verify_expected.append(str(tok.linenr) + ':' + word) else: self.printStatus('Checking ' + dumpfile + '...') cfgNumber = 0 for cfg in data.configurations: cfgNumber = cfgNumber + 1 if len(data.configurations) > 1: self.printStatus('Checking ' + dumpfile + ', config "' + cfg.name + '"...') self.executeCheck(207, self.misra_2_7, cfg) if cfgNumber == 1: self.executeCheck(301, self.misra_3_1, data.rawTokens) self.executeCheck(302, self.misra_3_2, data.rawTokens) self.executeCheck(401, self.misra_4_1, data.rawTokens) self.executeCheck(402, self.misra_4_2, data.rawTokens) self.executeCheck(501, self.misra_5_1, cfg) self.executeCheck(502, self.misra_5_2, cfg) self.executeCheck(504, self.misra_5_4, cfg) self.executeCheck(505, self.misra_5_5, cfg) # 6.1 require updates in Cppcheck (type info for bitfields are lost) # 6.2 require updates in Cppcheck (type info for bitfields are lost) if cfgNumber == 1: self.executeCheck(701, self.misra_7_1, data.rawTokens) self.executeCheck(703, self.misra_7_3, data.rawTokens) self.executeCheck(811, self.misra_8_11, cfg) self.executeCheck(812, self.misra_8_12, cfg) if cfgNumber == 1: self.executeCheck(814, self.misra_8_14, data.rawTokens) self.executeCheck(905, self.misra_9_5, data.rawTokens) self.executeCheck(1001, self.misra_10_1, cfg) self.executeCheck(1004, self.misra_10_4, cfg) self.executeCheck(1006, self.misra_10_6, cfg) self.executeCheck(1008, self.misra_10_8, cfg) self.executeCheck(1103, self.misra_11_3, cfg) self.executeCheck(1104, self.misra_11_4, cfg) self.executeCheck(1105, self.misra_11_5, cfg) self.executeCheck(1106, self.misra_11_6, cfg) self.executeCheck(1107, self.misra_11_7, cfg) self.executeCheck(1108, self.misra_11_8, cfg) self.executeCheck(1109, self.misra_11_9, cfg) if cfgNumber == 1: 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(1204, self.misra_12_4, cfg) self.executeCheck(1301, self.misra_13_1, cfg) self.executeCheck(1303, self.misra_13_3, cfg) self.executeCheck(1304, self.misra_13_4, cfg) self.executeCheck(1305, self.misra_13_5, cfg) self.executeCheck(1306, self.misra_13_6, cfg) self.executeCheck(1401, self.misra_14_1, cfg) self.executeCheck(1402, self.misra_14_2, cfg) self.executeCheck(1404, self.misra_14_4, cfg) self.executeCheck(1501, self.misra_15_1, cfg) self.executeCheck(1502, self.misra_15_2, cfg) self.executeCheck(1503, self.misra_15_3, cfg) self.executeCheck(1505, self.misra_15_5, cfg) if cfgNumber == 1: self.executeCheck(1506, self.misra_15_6, data.rawTokens) self.executeCheck(1507, self.misra_15_7, cfg) self.executeCheck(1602, self.misra_16_2, cfg) if cfgNumber == 1: self.executeCheck(1603, self.misra_16_3, data.rawTokens) self.executeCheck(1604, self.misra_16_4, cfg) self.executeCheck(1605, self.misra_16_5, cfg) self.executeCheck(1606, self.misra_16_6, cfg) self.executeCheck(1607, self.misra_16_7, cfg) self.executeCheck(1701, self.misra_17_1, cfg) self.executeCheck(1702, self.misra_17_2, cfg) if cfgNumber == 1: self.executeCheck(1706, self.misra_17_6, data.rawTokens) self.executeCheck(1707, self.misra_17_7, cfg) self.executeCheck(1708, self.misra_17_8, cfg) self.executeCheck(1804, self.misra_18_4, cfg) self.executeCheck(1805, self.misra_18_5, cfg) self.executeCheck(1807, self.misra_18_7, cfg) self.executeCheck(1808, self.misra_18_8, cfg) self.executeCheck(1902, self.misra_19_2, cfg) self.executeCheck(2001, self.misra_20_1, cfg) self.executeCheck(2002, self.misra_20_2, cfg) if cfgNumber == 1: self.executeCheck(2003, self.misra_20_3, data.rawTokens) self.executeCheck(2004, self.misra_20_4, cfg) self.executeCheck(2005, self.misra_20_5, cfg) self.executeCheck(2006, self.misra_20_7, cfg) self.executeCheck(2010, self.misra_20_10, cfg) self.executeCheck(2013, self.misra_20_13, cfg) self.executeCheck(2014, self.misra_20_14, cfg) self.executeCheck(2101, self.misra_21_1, cfg) self.executeCheck(2103, self.misra_21_3, cfg) self.executeCheck(2104, self.misra_21_4, cfg) self.executeCheck(2105, self.misra_21_5, cfg) self.executeCheck(2106, self.misra_21_6, cfg) self.executeCheck(2107, self.misra_21_7, cfg) self.executeCheck(2108, self.misra_21_8, cfg) self.executeCheck(2109, self.misra_21_9, cfg) self.executeCheck(2110, self.misra_21_10, cfg) self.executeCheck(2111, self.misra_21_11, cfg) self.executeCheck(2112, self.misra_21_12, cfg) # 22.4 is already covered by Cppcheck writeReadOnlyFile RULE_TEXTS_HELP = '''Path to text file of MISRA rules If you have the tool 'pdftotext' you might be able to generate this textfile with such command: pdftotext MISRA_C_2012.pdf MISRA_C_2012.txt Otherwise you can more or less copy/paste the chapter Appendix A Summary of guidelines from the MISRA pdf. You can buy the MISRA pdf from http://www.misra.org.uk/ Format: <..arbitrary text..> Appendix A Summary of guidelines Rule 1.1 Rule text for 1.1 Rule 1.2 Rule text for 1.2 <...> ''' SUPPRESS_RULES_HELP = '''MISRA rules to suppress (comma-separated) For example, if you'd like to suppress rules 15.1, 11.3, and 20.13, run: python misra.py --suppress-rules 15.1,11.3,20.13 ... ''' def get_args(): """Generates list of command-line arguments acceptable by misra.py script.""" parser = cppcheckdata.ArgumentParser() parser.add_argument("--rule-texts", type=str, help=RULE_TEXTS_HELP) parser.add_argument("--verify-rule-texts", help="Verify that all supported rules texts are present in given file and exit.", action="store_true") parser.add_argument("--suppress-rules", type=str, help=SUPPRESS_RULES_HELP) parser.add_argument("--no-summary", help="Hide summary of violations", action="store_true") parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true") parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules") parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true") parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true") return parser.parse_args() def main(): args = get_args() settings = MisraSettings(args) checker = MisraChecker(settings) if args.generate_table: generateTable() sys.exit(0) if args.rule_texts: filename = os.path.expanduser(args.rule_texts) filename = os.path.normpath(filename) if not os.path.isfile(filename): print('Fatal error: file is not found: ' + filename) sys.exit(1) checker.loadRuleTexts(filename) if args.verify_rule_texts: checker.verifyRuleTexts() sys.exit(0) if args.verify_rule_texts and not args.rule_texts: print("Error: Please specify rule texts file with --rule-texts=") sys.exit(1) if args.suppress_rules: checker.setSuppressionList(args.suppress_rules) if args.file_prefix: checker.setFilePrefix(args.file_prefix) if not args.dumpfile: if not args.quiet: print("No input files.") sys.exit(0) exitCode = 0 for item in args.dumpfile: checker.parseDump(item) if settings.verify: verify_expected = checker.get_verify_expected() verify_actual = checker.get_verify_actual() for expected in verify_expected: if expected not in verify_actual: print('Expected but not seen: ' + expected) exitCode = 1 for actual in verify_actual: if actual not in verify_expected: print('Not expected: ' + actual) exitCode = 1 # Existing behavior of verify mode is to exit # on the first un-expected output. # TODO: Is this required? or can it be moved to after # all input files have been processed if exitCode != 0: sys.exit(exitCode) # Under normal operation exit with a non-zero exit code # if there were any violations. if not settings.verify: number_of_violations = len(checker.get_violations()) if number_of_violations > 0: exitCode = 1 if settings.show_summary: print("\nMISRA rules violations found:\n\t%s\n" % ( "\n\t".join(["%s: %d" % (viol, len(checker.get_violations(viol))) for viol in checker.get_violation_types()]))) rules_violated = {} for severity, ids in checker.get_violations(): for misra_id in ids: rules_violated[misra_id] = rules_violated.get(misra_id, 0) + 1 print("MISRA rules violated:") convert = lambda text: int(text) if text.isdigit() else text misra_sort = lambda key: [ convert(c) for c in re.split(r'[\.-]([0-9]*)', key) ] for misra_id in sorted(rules_violated.keys(), key=misra_sort): res = re.match(r'misra-c2012-([0-9]+)\\.([0-9]+)', misra_id) if res is None: num = 0 else: num = int(res.group(1)) * 100 + int(res.group(2)) severity = '-' if num in checker.ruleTexts: severity = checker.ruleTexts[num].cppcheck_severity print("\t%15s (%s): %d" % (misra_id, severity, rules_violated[misra_id])) if args.show_suppressed_rules: checker.showSuppressedRules() sys.exit(exitCode) if __name__ == '__main__': main() cppcheck-1.90/addons/naming.json000066400000000000000000000005271357737443600166660ustar00rootroot00000000000000{ "RE_VARNAME": ["[a-z]*[a-zA-Z0-9_]*\\Z"], "RE_PRIVATE_MEMBER_VARIABLE": null, "RE_FUNCTIONNAME": ["[a-z0-9A-Z]*\\Z"], "var_prefixes": {"uint32_t": "ui32", "int*": "intp"}, "function_prefixes": {"uint16_t": "ui16", "uint32_t": "ui32"}, "skip_one_char_variables": false } cppcheck-1.90/addons/naming.py000077500000000000000000000066531357737443600163560ustar00rootroot00000000000000#!/usr/bin/env python3 # # cppcheck addon for naming conventions # # Example usage (variable name must start with lowercase, function name must start with uppercase): # $ cppcheck --dump path-to-src/ # $ python addons/naming.py --var='[a-z].*' --function='[A-Z].*' path-to-src/*.dump # import cppcheckdata import sys import re def validate_regex(expr): try: re.compile(expr) except re.error: print('Error: "{}" is not a valid regular expression.'.format(expr)) exit(1) RE_VARNAME = None RE_CONSTNAME = None RE_PRIVATE_MEMBER_VARIABLE = None RE_FUNCTIONNAME = None for arg in sys.argv[1:]: if arg[:6] == '--var=': RE_VARNAME = arg[6:] validate_regex(RE_VARNAME) elif arg.startswith('--const='): RE_CONSTNAME = arg[arg.find('=')+1:] validate_regex(RE_CONSTNAME) elif arg.startswith('--private-member-variable='): RE_PRIVATE_MEMBER_VARIABLE = arg[arg.find('=')+1:] validate_regex(RE_PRIVATE_MEMBER_VARIABLE) elif arg[:11] == '--function=': RE_FUNCTIONNAME = arg[11:] validate_regex(RE_FUNCTIONNAME) def reportError(token, severity, msg, errorId): cppcheckdata.reportError(token, severity, msg, 'naming', errorId) for arg in sys.argv[1:]: if not arg.endswith('.dump'): continue print('Checking ' + arg + '...') data = cppcheckdata.parsedump(arg) for cfg in data.configurations: if len(data.configurations) > 1: print('Checking ' + arg + ', config "' + cfg.name + '"...') if RE_VARNAME: for var in cfg.variables: if var.access == 'Private': continue if var.nameToken and not var.isConst: res = re.match(RE_VARNAME, var.nameToken.str) if not res: reportError(var.typeStartToken, 'style', 'Variable ' + var.nameToken.str + ' violates naming convention', 'varname') if RE_CONSTNAME: for var in cfg.variables: if var.access == 'Private': continue if var.nameToken and var.isConst: res = re.match(RE_CONSTNAME, var.nameToken.str) if not res: reportError(var.typeStartToken, 'style', 'Constant ' + var.nameToken.str + ' violates naming convention', 'constname') if RE_PRIVATE_MEMBER_VARIABLE: for var in cfg.variables: if (var.access is None) or var.access != 'Private': continue res = re.match(RE_PRIVATE_MEMBER_VARIABLE, var.nameToken.str) if not res: reportError(var.typeStartToken, 'style', 'Private member variable ' + var.nameToken.str + ' violates naming convention', 'privateMemberVariable') if RE_FUNCTIONNAME: for scope in cfg.scopes: if scope.type == 'Function': function = scope.function if function is not None and function.type in ('Constructor', 'Destructor'): continue res = re.match(RE_FUNCTIONNAME, scope.className) if not res: reportError( scope.bodyStart, 'style', 'Function ' + scope.className + ' violates naming convention', 'functionName') cppcheck-1.90/addons/namingng.py000077500000000000000000000266211357737443600167000ustar00rootroot00000000000000#!/usr/bin/env python3 # # cppcheck addon for naming conventions # An enhanced version. Configuration is taken from a json file # It supports to check for type-based prefixes in function or variable names. # # Example usage (variable name must start with lowercase, function name must start with uppercase): # $ cppcheck --dump path-to-src/ # $ python namingng.py test.c.dump # # JSON format: # # { # "RE_VARNAME": "[a-z]*[a-zA-Z0-9_]*\\Z", # "RE_PRIVATE_MEMBER_VARIABLE": null, # "RE_FUNCTIONNAME": "[a-z0-9A-Z]*\\Z", # "var_prefixes": {"uint32_t": "ui32"}, # "function_prefixes": {"uint16_t": "ui16", # "uint32_t": "ui32"} # } # # RE_VARNAME, RE_PRIVATE_MEMBER_VARIABLE and RE_FUNCTIONNAME are regular expressions to cover the basic names # In var_prefixes and function_prefixes there are the variable-type/prefix pairs import cppcheckdata import sys import re import argparse import json # Auxiliary class class DataStruct: def __init__(self, file, linenr, string): self.file = file self.linenr = linenr self.str = string def reportError(filename, linenr, severity, msg): message = "[{filename}:{linenr}] ( {severity} ) naming.py: {msg}\n".format( filename=filename, linenr=linenr, severity=severity, msg=msg ) sys.stderr.write(message) return message def loadConfig(configfile): with open(configfile) as fh: data = json.load(fh) return data def checkTrueRegex(data, expr, msg, errors): res = re.match(expr, data.str) if res: errors.append(reportError(data.file, data.linenr, 'style', msg)) def checkFalseRegex(data, expr, msg, errors): res = re.match(expr, data.str) if not res: errors.append(reportError(data.file, data.linenr, 'style', msg)) def evalExpr(conf, exp, mockToken, msgType, errors): if isinstance(conf, dict): if conf[exp][0]: msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][1] checkTrueRegex(mockToken, exp, msg, errors) elif ~conf[exp][0]: msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][1] checkFalseRegex(mockToken, exp, msg, errors) else: msg = msgType + ' ' + mockToken.str + ' violates naming convention : ' + conf[exp][0] checkFalseRegex(mockToken, exp, msg, errors) else: msg = msgType + ' ' + mockToken.str + ' violates naming convention' checkFalseRegex(mockToken, exp, msg, errors) def process(dumpfiles, configfile, debugprint=False): errors = [] conf = loadConfig(configfile) for afile in dumpfiles: if not afile[-5:] == '.dump': continue print('Checking ' + afile + '...') data = cppcheckdata.parsedump(afile) # Check File naming if "RE_FILE" in conf and conf["RE_FILE"]: mockToken = DataStruct(afile[:-5], "0", afile[afile.rfind('/') + 1:-5]) msgType = 'File name' for exp in conf["RE_FILE"]: evalExpr(conf["RE_FILE"], exp, mockToken, msgType, errors) # Check Namespace naming if "RE_NAMESPACE" in conf and conf["RE_NAMESPACE"]: for tk in data.rawTokens: if tk.str == 'namespace': mockToken = DataStruct(tk.next.file, tk.next.linenr, tk.next.str) msgType = 'Namespace' for exp in conf["RE_NAMESPACE"]: evalExpr(conf["RE_NAMESPACE"], exp, mockToken, msgType, errors) for cfg in data.configurations: if len(data.configurations) > 1: print('Checking ' + afile + ', config "' + cfg.name + '"...') if "RE_VARNAME" in conf and conf["RE_VARNAME"]: for var in cfg.variables: if var.nameToken and var.access != 'Global' and var.access != 'Public' and var.access != 'Private': prev = var.nameToken.previous varType = prev.str while "*" in varType and len(varType.replace("*", "")) == 0: prev = prev.previous varType = prev.str + varType if debugprint: print("Variable Name: " + str(var.nameToken.str)) print("original Type Name: " + str(var.nameToken.valueType.originalTypeName)) print("Type Name: " + var.nameToken.valueType.type) print("Sign: " + str(var.nameToken.valueType.sign)) print("variable type: " + varType) print("\n") print("\t-- {} {}".format(varType, str(var.nameToken.str))) if conf["skip_one_char_variables"] and len(var.nameToken.str) == 1: continue if varType in conf["var_prefixes"]: if not var.nameToken.str.startswith(conf["var_prefixes"][varType]): errors.append(reportError( var.typeStartToken.file, var.typeStartToken.linenr, 'style', 'Variable ' + var.nameToken.str + ' violates naming convention')) mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str) msgType = 'Variable' for exp in conf["RE_VARNAME"]: evalExpr(conf["RE_VARNAME"], exp, mockToken, msgType, errors) # Check Private Variable naming if "RE_PRIVATE_MEMBER_VARIABLE" in conf and conf["RE_PRIVATE_MEMBER_VARIABLE"]: # TODO: Not converted yet for var in cfg.variables: if (var.access is None) or var.access != 'Private': continue mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str) msgType = 'Private member variable' for exp in conf["RE_PRIVATE_MEMBER_VARIABLE"]: evalExpr(conf["RE_PRIVATE_MEMBER_VARIABLE"], exp, mockToken, msgType, errors) # Check Public Member Variable naming if "RE_PUBLIC_MEMBER_VARIABLE" in conf and conf["RE_PUBLIC_MEMBER_VARIABLE"]: for var in cfg.variables: if (var.access is None) or var.access != 'Public': continue mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str) msgType = 'Public member variable' for exp in conf["RE_PUBLIC_MEMBER_VARIABLE"]: evalExpr(conf["RE_PUBLIC_MEMBER_VARIABLE"], exp, mockToken, msgType, errors) # Check Global Variable naming if "RE_GLOBAL_VARNAME" in conf and conf["RE_GLOBAL_VARNAME"]: for var in cfg.variables: if (var.access is None) or var.access != 'Global': continue mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str) msgType = 'Public member variable' for exp in conf["RE_GLOBAL_VARNAME"]: evalExpr(conf["RE_GLOBAL_VARNAME"], exp, mockToken, msgType, errors) # Check Functions naming if "RE_FUNCTIONNAME" in conf and conf["RE_FUNCTIONNAME"]: for token in cfg.tokenlist: if token.function: if token.function.type == 'Constructor' or token.function.type == 'Destructor': continue retval = token.previous.str prev = token.previous while "*" in retval and len(retval.replace("*", "")) == 0: prev = prev.previous retval = prev.str + retval if debugprint: print("\t:: {} {}".format(retval, token.function.name)) if retval and retval in conf["function_prefixes"]: if not token.function.name.startswith(conf["function_prefixes"][retval]): errors.append(reportError( token.file, token.linenr, 'style', 'Function ' + token.function.name + ' violates naming convention')) mockToken = DataStruct(token.file, token.linenr, token.function.name) msgType = 'Function' for exp in conf["RE_FUNCTIONNAME"]: evalExpr(conf["RE_FUNCTIONNAME"], exp, mockToken, msgType, errors) # Check Class naming if "RE_CLASS_NAME" in conf and conf["RE_CLASS_NAME"]: for fnc in cfg.functions: # Check if it is Constructor/Destructor if fnc.type == 'Constructor' or fnc.type == 'Destructor': mockToken = DataStruct(fnc.tokenDef.file, fnc.tokenDef.linenr, fnc.name) msgType = 'Class ' + fnc.type for exp in conf["RE_CLASS_NAME"]: evalExpr(conf["RE_CLASS_NAME"], exp, mockToken, msgType, errors) return errors if __name__ == "__main__": parser = argparse.ArgumentParser(description='Naming verification') parser.add_argument('dumpfiles', type=str, nargs='+', help='A set of dumpfiles to process') parser.add_argument("--debugprint", action="store_true", default=False, help="Add debug prints") parser.add_argument("--configfile", type=str, default="naming.json", help="Naming check config file") parser.add_argument("--verify", action="store_true", default=False, help="verify this script. Must be executed in test folder !") args = parser.parse_args() errors = process(args.dumpfiles, args.configfile, args.debugprint) if args.verify: print(errors) if len(errors) < 6: print("Not enough errors found") sys.exit(1) target = [ '[namingng_test.c:8] ( style ) naming.py: Variable badui32 violates naming convention\n', '[namingng_test.c:11] ( style ) naming.py: Variable a violates naming convention\n', '[namingng_test.c:29] ( style ) naming.py: Variable badui32 violates naming convention\n', '[namingng_test.c:20] ( style ) naming.py: Function ui16bad_underscore violates naming convention\n', '[namingng_test.c:25] ( style ) naming.py: Function u32Bad violates naming convention\n', '[namingng_test.c:37] ( style ) naming.py: Function Badui16 violates naming convention\n'] diff = set(errors) - set(target) if len(diff): print("Not the right errors found {}".format(str(diff))) sys.exit(1) print("Verification done\n") sys.exit(0) if len(errors): print('Found errors: {}'.format(len(errors))) sys.exit(1) sys.exit(0) cppcheck-1.90/addons/test/000077500000000000000000000000001357737443600154755ustar00rootroot00000000000000cppcheck-1.90/addons/test/__init__.py000066400000000000000000000000001357737443600175740ustar00rootroot00000000000000cppcheck-1.90/addons/test/cert-test.c000066400000000000000000000067561357737443600175710ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump cert-test.c && python ../cert.py -verify cert-test.c.dump struct S { short a; short b; }; #pragma pack() struct PackedStruct { short a; short b; }; void api01() { const size_t String_Size = 20; struct bad_node_s { char name[String_Size]; struct bad_node_s* next; // cert-API01-C } struct good_node_s { struct good_node_s* next; char name[String_Size]; } struct also_good_node_s { struct also_good_node_s* next; char *name; } } void dostuff(int *data); void exp05() { const int x = 42; int y = (int)x; int *p; p = (int *)&x; // cert-EXP05-C const int data[] = {1,2,3,4}; dostuff(data); // cert-EXP05-C } void print(const char *p); void exp05_fp() { print("hello"); } void exp42() { struct S s1 = {1,2}; struct S s2 = {1,2}; memcmp(&s1, &s2, sizeof(struct S)); // cert-EXP42-C struct PackedStruct s3 = {1,2}; struct PackedStruct s4 = {1,2}; memcmp(&s3, &s4, sizeof(struct S)); } void exp46() { if ((x == y) & z) {} // cert-EXP46-c } unsigned char int31(int x) { x = (unsigned char)1000; // cert-INT31-c x = (signed char)0xff; // cert-INT31-c x = (unsigned char)-1; // cert-INT31-c x = (unsigned long long)-1; // cert-INT31-c } void env33() { system("chmod -x $(which chmod)"); // cert-ENV33-C system(""); // cert-ENV33-C system(NULL); // no-warning system(0); // no-warning const int *np = NULL; system(np); // no-warning int system; } void msc24() { struct S { int x; int fopen; }; struct S s; time_t rawtime; struct tm *timeinfo; char buffer[256]; int i; long int li; long long int lli; FILE *f; s.fopen = 123; f = fopen ("myfile.txt","w+"); //cert-MSC24-C setbuf ( f , buffer ) //cert-MSC24-C for ( i='A' ; i<='Z' ; i++) fputc ( n, f); rewind (f); //cert-MSC24-C fclose (f); time ( &rawtime ); timeinfo = localtime ( &rawtime ); printf ( "The current date/time is: %s", asctime (timeinfo) ); //cert-MSC24-C n = atof (buffer); //cert-MSC24-C m = sin (n*pi/180); i = atoi (buffer); //cert-MSC24-C li = atol(buffer); //cert-MSC24-C lli = atoll(buffer); //cert-MSC24-C time (&rawtime); printf ("The current local time is: %s", ctime (&rawtime)); //cert-MSC24-C freopen ("myfile.txt","w",stdout); //cert-MSC24-C printf ("This sentence is redirected to a file."); fclose (stdout); } void msc30() { unsigned int num = rand(); // cert-MSC30-c int rand = 5; int a = rand; } void exp15() { int x=5, y=7; if(x==y); //cert-EXP15-C { printf("not working\n"); } if(x) ; } void str03() { char *string_data=(char*)malloc(16); char a[16]; int d; strncpy(a, string_data, sizeof(a)); //cert-STR03-C strncpy(a, string_data, 5); d=sizeof(int); } void str05() { int x=5, y=7; if(x==y); //cert-EXP15-C { printf("not working\n"); } if(x) ; } void str07(char *buf, const char *newBuf) { const char *str="test"; strcat(buf,"bla"); strcat(buf, str); //cert-STR07-C strcat(buf, newBuf); //cert-STR07-C strcpy(str, newBuf); //cert-STR07-C } void str11() { const char str[3]="abc"; //cert-STR11-C const char *x[10]; x[3]="def"; } cppcheck-1.90/addons/test/cert-test.cpp000066400000000000000000000011161357737443600201120ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump cert-test.cpp && python ../cert.py -verify cert-test.cpp.dump #include class msc30TestClass { public: static int rand(); }; namespace exp05c { using uint32 = std::uint32_t; static const uint32 a = static_cast(0xFFFFFFFF); } void msc30(msc30TestClass & testClass) { unsigned int num = rand(); // cert-MSC30-c num = std::rand(); // cert-MSC30-c num = msc30TestClass::rand(); num = unknownClass::rand(); num = testClass.rand(); num = unknownClass.rand(); int rand = 5; int a = rand; } cppcheck-1.90/addons/test/misc-test.cpp000066400000000000000000000013411357737443600201100ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump misc-test.cpp && python ../misc.py -verify misc-test.cpp.dump // Warn about string concatenation in array initializers.. const char *a[] = {"a" "b"}; // stringConcatInArrayInit const char *b[] = {"a","b" "c"}; // stringConcatInArrayInit #define MACRO "MACRO" const char *c[] = { MACRO "text" }; // stringConcatInArrayInit // Function is implicitly virtual class base { virtual void dostuff(int); }; class derived : base { void dostuff(int); // implicitlyVirtual }; // Pass struct to ellipsis function struct {int x;int y;} s; void ellipsis(int x, ...); void foo(std::vector v) { ellipsis(321, s); // ellipsisStructArg ellipsis(321, v[0]); // ellipsisStructArg } cppcheck-1.90/addons/test/misra/000077500000000000000000000000001357737443600166105ustar00rootroot00000000000000cppcheck-1.90/addons/test/misra/misra-suppressions1-test.c000066400000000000000000000016351357737443600237050ustar00rootroot00000000000000// To test: // ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump // There should be no violations reported // This needs to stay at line number 7 to make the test pass // If it is changed update suppressions.txt with the new line number #include //21.6 extern int misra_5_2_var_hides_var______31x; static int misra_5_2_var_hides_var______31y;//5.2 static int misra_5_2_function_hides_var_31x; void misra_5_2_function_hides_var_31y(void) {}//5.2 void foo(void) { int i; switch(misra_5_2_func1()) //16.4 16.6 { case 1: { do { for(i = 0; i < 10; i++) { if(misra_5_2_func3()) //14.4 { int misra_5_2_var_hides_var_1____31x; int misra_5_2_var_hides_var_1____31y;//5.2 } } } while(misra_5_2_func2()); //14.4 } } } cppcheck-1.90/addons/test/misra/misra-suppressions2-test.c000066400000000000000000000010221357737443600236740ustar00rootroot00000000000000// To test: // ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump // There should be no violations reported union misra_5_2_field_hides_field__63x { //19.2 int misra_5_2_field_hides_field__31x; int misra_5_2_field_hides_field__31y;//5.2 }; struct misra_5_2_field_hides_field__63y { //5.2 int misra_5_2_field_hides_field1_31x; int misra_5_2_field_hides_field1_31y;//5.2 }; const char *s41_1 = "\x41g"; // 4.1 const char *s41_2 = "\x41\x42"; cppcheck-1.90/addons/test/misra/misra-test.c000066400000000000000000001115031357737443600210450ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump misra-test.c && python ../../misra.py -verify misra-test.c.dump #include "path\file.h" // 20.2 #include /*abc*/ "file.h" // no warning #include PATH "file.h" // 20.3 #include // no warning #include // 21.4 #include // 21.5 #include //21.6 #include //21.6 #include // 21.10 #include // 21.11 #include typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef signed int s32; typedef unsigned long long u64; /* // */ // 3.1 /* /* */ // 3.1 //// // http://example.com // no warning void misra_2_7_unused_param (int *param1, int unused_param) // 2.7 { *param1 = 42U; } void misra_2_7_used_params (int *param1, int param2, int param3) { (void)param3; *param1 = param2; } void misra_3_2(int enable) { // This won't generate a violation because of subsequent blank line \ int y = 0; int x = 0; // 3.2 non-compliant comment ends with backslash \ if (enable != 0) { ++x; // This is always executed // 3.2 potentially non-compliant comment ends with trigraph resolved to backslash ??/ ++y; // This is hidden if trigraph replacement is active } (void)printf("x=%i, y=%i\n", x, y); } extern int misra_5_1_extern_var_hides_var_x; extern int misra_5_1_extern_var_hides_var_y; //5.1 int misra_5_1_var_hides_var________a; int misra_5_1_var_hides_var________b; int misra_5_1_var_hides_var________b1; int misra_5_1_var_hides_var________b2; //5.1 int misra_5_1_var_hides_var________c; //5.1 int misra_5_1_var_hides_var________d; //5.1 int misra_5_1_var_hides_var________e; //5.1 extern const uint8_t misra_5_2_var1; const uint8_t misra_5_2_var1 = 3; // no warning static int misra_5_2_var_hides_var______31x; static int misra_5_2_var_hides_var______31y;//5.2 static int misra_5_2_function_hides_var_31x; void misra_5_2_function_hides_var_31y(void) {}//5.2 void foo(void) { int i; switch(misra_5_2_func1()) //16.4 16.6 { case 1: { do { for(i = 0; i < 10; i++) { if(misra_5_2_func3()) //14.4 { int misra_5_2_var_hides_var_1____31x; int misra_5_2_var_hides_var_1____31y;//5.2 } } } while(misra_5_2_func2()); //14.4 } } } union misra_5_2_field_hides_field__63x { //19.2 int misra_5_2_field_hides_field__31x; int misra_5_2_field_hides_field__31y;//5.2 }; struct misra_5_2_field_hides_field__63y { //5.2 int misra_5_2_field_hides_field1_31x; int misra_5_2_field_hides_field1_31y;//5.2 }; const char *s41_1 = "\x41g"; // 4.1 const char *s41_2 = "\x41\x42"; const char *s41_3 = "\x41" "\x42"; const char *s41_4 = "\x41" "g"; const char *s41_5 = "\x41\xA"; const char *s41_6 = "\xA\x41"; const char *s41_7 = "\xAA\xg\x41"; // 4.1 const char *s41_8 = "\xAA\x\x41"; // 4.1 const char *s41_9 = "unknown\gsequence"; const char *s41_10 = "simple\nsequence"; const char *s41_11 = "string"; int c41_3 = '\141t'; // 4.1 int c41_4 = '\141\t'; int c41_5 = '\0'; int c41_6 = '\0\t'; int c41_7 = '\12\t'; int c41_8 = '\0t'; // 4.1 int c41_9 = '\12'; int c41_10 = '\12\n'; int c41_11 = '\12n'; // 4.1 int c41_12 = '\12323'; // 4.1 int c41_13 = '\123\3'; int c41_14 = '\777\777'; int c41_15 = 'a'; void misra_4_1() { (void)printf("\x41g"); // 4.1 (void)printf("\x41\x42"); (void)printf("\x41" "g"); } const char *s42_1 = "String containing trigraphs ??-??-??"; // 4.2 const char *s42_2 = "String containing trigraph???=preceded by question mark"; // 4.2 const char *s42_3 = "No trigraph?(?'?)"; void misra_4_2() { (void)printf("??=Trigraph\n"); // 4.2 (void)printf("No?/Trigraph\n"); } #define misra_5_4_macro_hides_macro__31x 1 #define misra_5_4_param_hides_macro__31x 1 #define misra_5_4_macro_hides_macro__31y 2 //5.4 #define m1(misra_5_4_param_hides_macro__31y) 1 //5.4 #define m2(misra_5_4_param_hides_param__31x,misra_5_4_param_hides_param__31y) 1 //5.4 #ifdef misra_5_4_macro_hides_macro__31x #define misra_5_4_macro 1 // no warning #else #define misra_5_4_macro 2 // no warning #endif #define misra_5_5_var_hides_macro____31x 1 #define misra_5_5_functionhides_macro31x 1 #define misra_5_5_param_hides_macro__31x 1 #define misra_5_5_tag_hides_macro____31x 1 #define misra_5_5_hides_macro________31x 1 int misra_5_5_var_hides_macro____31y; //5.5 void misra_5_5_functionhides_macro31y(int misra_5_5_param_hides_macro__31y){(void)misra_5_5_param_hides_macro__31y;} //5.5 struct misra_5_5_tag_hides_macro____31y { //5.5 int x; }; void misra_5_5_func1() { switch(misra_5_5_func2()) //16.4 16.6 { case 1: { do { if(misra_5_5_func3()) //14.4 { int misra_5_5_hides_macro________31y; //5.5 } } while(misra_5_5_func2()); //14.4 } } } void misra_7_1() { int x = 066; // 7.1 } void misra_7_3() { long misra_7_3_a = 0l; //7.3 long misra_7_3_b = 0lU; //7.3 long long misra_7_3_c = 0Ull; //7.3 long long misra_7_3_d = 0ll; //7.3 long double misra_7_3_e = 7.3l; //7.3 } extern int a811[]; // 8.11 enum misra_8_12_a { misra_a1 = 1, misra_a2 = 2, misra_a3, misra_a4 = 3 }; //8.12 enum misra_8_12_b { misra_b1, misra_b2, misra_b3 = 3, misra_b4 = 3 }; // no-warning enum misra_8_12_c { misra_c1 = misra_a1, misra_c2 = 1 }; // no-warning enum misra_8_12_d { misra_d1 = 1, misra_d2 = 2, misra_d3 = misra_d1 }; // no-warning enum misra_8_12_e { misra_e1 = sizeof(int), misra_e2}; // no-crash void misra_8_14(char * restrict str) {(void)str;} // 8.14 void misra_9_5() { int x[] = {[0]=23}; // 9.5 } typedef char misra_10_1_char_t; #define MISRA_10_1_CHAR char void misra_10_1(uint8_t u, char c1, char c2) { int32_t i; char c; enum { E1 = 1 }; i = 3 << 1; // 10.1 i = (u & u) << 4; // no-warning c = c1 & c2; // 10.1 c = c1 << 1; // 10.1 i = c1 > c2; // no-warning i = E1 + i; // no-warning char ch1 = 'a'; char ch2 = 'b'; char ch3; ch3 = ch1 & ch2; // 10.1 misra_10_1_char_t ct1 = 'a'; misra_10_1_char_t ct2 = 'b'; misra_10_1_char_t ct3; ct3 = ct1 & ct2; // 10.1 MISRA_10_1_CHAR cd1 = 'a'; MISRA_10_1_CHAR cd2 = 'b'; MISRA_10_1_CHAR cd3; cd3 = cd1 & cd2; // 10.1 } void misra_10_1_ternary() { int a; uint8_t ui8; uint16_t ui16; int8_t i8; int16_t i16; a = ui16 << ui16; a = ui16 << (get_bool(42) ? ui16 : ui16); a = ui16 << (get_bool(42) ? ui16 : (get_bool(34) ? ui16 : ui16)); // 10.4 a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui16) : ui16); // 10.4 a = ui16 << (get_bool(42) ? i16 : (get_bool(34) ? ui16 : ui16)); // 10.1 a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : i16) : ui16); // 10.1 10.4 a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui16) : i16); // 10.1 a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8); // 10.4 a = ui16 << (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8); // 10.1 10.4 a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << ui16; // 10.4 a = (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8) << ui16; // 10.1 10.4 a = (get_bool(42) ? (get_bool(34) ? ui16 : i8) : ui8) << ui16; // 10.1 10.4 a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : i8) << ui16; // 10.1 a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << (get_bool(19) ? ui16 : ui8); // 10.4 a = (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8) << (get_bool(19) ? ui16 : ui8); // 10.1 10.4 a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << (get_bool(19) ? i16 : ui8); // 10.1 10.4 } void misra_10_4(u32 x, s32 y) { z = x + 3; // 10.4 enum misra_10_4_enuma { misra_10_4_A1, misra_10_4_A2, misra_10_4_A3 } a; enum misra_10_4_enumb { misra_10_4_B1, misra_10_4_B2, misra_10_4_B3 }; if ( misra_10_4_B1 > misra_10_4_A1 ) //10.4 { ; } z = x + y; //10.4 z = (a == misra_10_4_A3) ? x : y; //10.4 z = (a == misra_10_4_A3) ? y : y; // no-warning } void misra_10_6(u8 x, u32 a, u32 b, char c1, char c2) { u16 y = x+x; // 10.6 u16 z = ~u8 x ;//10.6 u32 c = ( u16) ( u32 a + u32 b ); //10.6 s32 i = c1 - c2; // FIXME: False positive for 10.6 (this is compliant). Trac #9488 } void misra_10_8(u8 x, s32 a, s32 b) { y = (u16)x; y = (u16)(x+x); // 10.8 y = (u16) (a + b) //10.8 } struct Fred {}; struct Wilma {}; void misra_11_3(u8* p, struct Fred *fred) { x = (u64*)p; // 11.3 struct Wilma *wilma = (struct Wilma *)fred; // 11.3 } void misra_11_4(u8*p) { u64 y = (u64)p; // 11.4 u8 *misra_11_4_A = ( u8 * ) 0x0005;// 11.4 s32 misra_11_4_B; u8 *q = ( u8 * ) misra_11_4_B; // 11.4 } void misra_11_5(void *p) { u16 *p16; x = (u8 *)p; // 11.5 p16 = p; // 11.5 } void misra_11_6() { void *p; p = (void*)123; // 11.6 x = (u64)p; // 11.6 p = ( void * )0; // no-warning (void)p; // no-warning } void misra_11_7(int *p, float f) { x = ( float ) p; //11.7 y = ( int * ) f; //11.7 } void misra_11_7_extra(int *p, float f, bool b) { (void) p; // no-warning (void) f; // no-warning (void) b; // no-warning } char * misra_11_8_const(const char *str) {(void)str;} char * misra_11_8(const char *str) { (void)misra_11_8_const(str); // no-warning return (char *)str; // 11.8 } #define MISRA_11_9_NULL_1 (1-1) #define MISRA_11_9_NULL_2 ( void * ) 0 #define MISRA_11_9_NULL_3 NULL void misra_11_9(){ int *p1 = (5-5); //11.9 int *p2 = MISRA_11_9_NULL_2 ; // no-warning int *p3 = MISRA_11_9_NULL_3 ; // no-warning if ( p1 == MISRA_11_9_NULL_1 ) //11.9 { ; } } void misra_12_1() { sz = sizeof x + y; // 12.1 a = (b * c) + d; a = b << c + d; // 12.1 } void misra_12_2(u8 x) { a = x << 8; // 12.2 } static int misra_12_3_v1 = 0, misra_12_3_v2; // 12.3 static int misra_12_3_v3, misra_12_3_v4; // 12.3 enum misra_12_3_e1 { M123A1, M123B1, M123C1 }; enum misra_12_3_e2 { M123A2 = 3, M123B2 = 4, M123C2 }; typedef enum misra_12_3_e3 { M123A3 , M123B3, M123C3 } misra_12_3_e3_t; typedef enum { M123A4 , M123B4, M123C4 } misra_12_3_e4_t; struct misra_12_3_s1 { int a; int b; int c, d; }; 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; 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 int misra_12_3_fn3(int a, int b) { return a+b;} static int misra_12_3_v5, misra_12_4_v6; // 12.3 void misra_12_3_fn4(const uint32_t value, uint8_t * const y) {} // 2.7 uint32_t misra_12_3_fn5(const uint32_t * const, const uint8_t) {} // 2.7 extern void misra_12_3_fn6(const uint32_t value, uint8_t * const y); extern uint32_t misra_12_3_fn7(const uint32_t * const, const uint8_t); #define MISRA_12_3_FN3_1(A, B) (misra_12_3_fn3(A, B)) #define MISRA_12_3_FN3_2(A, B) (misra_12_3_fn3(A, \ B)) #define MISRA_12_3_FN3_2_MSG(x) x, fflush(stderr) void misra_12_3(int, int, int); // no warning void misra_12_3(int a, int b, int c) { // no warning int a1, a2; // 12.3 int a3; int a4; // no warning int a5 = 9, a6; // 12.3 int a7, a8 = 11; // 12.3 int a9 = foo(), a10; // 12.3 int a11 = a = b = c; // 17.8 struct s1 {int a, b;}; int a12, a13; // 12.3 int a14, a15; misra_12_3_fn3(a14, a15); // 12.3 17.7 ; int a16, a17; // 12.3 int a18; int a19, a20; // 12.3 int a21, a22; int a23; // 12.3 int a24, // 12.3 a25; int a26 , a27; // 12.3 int a28 , // 12.3 a29; struct misra_12_3_s2 a30 = {1, 2}, a31; // 12.3 struct misra_12_3_s2 a32, a33; // 12.3 struct misra_12_3_s2 a34, a35 = {1, 2}, a36; // 12.3 int a37 = MISRA_12_3_FN3_1(a34, a35), a38; // 12.3 int a39, a40 = MISRA_12_3_FN3_1(a34, a35); // 12.3 int a41 = MISRA_12_3_FN3_2(a34, a35), a42; // 12.3 int a43, a44 = MISRA_12_3_FN3_2(a34, a35); // 12.3 MISRA_12_3_FN3_2_MSG(fprintf(stderr, "test\n")); // 12.3 f((1,2),3); // TODO for (i=0; i<10; i++, j++){} // 12.3 for (int i = 0, p = &a1; // 12.3 14.2 i < 42; ++i, ++p ) // 12.3 {} // No false positives in local and extern function calls misra_12_3_fn4(misra_12_3_fn5(&a1, 32), &a1); misra_12_3_fn4(misra_12_3_fn7(&a1, 32), &a1); misra_12_3_fn6(misra_12_3_fn5(&a1, 32), &a1); misra_12_3_fn6(misra_12_3_fn7(&a1, 32), &a1); } #define MISRA12_4a 2000000000u #define MISRA12_4b 4000000000u void misra_12_4() { uint32_t x; bool t; x = 123456u * 123456u; // 12.4 x = MISRA12_4a + MISRA12_4b; // 12.4 x = 0u - 1u; // 12.4 x = t ? 0u : (0u-1u); // 12.4 x = 556200230913ULL; } struct misra_13_1_t { int a; int b; }; void misra_13_1(int *p) { volatile int v; int a1[3] = {0, (*p)++, 2}; // 13.1 int a2[3] = {0, ((*p) += 1), 2}; // 13.1 int a3[3] = {0, ((*p) = 19), 2}; // 13.1 int b[2] = {v,1}; struct misra_13_1_t c = { .a=4, .b=5 }; // no fp volatile int vv; int v = 42; int a1[3] = { 0, (*p)++, 2 }; // 13.1 int a2[2] = { [0]=19, [1]=42 }; int a3[2] = { [0]=v, [1]=42 }; int a4[2] = { [0]=0, [1]=(v+=1) }; // 13.1 int a5[2] = { [0]=0, [1]=(v+1) }; int a6[2] = { v, 1 }; int a6[2] = { v >>= 3 }; // 13.1 int a7[2] = { v, ++v }; // 13.1 int a8[1] = { vv }; // TODO: 13.1 Trac #9504 struct misra_13_1_t c01 = { 4, 5 }; struct misra_13_1_t c02 = { 16 == 1, 5+1 }; struct misra_13_1_t c03 = { (v += 1), 5+1 }; // 13.1 struct misra_13_1_t c04 = { v <<= 1, 5+1 }; // 13.1 struct misra_13_1_t c05 = { v += 1, 5+1 }; // 13.1 struct misra_13_1_t c06 = { (4.5 + 0.5), 1 }; struct misra_13_1_t c07 = { (4.5 + 0.5), ++v }; // 13.1 struct misra_13_1_t c08 = { (int)4.5, 5 }; struct misra_13_1_t c09 = { (int)4.5+(*p)++, 5 }; // 13.1 struct misra_13_1_t c10 = { (int)4.5, (*p)++ }; // 13.1 struct misra_13_1_t c11 = { .a=4+1, .b=3/3 }; struct misra_13_1_t c12 = { .a=4, .b=5 }; struct misra_13_1_t c13 = { (*v)<<=(int)(4.5), .b=5 }; // 13.1 struct misra_13_1_t c14 = { (*p)/=(int)(4.5) }; // 13.1 } void misra_13_3() { x = y++; // 13.3 } #define STRING_DEF_13_4 "This is a string" typedef struct { char string[sizeof(STRING_DEF_13_4)]; } s13_4_t; static s13_4_t s13_4 = { .string = STRING_DEF_13_4 // no-warning }; void misra_13_4() { if (x != (y = z)) {} // 13.4 else {} } void misra_13_5() { if (x && (y++ < 123)){} // 13.5 if (x || ((y += 19) > 33)){} // 13.5 if (x || ((y = 25) > 33)){} // 13.5 13.4 if (x || ((--y) > 33)){} // 13.5 else {} } void misra_13_6() { int a = sizeof(x|=42); // 13.6 a = sizeof(--x); // 13.6 13.3 return sizeof(x++); // 13.6 } void misra_14_1() { for (float f=0.1f; f<1.0f; f += 0.1f){} // 14.1 float a = 0.0f; int b = 10; while ((a<100.0f) || (b > 100)) //14.1 { a++; } do { ; } while ( a < 10.0f ); // no-warning } void misra_14_2_init_value(int32_t *var) { *var = 0; } void misra_14_2_fn1(bool b) { for (;i++<10;) {} // 14.2 for (;i<10;dostuff()) {} // TODO int32_t g = 0; int g_arr[42]; g += 2; // no-warning for (int32_t i2 = 0; i2 < 8; ++i2) { i2 += 2; // 14.2 i2 |= 2; // 14.2 g += 2; i2 ^= 2; // 14.2 if (i2 == 2) { g += g_arr[i2]; } misra_14_2_init_value(&i2); // TODO: Fix false negative in function call } for (misra_14_2_init_value(&i); i < 10; ++i) {} // no-warning FIXME: False positive for 14.2 Trac #9491 bool abort = false; for (i = 0; (i < 10) && !abort; ++i) { // no-warning if (b) { abort = true; } } for (;;) {} // no-warning int x = 10; for (int i = x; i < 42; i++) { x++; // no warning } for (int i = (x - 3); i < 42; i++) { x ^= 3; // no warning } for (int i = 0, j = 19; i < 42; i++) { // 12.3 14.2 i += 12; // 14.2 j /= 3; // TODO: 14.2 } for (int i = 0; i < 19; i++) { for (int j = 0; j < 42; j++) { i--; // 14.2 for (int k = j; k > 5; k--) { i++; // 14.2 for (int h = 35; h > 5; k++) // 14.2 {} } } } } static void misra_14_2_fn2() { int y = 0; // Handle cases when i is not treated as loop counter according MISRA // definition. for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3 i++; // no warning } for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3 i++; // no warning } for (int i = 0; y < 10; y++) { // TODO: 14.2 i++; // no warning } for (int i = 0; i < 10; y++) { // TODO: 14.2 i++; // no warning } for (int i = 0; y < 10; i++) { // TODO: 14.2 i++; // no warning } for (int i = 0; i < 10; (y+=i)) { i++; // no warning } // i is a loop counter according MISRA definition for (int i = 0; i < 10; i++) { i++; // 14.2 if (++i > 5) { // 14.2 break; } } for (int i = 0; i < 10; (i+=42)) { i++; // 14.2 } for (int i = 0; i < 10; (i|=y)) { i++; // 14.2 } return 0; } struct { unsigned int x:1; unsigned int y:1; } r14_4_struct; void misra_14_4(bool b) { if (x+4){} // 14.4 else {} if (b) {} else {} if (r14_4_struct.x) {} } void misra_15_1() { goto a1; // 15.1 a1: } void misra_15_2() { label: goto label; // 15.2 15.1 } void misra_15_3() { if (x!=0) { goto L1; // 15.3 15.1 if (y!=0) { L1: } else {} } else {} } int misra_15_5() { if (x!=0) { return 1; // 15.5 } else {} return 2; } void misra_15_6() { if (x!=0); // 15.6 else{} #if A>1 // no-warning (void)0; #endif do {} while (x<0); // no-warning } void misra_15_7() { uint32_t var = 0; uint32_t var2 = 0; if (x!=0){} // no-warning if (x!=0){} else if(x==1){} // 15.7 if (x!=0){} else if(x==1){}else{;} // no-warning if (x!=0) { } else { var = 5u; if (var != 5u) { var2 = 10u; } // no-warning } if (a==2) {} else if (b==4) {} // 15.7 if (a==2) {} else { if (b==4) {} } // no-warning } void misra_16_2() { switch (x) { default: break; case 1: while (y>4) { case 2: break; // 16.2 } break; } } void misra_16_3() { switch (x) { case 1: case 2: a=1; case 3: // 16.3 a=2; // fallthrough case 5: break; case 7: a=3; [[fallthrough]]; case 8: a=4; break; case 9: if (a==b) { break; } case 10: // 16.3 return; // 15.5 case 11: { break; } case 12: default: break; } switch (x) { case 1: // comment 1 { a = 1; break; } case 2: // comment 2 { a = 2; break; } default: { break; } } switch (x) { case 1: break; default: // 16.5 x++; case 19: // 16.3 break; case 20: x + 2; x + 3; break; } switch (x) { // 16.6 default:; } // 16.3 switch (x) { default:; } // 16.3 16.6 switch (x) { case 20: x + 2; x + 3; break; case 21: x + 2; x + 3; break; default: ; } // 16.3 switch (x) { // 16.4 16.6 case 1: x++; break; case 2: x++; } // 16.3 } void misra_16_4() { switch (x) { // 16.4 case 1: break; case 2: break; } } void misra_16_5() { switch (x) { case 1: break; default: // 16.5 break; case 2: break; } } void misra_16_6() { switch (x) { // 16.6 default: break; } switch (x) { case 1: break; case 2: break; default: break; } // No 16 6 in this switch: switch (x) { case A: return 1; // 15.5 case B: return 1; // 15.5 case C: return 1; // 15.5 default: return 2; // 15.5 } } void misra_16_7() { switch (x != 123) { // 16.7 case 1: break; default: break; } } void misra_17_1() { va_list(); // 17.1 va_arg(); // 17.1 va_start(); // 17.1 va_end(); // 17.1 va_copy(); // 17.1 } void misra_17_2_ok_1(void) { ; } void misra_17_2_ok_2(void) { misra_17_2_ok_1(); // no-warning } void misra_17_2_1(void) { misra_17_2_ok_1(); // no-warning misra_17_2_1(); // 17.2 misra_17_2_ok_2(); // no-warning misra_17_2_1(); // 17.2 } void misra_17_2_2(void) { misra_17_2_3(); // 17.2 } void misra_17_2_3(void) { misra_17_2_4(); // 17.2 } void misra_17_2_4(void) { misra_17_2_2(); // 17.2 misra_17_2_3(); // 17.2 } void misra_17_2_5(void) { misra_17_2_ok_1(); // no-warning misra_17_2_5(); // 17.2 misra_17_2_1(); // no-warning } void misra_17_6(int x[static 20]) {(void)x;} // 17.6 int calculation(int x) { return x + 1; } void misra_17_7(void) { calculation(123); // 17.7 } void misra_17_8(int x) { x = 3; // 17.8 } void misra_18_4() { int b = 42; int *bp = &b; bp += 1; // 18.4 bp -= 2; // 18.4 int *p = bp - 2; // 18.4 int *ab = &b + 1; // 18.4 p = bp + p; // 18.4 bp = 1 + p + 1; // 18.4 b += 19; // no-warning b = b + 9; // no-warning } void misra_18_5() { int *** p; // 18.5 } struct { uint16_t len; struct { uint8_t data_1[]; // 18.7 } nested_1; struct named { struct { uint8_t len_1; uint32_t data_2[]; // 18.7 } nested_2; uint8_t data_3[]; // 18.7 } nested_3; } r18_7_struct; struct { uint16_t len; uint8_t data_1[ 19 ]; uint8_t data_2[ ]; // 18.7 } r18_7_struct; void misra_18_8(int x) { int buf1[10]; int buf2[sizeof(int)]; int vla[x]; // 18.8 static const unsigned char arr18_8_1[] = UNDEFINED_ID; } union misra_19_2 { }; // 19.2 #include "notfound.h" // 20.1 #define int short // 20.4 #undef X // 20.5 #define M_20_7_1(A) (A+1) // 20.7 #define M_20_7_2(A,B) (1+AB+2) // no warning #define M_20_7_3(A) ((A)+A) // 20.7 #define M_20_7_4(A) x##A // 20.10 this test was written to see there are not FPs #define M_20_7_5(A,B) f(A, B) // no warning #define M_20_10(a) (#a) // 20.10 #else1 // 20.13 #ifdef A>1 # define somethingis 5 // no warning # define func_20_13(v) (v) // no warning #else # definesomethingis 6 // 20.13 # def fun_2013(v) () // 20.13 #endif #define _Incompatible 0xdeadbeef // 21.1 #define __Incompatible 0xdeadbeef // 21.1 #define __starts_with_lower 0xdeadbeef // 21.1 #define __MY_HEADER_ // 21.1 #define _macro_starts_with_lower 1 // no warning static int _file_scope_id_21_1 = 42; // no warning static int _file_scope_id_21_1_fn() { return 42; } // no warning static int __file_scope_id_21_1 = 42; // 21.1 static int __file_scope_id_21_1_fn() { return 42; } // 21.1 static int _File_scope_id_21_1 = 42; // 21.1 static int _File_scope_id_21_1_fn() { return 42; } // 21.1 int _external_scope_id_21_1 = 42; // 21.1 int _external_scope_id_21_1_fn() { return 42; } // 21.1 int __external_scope_id_21_1 = 42; // 21.1 int __external_scope_id_21_1_fn() { return 42; } // 21.1 int _External_scope_id_21_1 = 42; // 21.1 int _External_scope_id_21_1_fn() { return 42; } // 21.1 int errno = 42; // 21.1 5.5 int misra_21_1() { int _a = 42; // 21.1 errno = EINVAL; // no warning _a ++; // no warning _exit(1); // no warning return _a; // no warning } int _misra_21_1_1(); // 21.1 static int _misra_21_1_2(); // no warning #define errno 11 // 21.1 struct _struct_21_1 { int a; }; // 21.1 struct _Struct_21_1 { int a; }; // 21.1 struct __struct_21_1 { int a; }; // 21.1 typedef struct { int a; } _struct_21_1_t; // 21.1 typedef struct { int a; } _Struct_21_1_t; // 21.1 typedef struct { int a; } __struct_21_1_t; // 21.1 enum _enum_21_1 { ENUM211_1 }; // 21.1 enum _Enum_21_1 { ENUM211_2 }; // 21.1 enum __enum_21_1 { ENUM211_3 }; // 21.1 enum __enum_21_1 { ENUM211_3 }; // 21.1 typedef enum { ENUM211_4 } _enum_21_1_t; // 21.1 typedef enum { ENUM211_5 } _Enum_21_1_t; // 21.1 typedef enum { ENUM211_6 } __enum_21_1_t; // 21.1 enum enum_21_1_valid_id { ENUM211_7, _ENUM211_8, // 21.1 __ENUM211_9, // 21.1 _eNUM211_10, // 21.1 enum211_11 }; union _union_21_1 { int a; }; // 21.1 19.2 union _Union_21_1 { int a; }; // 21.1 19.2 union __union_21_1 { int a; }; // 21.1 19.2 typedef union { int a; } _union_21_1_t; // 21.1 19.2 typedef union { int a; } _Union_21_1_t; // 21.1 19.2 typedef union { int a; } __union_21_1_t; // 21.1 19.2 void misra_21_3() { p1=malloc(10); // 21.3 p2=calloc(10); // 21.3 realloc(10); // 21.3 free(p1); // 21.3 } void misra_21_7() { (void)atof(str); // 21.7 (void)atoi(str); // 21.7 (void)atol(str); // 21.7 (void)atoll(str); // 21.7 } void misra_21_8() { abort(); // 21.8 (void)getenv("foo"); // 21.8 (void)system(""); // 21.8 exit(-1); // 21.8 } void misra_21_9() { (void)bsearch(key,base,num,size,cmp); // 21.9 qsort(base,num,size,cmp); // 21.9 } void misra_21_12() { int rc; fexcept_t f; // 21.12 rc = feclearexcept(1); // 21.12 rc = fegetexceptflag(&f, 1); // 21.12 rc = feraiseexcept(1); // 21.12 rc = fesetexceptflag(&f, 1); // 21.12 rc = fetestexcept(1); // 21.12 } // Large arrays for R13.1. Size exceeds default Python's max recursion depth. static uint8_t misra_13_1_large_ok[1024] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static uint8_t misra_13_1_large_bad[1024] = { // 13.1 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, i++, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; cppcheck-1.90/addons/test/misra/misra-test.cpp000066400000000000000000000004711357737443600214060ustar00rootroot00000000000000// #8441 class C { int a; int b; C() : a(1), b(1) { c; } }; class misra_21_1_C { public: misra_21_1_C operator=(const misra_21_1_C &); }; class C2 { public: C2(); private: void* f; }; C2::C2() : f(NULL) {} static bool test_misra_21_1_crash() { auto misra_21_1_C a, b; a = b; } cppcheck-1.90/addons/test/misra/misra-test.h000066400000000000000000000001401357737443600210440ustar00rootroot00000000000000#ifndef MISRA_TEST_H #define MISRA_TEST_H struct misra_h_s { int foo; }; #endif // MISRA_TEST_H cppcheck-1.90/addons/test/misra/misra2012_rules_dummy_ascii.txt000066400000000000000000000001251357737443600245640ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Text of rule 1.1 Rule 1.2 Text of rule 1.2 cppcheck-1.90/addons/test/misra/misra2012_rules_dummy_utf8.txt000066400000000000000000000001451357737443600243640ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Text of rule 1.1, utf8 test: ∑ Rule 1.2 Text of rule 1.2 cppcheck-1.90/addons/test/misra/misra2012_rules_dummy_windows1250.txt000066400000000000000000000001521357737443600254760ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Text of rule 1.1, windows1250 test: Rule 1.2 Text of rule 1.2 cppcheck-1.90/addons/test/misra/misra_rules_dummy.txt000066400000000000000000000005141357737443600231110ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 3.1 Required R3.1 text. Rule 4.1 Required R4.1 text. Rule 10.4 Mandatory R10.4 text. Rule 11.5 Advisory R11.5 text. Rule 15.5 Advisory R15.5 text. Rule 15.6 Required R15.6 text. Rule 17.7 Required R17.7 text. Rule 20.1 Advisory R20.1 text. Rule 21.3 Required R21.3 text. Rule 21.4 R21.4 text. cppcheck-1.90/addons/test/misra/misra_rules_empty_lines.txt000066400000000000000000000003231357737443600243040ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Add this rule and parse to next, skipping empty lines. Rule 1.2 Rule text. Rule 1.3 There is 3 rules. cppcheck-1.90/addons/test/misra/misra_rules_multiple_lines.txt000066400000000000000000000004061357737443600250030ustar00rootroot00000000000000Appendix A Summary of guidelines Rule 1.1 Multiple lines text. Rule 1.2 Multiple lines text. Rule 1.3 Required Multiple lines text. Rule 1.4 Should Starts from lowercase letter. Rule 1.5 Should starts from lowercase letter. Rule 1.6 Can contain empty lines. cppcheck-1.90/addons/test/misra/misra_rules_structure.txt000066400000000000000000000003341357737443600240160ustar00rootroot00000000000000Here can be any text. Incorrect definitions: Appendix A Appendix A Summary: Rule 1.1 Error! Here we go: Appendix A Summary of guidelines Rule 1.2 Rule text. Stop parsing after this line: Appendix B Rule 1.3 Error! cppcheck-1.90/addons/test/misra/suppressions.txt000066400000000000000000000003241357737443600221250ustar00rootroot00000000000000misra_21.6:misra-suppressions1-test.c:7 misra_14_4 misra.5.2 MISRA_16_4:misra-suppressions1-test.c MISRA.16.6:misra-suppressions1-test.c MISRA_4_1:misra-suppressions2-test.c MISRA.19_2:misra-suppressions2-test.c cppcheck-1.90/addons/test/naming_test.c000066400000000000000000000003241357737443600201500ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump naming_test.c && python ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump // Should not crash when there is no name void func(int number, bool); cppcheck-1.90/addons/test/naming_test.cpp000066400000000000000000000004701357737443600205120ustar00rootroot00000000000000// To test: // ~/cppcheck/cppcheck --dump naming_test.cpp && python ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump // No error for mismatching Constructor/Destructor names should be issued, they can not be changed. class TestClass1 { TestClass1() {} ~TestClass1() {} }; cppcheck-1.90/addons/test/namingng_test.c000066400000000000000000000012001357737443600204670ustar00rootroot00000000000000#include #include uint32_t ui32Good (int a) { uint32_t ui32good; int32_t i32good; uint32_t badui32; int32_t badi32; uint32_t a; // Short return 5; } uint16_t ui16Good (int a) { return 5; } uint16_t ui16bad_underscore (int a) { return 5; } uint32_t u32Bad (int a) { uint32_t ui32good; int32_t i32good; uint32_t badui32; int32_t badi32; int * intpointer=NULL; int ** intppointer=NULL; int *** intpppointer=NULL; return 5; } uint16_t Badui16 (int a) { return 5; } void * Pointer() { return NULL; } void ** PPointer() { return NULL; } cppcheck-1.90/addons/test/path1/000077500000000000000000000000001357737443600165125ustar00rootroot00000000000000cppcheck-1.90/addons/test/path1/misra-suppressions1-test.c000066400000000000000000000016351357737443600236070ustar00rootroot00000000000000// To test: // ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump // There should be no violations reported // This needs to stay at line number 7 to make the test pass // If it is changed update suppressions.txt with the new line number #include //21.6 extern int misra_5_2_var_hides_var______31x; static int misra_5_2_var_hides_var______31y;//5.2 static int misra_5_2_function_hides_var_31x; void misra_5_2_function_hides_var_31y(void) {}//5.2 void foo(void) { int i; switch(misra_5_2_func1()) //16.4 16.6 { case 1: { do { for(i = 0; i < 10; i++) { if(misra_5_2_func3()) //14.4 { int misra_5_2_var_hides_var_1____31x; int misra_5_2_var_hides_var_1____31y;//5.2 } } } while(misra_5_2_func2()); //14.4 } } } cppcheck-1.90/addons/test/path1/misra-suppressions2-test.c000066400000000000000000000010221357737443600235760ustar00rootroot00000000000000// To test: // ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump // There should be no violations reported union misra_5_2_field_hides_field__63x { //19.2 int misra_5_2_field_hides_field__31x; int misra_5_2_field_hides_field__31y;//5.2 }; struct misra_5_2_field_hides_field__63y { //5.2 int misra_5_2_field_hides_field1_31x; int misra_5_2_field_hides_field1_31y;//5.2 }; const char *s41_1 = "\x41g"; // 4.1 const char *s41_2 = "\x41\x42"; cppcheck-1.90/addons/test/test-cert.py000066400000000000000000000017071357737443600177660ustar00rootroot00000000000000# Running the test with Python 2: # Be sure to install pytest version 4.6.4 (newer should also work) # Command in cppcheck directory: # python -m pytest addons/test/test-cert.py # # Running the test with Python 3: # Command in cppcheck directory: # PYTHONPATH=./addons python3 -m pytest addons/test/test-cert.py import sys import pytest def test_arguments_regression(): args_ok = ["-q", "--quiet", "-verify", "--cli"] # Arguments with expected SystemExit args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"] from addons.cert import get_args for arg in args_exit: sys.argv.append(arg) with pytest.raises(SystemExit): get_args() sys.argv.remove(arg) for arg in args_ok: sys.argv.append(arg) try: get_args() except SystemExit: pytest.fail("Unexpected SystemExit with '%s'" % arg) sys.argv.remove(arg) cppcheck-1.90/addons/test/test-misra.py000066400000000000000000000114421357737443600201410ustar00rootroot00000000000000# Running the test with Python 2: # Be sure to install pytest version 4.6.4 (newer should also work) # Command in cppcheck directory: # python -m pytest addons/test/test-misra.py # # Running the test with Python 3: # Command in cppcheck directory: # PYTHONPATH=./addons python3 -m pytest addons/test/test-misra.py import pytest import re import sys import subprocess from .util import dump_create, dump_remove, convert_json_output TEST_SOURCE_FILES = ['./addons/test/misra/misra-test.c'] def setup_module(module): for f in TEST_SOURCE_FILES: dump_create(f) def teardown_module(module): for f in TEST_SOURCE_FILES: dump_remove(f) @pytest.fixture(scope="function") def checker(): from addons.misra import MisraChecker, MisraSettings, get_args args = get_args() settings = MisraSettings(args) return MisraChecker(settings) def test_loadRuleTexts_structure(checker): checker.loadRuleTexts("./addons/test/misra/misra_rules_structure.txt") assert(checker.ruleTexts.get(101, None) is None) assert(checker.ruleTexts[102].text == "Rule text.") assert(checker.ruleTexts.get(103, None) is None) def test_loadRuleTexts_empty_lines(checker): checker.loadRuleTexts("./addons/test/misra/misra_rules_empty_lines.txt") assert(len(checker.ruleTexts) == 3) assert(len(checker.ruleTexts[102].text) == len("Rule text.")) def test_loadRuleTexts_mutiple_lines(checker): checker.loadRuleTexts("./addons/test/misra/misra_rules_multiple_lines.txt") assert(checker.ruleTexts[101].text == "Multiple lines text.") assert(checker.ruleTexts[102].text == "Multiple lines text.") assert(checker.ruleTexts[103].text == "Multiple lines text.") assert(checker.ruleTexts[104].text == "Should") assert(checker.ruleTexts[105].text == "Should") assert(checker.ruleTexts[106].text == "Can contain empty lines.") def test_verifyRuleTexts(checker, capsys): checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") checker.verifyRuleTexts() captured = capsys.readouterr().out assert("21.3" not in captured) assert("1.3" in captured) def test_rules_misra_severity(checker): checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") assert(checker.ruleTexts[1004].misra_severity == 'Mandatory') assert(checker.ruleTexts[401].misra_severity == 'Required') assert(checker.ruleTexts[1505].misra_severity == 'Advisory') assert(checker.ruleTexts[2104].misra_severity == '') def test_json_out(checker, capsys): sys.argv.append("--cli") checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") checker.parseDump("./addons/test/misra/misra-test.c.dump") captured = capsys.readouterr() captured = captured.out.splitlines() sys.argv.remove("--cli") json_output = convert_json_output(captured) assert("Mandatory" in json_output['c2012-10.4'][0]['extra']) assert("Required" in json_output['c2012-21.3'][0]['extra']) assert("Advisory" in json_output['c2012-20.1'][0]['extra']) def test_rules_cppcheck_severity(checker, capsys): checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") checker.parseDump("./addons/test/misra/misra-test.c.dump") captured = capsys.readouterr().err assert("(error)" not in captured) assert("(warning)" not in captured) assert("(style)" in captured) def test_rules_suppression(checker, capsys): test_sources = ["addons/test/misra/misra-suppressions1-test.c", "addons/test/misra/misra-suppressions2-test.c"] for src in test_sources: re_suppressed= r"\[%s\:[0-9]+\]" % src dump_remove(src) dump_create(src, "--suppressions-list=addons/test/misra/suppressions.txt") checker.parseDump(src + ".dump") captured = capsys.readouterr().err found = re.search(re_suppressed, captured) assert(found is None) dump_remove(src) def test_arguments_regression(): args_ok = ["-generate-table", "--rule-texts=./addons/test/assets/misra_rules_multiple_lines.txt", "--verify-rule-texts", "-t=foo", "--template=foo", "--suppress-rules=15.1", "--quiet", "--cli", "--no-summary", "--show-suppressed-rules", "-P=src/", "--file-prefix=src/"] # Arguments with expected SystemExit args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"] from addons.misra import get_args for arg in args_exit: sys.argv.append(arg) with pytest.raises(SystemExit): get_args() sys.argv.remove(arg) for arg in args_ok: sys.argv.append(arg) try: get_args() except SystemExit: pytest.fail("Unexpected SystemExit with '%s'" % arg) sys.argv.remove(arg) cppcheck-1.90/addons/test/test-y2038.py000066400000000000000000000110621357737443600176110ustar00rootroot00000000000000# Running the test with Python 2: # Be sure to install pytest version 4.6.4 (newer should also work) # Command in cppcheck directory: # python -m pytest addons/test/test-y2038.py # # Running the test with Python 3: # Command in cppcheck directory: # PYTHONPATH=./addons python3 -m pytest addons/test/test-y2038.py import sys import pytest from addons.y2038 import check_y2038_safe from .util import dump_create, dump_remove, convert_json_output TEST_SOURCE_FILES = ['./addons/test/y2038/y2038-test-1-bad-time-bits.c', './addons/test/y2038/y2038-test-2-no-time-bits.c', './addons/test/y2038/y2038-test-3-no-use-time-bits.c', './addons/test/y2038/y2038-test-4-good.c', './addons/test/y2038/y2038-test-5-good-no-time-used.c'] def setup_module(module): sys.argv.append("--cli") for f in TEST_SOURCE_FILES: dump_create(f) def teardown_module(module): sys.argv.remove("--cli") for f in TEST_SOURCE_FILES: dump_remove(f) def test_1_bad_time_bits(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-1-bad-time-bits.c.dump', quiet=True) assert(is_safe is False) captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # Has exactly one warnings of _TIME_BITS and _USE_TIME_BITS64 kind. assert(len(json_output['type-bits-undef']) == 1) assert(len(json_output['type-bits-not-64']) == 1) # There are 2 unsafe calls in test source and 3 in y2038-in.h unsafe_calls = json_output['unsafe-call'] assert(len([c for c in unsafe_calls if c['file'].endswith('h')]) == 3) assert(len([c for c in unsafe_calls if c['file'].endswith('c')]) == 0) def test_2_no_time_bits(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-2-no-time-bits.c.dump', quiet=True) assert(is_safe is False) captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # _USE_TIME_BITS64 defined in y2038-inc.h header, but there is not # _TIME_BITS definition. Here must be appropriate warning. assert(len(json_output['type-bits-undef']) == 1) assert(json_output.get('type-bits-not-64') is None) # y2038-in.h still has y2038-unsafe calls. unsafe_calls = json_output['unsafe-call'] assert(len([c for c in unsafe_calls if c['file'].endswith('h')]) == 3) def test_3_no_use_time_bits(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-3-no-use-time-bits.c.dump', quiet=True) assert(is_safe is False) captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # Included bad _USE_TIME_BITS64 definition must trigger the errors. unsafe_calls = json_output['unsafe-call'] assert(len(unsafe_calls) == 2) def test_4_good(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-4-good.c.dump', quiet=True) # assert(is_safe is True) # FIXME: This should be a "good" example returning "True" instead of "False" captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # Defined _TIME_BITS equal to 64 so that glibc knows we want Y2038 support. # There are no warnings from C sources. unsafe_calls = json_output['unsafe-call'] assert(len([c for c in unsafe_calls if c['file'].endswith('.c')]) == 0) def test_5_good(capsys): is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-5-good-no-time-used.c.dump', quiet=True) assert(is_safe is True) captured = capsys.readouterr() captured = captured.out.splitlines() json_output = convert_json_output(captured) # There are no warnings from C sources. if 'unsafe-call' in json_output: unsafe_calls = json_output['unsafe-call'] assert(len([c for c in unsafe_calls if c['file'].endswith('.c')]) == 0) def test_arguments_regression(): args_ok = ["-t=foo", "--template=foo", "-q", "--quiet", "--cli"] # Arguments with expected SystemExit args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"] from addons.y2038 import get_args for arg in args_exit: sys.argv.append(arg) with pytest.raises(SystemExit): get_args() sys.argv.remove(arg) for arg in args_ok: sys.argv.append(arg) try: get_args() except SystemExit: pytest.fail("Unexpected SystemExit with '%s'" % arg) sys.argv.remove(arg) cppcheck-1.90/addons/test/util.py000066400000000000000000000022671357737443600170330ustar00rootroot00000000000000# Helpers for pytest tests import subprocess import json import os def find_cppcheck_binary(): possible_locations = [ "./cppcheck", r".\bin\cppcheck.exe", ] for location in possible_locations: if os.path.exists(location): break else: raise RuntimeError("Could not fine cppcheck binary") return location def dump_create(fpath, *argv): cppcheck_binary = find_cppcheck_binary() cmd = [cppcheck_binary, "--dump", "--quiet", fpath] + list(argv) p = subprocess.Popen(cmd) p.communicate() if p.returncode != 0: raise OSError("cppcheck returns error code: %d" % p.returncode) subprocess.Popen(["sync"]) def dump_remove(fpath): subprocess.Popen(["rm", "-f", fpath + ".dump"]) def convert_json_output(raw_json_strings): """Convert raw stdout/stderr cppcheck JSON output to python dict.""" json_output = {} for line in raw_json_strings: try: json_line = json.loads(line) # json_output[json_line['errorId']] = json_line json_output.setdefault(json_line['errorId'], []).append(json_line) except ValueError: pass return json_output cppcheck-1.90/addons/test/y2038/000077500000000000000000000000001357737443600162625ustar00rootroot00000000000000cppcheck-1.90/addons/test/y2038/y2038-inc.h000066400000000000000000000007771357737443600200020ustar00rootroot00000000000000#ifndef __INC2038 #define _INC2038 /* * This file defines _USE_TIME_BITS64. * It plays the role of a Y2038-proof glibc. */ #define _USE_TIME_BITS64 /* * Declare just enough for clock_gettime */ typedef int clockid_t; typedef int __time_t; typedef long int __syscall_slong_t; struct timespec { __time_t tv_sec; /* Seconds. */ __syscall_slong_t tv_nsec; /* Nanoseconds. */ }; extern int clock_gettime(clockid_t clk_id, struct timespec *tp); #define CLOCK_REALTIME 0 #endif /* INC2038 */ cppcheck-1.90/addons/test/y2038/y2038-test-1-bad-time-bits.c000066400000000000000000000004521357737443600227460ustar00rootroot00000000000000#include #include /* * Define _TIME_BITS unequal to 64 to trigger error */ #define _TIME_BITS 62 #include "y2038-inc.h" int main(int argc, char **argv) { clockid_t my_clk_id = CLOCK_REALTIME; struct timespec *my_tp; return clock_gettime(my_clk_id, &my_tp); } cppcheck-1.90/addons/test/y2038/y2038-test-2-no-time-bits.c000066400000000000000000000004351357737443600226360ustar00rootroot00000000000000#include #include /* * Do not define _TIME_BITS but have _USE_TIME_BITS64 defined */ #include "y2038-inc.h" int main(int argc, char **argv) { clockid_t my_clk_id = CLOCK_REALTIME; struct timespec *my_tp; return clock_gettime(my_clk_id, &my_tp); } cppcheck-1.90/addons/test/y2038/y2038-test-3-no-use-time-bits.c000066400000000000000000000004321357737443600234260ustar00rootroot00000000000000#include #include /* * Include bad _USE_TIME_BITS64 definition to trigger error */ #define _TIME_BITS 64 int main(int argc, char **argv) { clockid_t my_clk_id = CLOCK_REALTIME; struct timespec *my_tp; return clock_gettime(my_clk_id, &my_tp); } cppcheck-1.90/addons/test/y2038/y2038-test-4-good.c000066400000000000000000000004331357737443600212570ustar00rootroot00000000000000/* * Define _TIME_BITS equal to 64 so that glibc knows we want Y2038 support. */ #define _TIME_BITS 64 #include "y2038-inc.h" int main(int argc, char **argv) { clockid_t my_clk_id = CLOCK_REALTIME; struct timespec *my_tp; return clock_gettime(my_clk_id, &my_tp); } cppcheck-1.90/addons/test/y2038/y2038-test-5-good-no-time-used.c000066400000000000000000000003301357737443600235600ustar00rootroot00000000000000/* * C file that does not use any time functionality -> no errors should * be reported. */ #include int main(int argc, char **argv) { if (argc > 1) { printf("Hello"); } return 0; } cppcheck-1.90/addons/threadsafety.py000077500000000000000000000022641357737443600175620ustar00rootroot00000000000000#!/usr/bin/env python3 # # This script analyses Cppcheck dump files to locate threadsafety issues # - warn about static local objects # import cppcheckdata import sys def reportError(token, severity, msg, id): cppcheckdata.reportError(token, severity, msg, 'threadsafety', id) def checkstatic(data): for var in data.variables: if var.isStatic and var.isLocal: type = None if var.isClass: type = 'object' else: type = 'variable' if var.isConst: reportError(var.typeStartToken, 'warning', 'Local constant static ' + type + ' \'' + var.nameToken.str + '\', dangerous if it is initialized in parallel threads', 'threadsafety') else: reportError(var.typeStartToken, 'warning', 'Local static ' + type + ': ' + var.nameToken.str, 'threadsafety') for arg in sys.argv[1:]: if arg.startswith('-'): continue print('Checking ' + arg + '...') data = cppcheckdata.parsedump(arg) for cfg in data.configurations: if len(data.configurations) > 1: print('Checking ' + arg + ', config "' + cfg.name + '"...') checkstatic(cfg) cppcheck-1.90/addons/y2038.py000077500000000000000000000164101357737443600156620ustar00rootroot00000000000000#!/usr/bin/env python3 # # cppcheck addon for Y2038 safeness detection # # Detects: # # 1. _TIME_BITS being defined to something else than 64 bits # 2. _USE_TIME_BITS64 being defined when _TIME_BITS is not # 3. Any Y2038-unsafe symbol when _USE_TIME_BITS64 is not defined. # # Example usage: # $ cppcheck --dump path-to-src/test.c # $ y2038.py path-to-src/test.c.dump # # y2038.py will walk the source tree for .dump files. from __future__ import print_function import cppcheckdata import sys import os import re # -------------------------------------------- # #define/#undef detection regular expressions # -------------------------------------------- # test for '#define _TIME_BITS 64' re_define_time_bits_64 = re.compile(r'^\s*#\s*define\s+_TIME_BITS\s+64\s*$') # test for '#define _TIME_BITS ...' (combine w/ above to test for 'not 64') re_define_time_bits = re.compile(r'^\s*#\s*define\s+_TIME_BITS\s+.*$') # test for '#undef _TIME_BITS' (if it ever happens) re_undef_time_bits = re.compile(r'^\s*#\s*undef\s+_TIME_BITS\s*$') # test for '#define _USE_TIME_BITS64' re_define_use_time_bits64 = re.compile(r'^\s*#\s*define\s+_USE_TIME_BITS64\s*$') # test for '#undef _USE_TIME_BITS64' (if it ever happens) re_undef_use_time_bits64 = re.compile(r'^\s*#\s*undef\s+_USE_TIME_BITS64\s*$') # -------------------------------- # List of Y2038-unsafe identifiers # -------------------------------- # This is WIP. Eventually it should contain all identifiers (types # and functions) which would be affected by the Y2038 bug. id_Y2038 = { # Y2038-unsafe types by definition 'time_t' # Types using Y2038-unsafe types 'lastlog', 'msqid_ds', 'semid_ds', 'timeb', 'timespec', 'timeval', 'utimbuf', 'itimerspec', 'stat', 'clnt_ops', 'elf_prstatus', 'itimerval', 'ntptimeval', 'rusage', 'timex', 'utmp', 'utmpx', # APIs using 2038-unsafe types 'ctime', 'ctime_r', 'difftime', 'gmtime', 'gmtime_r', 'localtime', 'localtime_r', 'mktime', 'stime', 'timegm', 'timelocal', 'time', 'msgctl', 'ftime', 'aio_suspend', 'clock_getres', 'clock_gettime', 'clock_nanosleep', 'clock_settime', 'futimens', 'mq_timedreceive', 'mq_timedsend', 'nanosleep', 'pselect', 'pthread_cond_timedwait', 'pthread_mutex_timedlock', 'pthread_rwlock_timedrdlock', 'pthread_rwlock_timedwrlock', 'sched_rr_get_interval', 'sem_timedwait', 'sigtimedwait', 'timespec_get', 'utimensat', 'adjtime', 'pmap_rmtcall', 'clntudp_bufcreate', 'clntudp_create', 'futimes', 'gettimeofday', 'lutimes', 'select', 'settimeofday', 'utimes', 'utime', 'timerfd_gettime', 'timerfd_settime', 'timer_gettime', 'timer_settime', 'fstatat', 'fstat', '__fxstatat', '__fxstat', 'lstat', '__lxstat', 'stat', '__xstat', 'struct itimerval', 'setitimer', 'getitimer', 'ntp_gettime', 'getrusage', 'wait3', 'wait4', 'adjtimex', 'ntp_adjtime', 'getutent_r', 'getutent', 'getutid_r', 'getutid', 'getutline_r', 'getutline', 'login', 'pututline', 'updwtmp', 'getutxent', 'getutxid', 'getutxline', 'pututxline' } def check_y2038_safe(dumpfile, quiet=False): # Assume that the code is Y2038 safe until proven otherwise y2038safe = True # load XML from .dump file data = cppcheckdata.parsedump(dumpfile) # Convert dump file path to source file in format generated by cppcheck. # For example after the following call: # cppcheck ./src/my-src.c --dump # We got 'src/my-src.c' value for 'file' field in cppcheckdata. srcfile = dumpfile.rstrip('.dump') srcfile = os.path.expanduser(srcfile) srcfile = os.path.normpath(srcfile) # go through each configuration for cfg in data.configurations: if not quiet: print('Checking ' + srcfile + ', config "' + cfg.name + '"...') safe_ranges = [] safe = -1 time_bits_defined = False srclinenr = '0' for directive in cfg.directives: # track source line number if directive.file == srcfile: srclinenr = directive.linenr # check for correct _TIME_BITS if present if re_define_time_bits_64.match(directive.str): time_bits_defined = True elif re_define_time_bits.match(directive.str): cppcheckdata.reportError(directive, 'error', '_TIME_BITS must be defined equal to 64', 'y2038', 'type-bits-not-64') time_bits_defined = False y2038safe = False elif re_undef_time_bits.match(directive.str): time_bits_defined = False # check for _USE_TIME_BITS64 (un)definition if re_define_use_time_bits64.match(directive.str): safe = int(srclinenr) # warn about _TIME_BITS not being defined if not time_bits_defined: cppcheckdata.reportError(directive, 'warning', '_USE_TIME_BITS64 is defined but _TIME_BITS was not', 'y2038', 'type-bits-undef') elif re_undef_use_time_bits64.match(directive.str): unsafe = int(srclinenr) # do we have a safe..unsafe area? if unsafe > safe > 0: safe_ranges.append((safe, unsafe)) safe = -1 # check end of source beyond last directive if len(cfg.tokenlist) > 0: unsafe = int(cfg.tokenlist[-1].linenr) if unsafe > safe > 0: safe_ranges.append((safe, unsafe)) # go through all tokens for token in cfg.tokenlist: if token.str in id_Y2038: if not any(lower <= int(token.linenr) <= upper for (lower, upper) in safe_ranges): cppcheckdata.reportError(token, 'warning', token.str + ' is Y2038-unsafe', 'y2038', 'unsafe-call') y2038safe = False token = token.next return y2038safe def get_args(): parser = cppcheckdata.ArgumentParser() return parser.parse_args() if __name__ == '__main__': args = get_args() exit_code = 0 quiet = not any((args.quiet, args.cli)) if not args.dumpfile: if not args.quiet: print("no input files.") sys.exit(0) for dumpfile in args.dumpfile: if not os.path.isfile(dumpfile): print("Error: File not found: %s" % dumpfile) sys.exit(127) if not os.access(dumpfile, os.R_OK): print("Error: Permission denied: %s" % dumpfile) sys.exit(13) if not args.quiet: print('Checking ' + dumpfile + '...') y2038safe = check_y2038_safe(dumpfile, quiet) if not y2038safe and exit_code == 0: exit_code = 1 sys.exit(exit_code) cppcheck-1.90/appveyor.yml000066400000000000000000000060501357737443600156370ustar00rootroot00000000000000version: '{build}' clone_depth: 10 matrix: fast_finish: true environment: matrix: - VisualStudioVersion: 12.0 platform: Win32 configuration: Debug vcvarsall_platform: x86 PlatformToolset: v120 - VisualStudioVersion: 12.0 platform: Win32 configuration: Release vcvarsall_platform: x86 PlatformToolset: v120 MYQTDIR: C:\Qt\5.6\msvc2013 - VisualStudioVersion: 12.0 platform: x64 configuration: Debug vcvarsall_platform: x64 PlatformToolset: v120 cygwin_build: yes - VisualStudioVersion: 12.0 platform: x64 configuration: Release vcvarsall_platform: x64 PlatformToolset: v120 MYQTDIR: C:\Qt\5.6\msvc2013_64 # FIXME: These are disabled for now. They were broken by ae8653612802b41b70424ec9a5eefe8a1178f6d1 # - VisualStudioVersion: 14.0 # platform: Win32 # configuration: Debug # vcvarsall_platform: x86 # PlatformToolset: v140 # - VisualStudioVersion: 14.0 # platform: Win32 # configuration: Release # vcvarsall_platform: x86 # PlatformToolset: v140 # MYQTDIR: C:\Qt\5.11\msvc2015 # - VisualStudioVersion: 14.0 # platform: x64 # configuration: Debug # vcvarsall_platform: x64 # PlatformToolset: v140 # That platform causes frequent errors on appveyor due to an unknown infrastructure failure # - VisualStudioVersion: 14.0 # platform: x64 # configuration: Release # vcvarsall_platform: x64 # PlatformToolset: v140 # MYQTDIR: C:\Qt\5.11\msvc2015_64 install: - pip install pytest build_script: - ECHO Building %configuration% %platform% with MSVC %VisualStudioVersion% using %PlatformToolset% PlatformToolset - 'CALL "C:\Program Files (x86)\Microsoft Visual Studio %VisualStudioVersion%\VC\vcvarsall.bat" %vcvarsall_platform%' # Visual studio project.. - msbuild "cppcheck.sln" /consoleloggerparameters:Verbosity=minimal /target:Build /property:Configuration="%configuration%";Platform=%platform% /p:PlatformToolset=%PlatformToolset% /maxcpucount /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" # cmake.. - mkdir build - cd build - cmake -DBUILD_TESTS=ON -G"NMake Makefiles" .. - nmake - copy bin\cppcheck.exe .. - cd .. # build gui.. - ECHO MYQTDIR=%MYQTDIR% - 'IF defined MYQTDIR cd gui' - 'IF defined MYQTDIR set QTDIR=%MYQTDIR%' - 'IF defined MYQTDIR %QTDIR%\bin\qmake' - 'IF defined MYQTDIR nmake' - 'IF defined MYQTDIR cd ..' test_script: - build\bin\testrunner.exe -q - IF EXIST bin\debug\testrunner.exe bin\debug\testrunner.exe - IF EXIST bin\testrunner.exe bin\testrunner.exe - cd test\cli - python -m pytest test-helloworld.py - python -m pytest test-inline-suppress.py - python -m pytest test-suppress-syntaxError.py - 'IF defined cygwin_build C:\cygwin64\bin\bash -e -l -c "cd /cygdrive/c/projects/cppcheck && make clean && make -j 2 && make test && make checkcfg"' artifacts: - path: bin name: CLI binaries type: zip - path: Build\gui name: GUI binaries type: zip cppcheck-1.90/benchmarks.txt000066400000000000000000000007531357737443600161310ustar00rootroot00000000000000 ========== Benchmarks ========== In this file we can document some good code repos / code samples to use when working on optimisations. Trac tickets ------------ http://trac.cppcheck.net/ticket/2435 -- Tokenizer::simplifyTypedef http://trac.cppcheck.net/ticket/8355 -- TokenList::createAst http://trac.cppcheck.net/ticket/9007 -- Unused types Repos ----- Small C++ library with lots of templates: https://framagit.org/dtschump/CImg Just check the file examples/use_tinymatwriter.cpp cppcheck-1.90/build-pcre.txt000066400000000000000000000035211357737443600160360ustar00rootroot00000000000000PCRE is a library that is used by the optional "rules" feature for the command line version of cppcheck. It is readily available on Linux and Mac OS X, but must be obtained separately for Windows. If you're using qmake to generate makefiles, the following behavior applies: - If you're not on Windows, it assumes by default that you have PCRE and want to enable rules support. You can disable rules support (removing the PCRE dependency) by passing HAVE_RULES=no to qmake. - If you are on Windows, but have PCRE available, you can enable rules support by passing HAVE_RULES=yes to qmake. - Note: This includes using build.bat since it calls qmake - to use PCRE and build.bat, you need to run set HAVE_RULES=yes before each run of build.bat Build instructions ------------------ Windows ------- Visual Studio To build PCRE, download the source code from www.pcre.org and CMake (https://cmake.org/download/). We assume you use Visual Studio 2015 - otherwise adapt the commands for your version. VS Solution file cmake . -G "Visual Studio 14 2015" Open PCRE.sln with VS IDE or via cmd: call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" x86 MSBuild PCRE.sln /target:Build /property:Configuration="Release" For 64-bit target: cmake . -G "Visual Studio 14 2015 Win64" or using NMake call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" x86 cmake . -G "NMake Makefiles" nmake or using MSYS cmake . -G "MSYS Makefiles" make Linux ----- The normal Makefile should work. Install PCRE on Ubuntu might be needed: sudo apt-get install libpcre3 libpcre3-dev Mac OSX ------- Install PCRE: sudo port install pcre Ensure /path/to/pcre.h is in CXXFLAGS, e.g: export CXXFLAGS=${CXXFLAGS}:/opt/local/include Or for MSVC copy pcre.lib and pcre.h in /externals directory. cppcheck-1.90/build.bat000066400000000000000000000031311357737443600150330ustar00rootroot00000000000000@echo off REM A simple script to build different cppcheck targets from project root REM folder. This script can be run from VS prompt or Qt prompt. REM REM Usage: build [release|debug] REM where is any of cppcheck/gui/tests/all REM release or debug is the configuration REM all-target builds both cppcheck and gui. REM REM Run the command before build.bat to enable rules using pcre: REM set HAVE_RULES=yes REM REM TODO: REM - run tests too pushd %~dp0 if "%1" == "" goto help REM Qt prompt sets QMAKESPEC if "%QMAKESPEC%" == "" ( REM parse qmakespec to see if it's some msvc if "%QMAKESPEC:~6,4%" == "msvc" ( set MAKE=nmake ) else ( set MAKE=mingw32-make ) ) else ( set MAKE=nmake ) if "%2" == "" set TARGET=release if "%2" == "debug" set TARGET=debug if "%2" == "release" set TARGET=release if "%1" == "all" goto cppcheck if "%1" == "cppcheck" goto cppcheck if "%1" == "gui" goto gui if "%1" == "tests" goto tests goto help :cppcheck pushd cli qmake -config %TARGET% HAVE_RULES=%HAVE_RULES% %MAKE% popd if "%1" == "all" goto gui goto end :gui pushd gui qmake -config %TARGET% HAVE_RULES=%HAVE_RULES% %MAKE% lupdate -no-obsolete gui.pro lrelease gui.pro popd goto end :tests pushd test qmake -config %TARGET% HAVE_RULES=%HAVE_RULES% %MAKE% popd goto end :help echo Syntax: build ^ [debug^|release] echo where ^ is any of cppcheck/gui/tests/all echo debug or release define used configuration echo all- target builds both cppcheck and gui. :end cppcheck-1.90/cfg/000077500000000000000000000000001357737443600140055ustar00rootroot00000000000000cppcheck-1.90/cfg/avr.cfg000066400000000000000000000306561357737443600152700ustar00rootroot00000000000000 false 0:255 false false false false false false false false false 0: false false false false false false false false false false false false false false false false cppcheck-1.90/cfg/boost.cfg000066400000000000000000001056151357737443600156240ustar00rootroot00000000000000 false false false false false arg1==0?0:(arg1<0?-1:1) false arg1<0?1:0 false false false 5.000000000000000000000000000000000000e-01 false 3.333333333333333333333333333333333333e-01 false 6.666666666666666666666666666666666666e-01 false 6.666666666666666666666666666666666666e-01 false 1.66666666666666666666666666666666666666666e-01 false 7.500000000000000000000000000000000000e-01 false 1.414213562373095048801688724209698078e+00 false 1.732050807568877293527446341505872366e+00 false 7.071067811865475244008443621048490392e-01 false 6.931471805599453094172321214581765680e-01 false -3.665129205816643270124391582326694694e-01 false 1.177410022515474691011569326459699637e+00 false 7.071067811865475244008443621048490392e-01 false 3.141592653589793238462643383279502884e+00 false 1.570796326794896619231321691639751442e+00 false 1.047197551196597746154214461093167628e+00 false 5.235987755982988730771072305465838140e-01 false 6.283185307179586476925286766559005768e+00 false 2.094395102393195492308428922186335256e+00 false 2.356194490192344928846982537459627163e+00 false 4.188790204786390984616857844372670512e+00 false 1.591549430918953357688837633725143620e-01 false 3.989422804014326779399460599343818684e-01 false 1.772453850905516027298167483341145182e+00 false 1.253314137315500251207882642405522626e+00 false 2.506628274631000502415765284811045253e+00 false 9.189385332046727417803297364056176398e-01 false 5.641895835477562869480794515607725858e-01 false 5.641895835477562869480794515607725858e-01 false 1.415926535897932384626433832795028841e-01 false 8.584073464102067615373566167204971158e-01 false 7.953167673715975443483953350568065807e-01 false 2.245915771836104547342715220454373502e+01 false 9.869604401089358618834490999876151135e+00 false 1.644934066848226436472415166646025189e+00 false 3.100627668029982017547631506710139520e+01 false 1.464591887561523263020142527263790391e+00 false 6.827840632552956814670208331581645981e-01 false 2.718281828459045235360287471352662497e+00 false 6.065306597126334236037995349911804534e-01 false 2.314069263277926900572908636794854738e+01 false 1.648721270700128146848650787814163571e+00 false 4.342944819032518276511289189166050822e-01 false 2.302585092994045684017991454684364207e+00 false 2.302585092994045684017991454684364207e+00 false 1.745329251994329576923690768488612713e-02 false 5.729577951308232087679815481410517033e+01 false 8.414709848078965066525023216302989996e-01 false 5.403023058681397174009366074429766037e-01 false 1.175201193643801456882381850595600815e+00 false 1.543080634815243778477905620757061682e+00 false 1.618033988749894848204586834365638117e+00 false 4.812118250596034474977589134243684231e-01 false 2.078086921235027537601322606117795767e+00 false 5.772156649015328606065120900824024310e-01 false 1.732454714600633473583025315860829681e+00 false 3.331779238077186743183761363552442266e-01 false 1.644934066848226436472415166646025189e+00 false 1.202056903159594285399738161511449990e+00 false 9.159655941772190150546035149323841107e-01 false 1.282427129100622636875342568869791727e+00 false 2.685452001065306445309714835481795693e+00 false 1.139547099404648657492793019389846112e+00 false 6.311106578189371381918993515442277798e-01 false 3.245089300687638062848660410619754415e+00 false 2.450893006876380628486604106197544154e-01 false 6.366197723675813430755350534900574481e-01 false 7.978845608028653558798921198687637369e-01 false false false false false false false false false false false false false false false cppcheck-1.90/cfg/bsd.cfg000066400000000000000000000173741357737443600152520ustar00rootroot00000000000000 false false false false false false false false false false false false false false false 0: false 0: false false 1: false 2: false false 0: false 1: -1: cppcheck-1.90/cfg/cairo.cfg000066400000000000000000000064101357737443600155640ustar00rootroot00000000000000 false false false false false false cppcheck-1.90/cfg/cppcheck-cfg.rng000066400000000000000000000473551357737443600170500ustar00rootroot00000000000000 2 ([a-zA-Z_][a-zA-Z_0-9]*[ ])*([a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*([ ]?[*&])* all|([0-9]*:[0-9]*) error warning style performance portability information Obsolescent Obsolete c99 any variadic (-?[0-9]*(\.[0-9]+)?[,:])*([-]?[0-9]+(\.[0-9]+)?)? strlen argvalue sizeof mul value first middle last [.][a-z]+ std-like std-like array-like bool char short int long long long 1 2 4 8 s u 1 20 0 2 in out inout true false [a-zA-Z_][a-zA-Z_0-9]* [a-zA-Z_][a-zA-Z_0-9:,]* malloc(:[1-5])?|calloc(:[1-5],[1-5])?|strdup(:[1-5])? resize clear push pop find insert erase change-content change-internal change at_index item buffer buffer-nt start-iterator end-iterator iterator size empty 1 9223372036854775807 cppcheck-1.90/cfg/cppcheck-lib.cfg000066400000000000000000000040221357737443600170100ustar00rootroot00000000000000 false false false false -50:50 false -50:50 false cppcheck-1.90/cfg/cppunit.cfg000066400000000000000000000045361357737443600161600ustar00rootroot00000000000000 false false false cppcheck-1.90/cfg/daca.cfg000066400000000000000000000016141357737443600153600ustar00rootroot00000000000000 false arg1<arg2?arg1:arg2 false arg1>arg2?arg1:arg2 cppcheck-1.90/cfg/embedded_sql.cfg000066400000000000000000000001551357737443600170770ustar00rootroot00000000000000 cppcheck-1.90/cfg/gnu.cfg000066400000000000000000001135261357737443600152670ustar00rootroot00000000000000 free get_current_dir_name asprintf free xmalloc xcalloc xstrdup xrealloc free xfree backtrace_symbols free pvalloc free false 0: false arg1 false 0: false false false false false false true false true 0: false 0: 0: 1: false 0: 0: 1: false 1: 0: 0: false 0: false arg1>=0 && arg1<=0x7F false 0:255 false false 0: false 0: false false 0: false 0: 0: false false false false false 0: false false false false 0: false 0: false 0: false 0: false false false false false false false false 0:4294967295 0:4294967295 false false false false false false false false false 0: 0: false 0: 0: false 0: false false false false 1: false false 0: 0: false 0: 1: false 0: 1: false 0: false false false false false true false false false false 1: -1: mkostemp mkstemps mkostemps close close epoll_create close epoll_create1 cppcheck-1.90/cfg/googletest.cfg000066400000000000000000000151371357737443600166510ustar00rootroot00000000000000 cppcheck-1.90/cfg/gtk.cfg000066400000000000000000021770241357737443600152700ustar00rootroot00000000000000 g_thread_new g_thread_try_new g_thread_ref g_thread_unref g_variant_iter_copy g_variant_iter_new g_variant_iter_free g_source_new g_idle_source_new g_timeout_source_new g_timeout_source_new_seconds g_child_watch_source_new g_cancellable_source_new g_io_create_watch g_source_ref g_source_unref g_date_time_new g_date_time_new_now g_date_time_new_now_local g_date_time_new_now_utc g_date_time_new_from_unix_local g_date_time_new_from_unix_utc g_date_time_new_from_timeval_local g_date_time_new_from_timeval_utc g_date_time_new_local g_date_time_new_utc g_date_time_add g_date_time_add_years g_date_time_add_months g_date_time_add_weeks g_date_time_add_days g_date_time_add_hours g_date_time_add_minutes g_date_time_add_seconds g_date_time_add_full g_date_time_to_timezone g_date_time_to_local g_date_time_to_utc g_date_time_ref g_date_time_unref g_dir_open g_dir_rewind g_dir_close g_timer_new g_timer_destroy g_file_attribute_info_list_new g_file_attribute_info_list_dup g_file_attribute_info_list_ref g_file_attribute_info_list_unref g_slist_alloc g_slist_copy g_slist_copy_deep g_slist_free g_slist_free_1 g_slist_free_full g_variant_new g_variant_new_va g_variant_new_boolean g_variant_new_byte g_variant_new_int16 g_variant_new_uint16 g_variant_new_int32 g_variant_new_uint32 g_variant_new_int64 g_variant_new_uint64 g_variant_new_handle g_variant_new_double g_variant_new_string g_variant_new_take_string g_variant_new_printf g_variant_new_signature g_variant_new_object_path g_variant_new_variant g_variant_new_objv g_variant_new_strv g_variant_new_bytestring g_variant_new_bytestring_array g_variant_new_maybe g_variant_new_array g_variant_new_tuple g_variant_new_dict_entry g_variant_new_fixed_array g_variant_new_from_data g_variant_new_from_bytes g_variant_builder_end g_variant_new_parsed_va g_variant_new_parsed g_variant_byteswap g_variant_get_child_value g_variant_get_normal_form g_variant_parse g_variant_ref g_variant_take_ref g_variant_ref_sink g_variant_unref g_variant_iter_new g_variant_iter_free g_variant_type_new g_variant_type_copy g_variant_type_new_array g_variant_type_new_dict_entry g_variant_type_new_maybe g_variant_type_new_tuple g_variant_type_free g_allocator_new g_allocator_free g_bookmark_file_new g_bookmark_file_free g_srv_target_new g_srv_target_free g_string_chunk_new g_string_chunk_free g_test_log_buffer_new g_test_log_buffer_free g_value_array_new g_value_array_free g_cache_new g_cache_destroy g_cclosure_new g_cclosure_new_swap g_cclosure_new_object g_cclosure_new_object_swap g_closure_new_object g_closure_new_simple g_closure_ref g_closure_unref g_array_new g_array_sized_new g_array_ref g_array_free g_array_unref g_async_queue_new g_async_queue_new_full g_async_queue_ref g_async_queue_unref g_byte_array_new g_byte_array_sized_new g_byte_array_new_take g_byte_array_sized_new g_bytes_unref_to_array g_byte_array_ref g_byte_array_free g_byte_array_unref g_checksum_new g_checksum_copy g_checksum_free g_main_loop_new g_main_new g_main_loop_ref g_main_loop_unref g_main_destroy g_main_context_new g_main_context_ref g_main_context_unref g_main_destroy g_thread_pool_new g_thread_pool_free g_error_copy g_error_new_valist g_error_new_literal g_error_new g_error_free g_string_new g_string_new_len g_string_sized_new g_variant_print_string g_string_free g_ptr_array_new g_ptr_array_new_full g_ptr_array_new_with_free_func g_ptr_array_ref g_ptr_array_free g_ptr_array_unref g_pattern_spec_new g_pattern_spec_free g_key_file_new g_key_file_ref g_key_file_free g_key_file_unref g_io_module_scope_new g_io_module_scope_free g_ascii_strdown g_ascii_strup g_base64_decode g_base64_encode g_bookmark_file_get_description g_bookmark_file_get_mime_type g_bookmark_file_get_title g_bookmark_file_to_data g_build_filename g_build_filenamev g_build_path g_build_pathv g_bytes_unref_to_data g_compute_checksum_for_bytes g_compute_checksum_for_data g_compute_checksum_for_string g_compute_hmac_for_data g_compute_hmac_for_string g_convert g_convert_with_fallback g_convert_with_iconv g_credentials_to_string g_date_time_format g_filename_display_basename g_filename_display_name g_filename_from_uri g_filename_to_uri g_get_codeset g_get_current_dir g_get_locale_variants g_key_file_get_start_group g_key_file_to_data g_malloc g_realloc g_malloc0 g_malloc0_n g_malloc_n g_realloc_n g_memdup g_path_get_basename g_path_get_dirname g_slice_alloc g_slice_alloc0 g_slice_copy g_strcompress g_strconcat g_strdup g_strdup_printf g_strdup_vprintf g_strescape g_strjoin g_strjoinv g_strndup g_strnfill g_time_val_to_iso8601 g_try_malloc g_try_realloc g_try_malloc0 g_try_malloc0_n g_try_malloc_n g_try_realloc_n g_ucs4_to_utf16 g_ucs4_to_utf8 g_unicode_canonical_decomposition g_utf16_to_ucs4 g_utf16_to_utf8 g_utf8_casefold g_utf8_collate_key g_utf8_collate_key_for_filename g_utf8_normalize g_utf8_strdown g_utf8_strreverse g_utf8_strup g_utf8_substring g_utf8_to_ucs4 g_utf8_to_ucs4_fast g_utf8_to_ucs4_fast g_utf8_to_utf16 g_key_file_get_locale_string g_key_file_get_value g_key_file_get_string g_key_file_get_boolean_list g_key_file_get_integer_list g_key_file_get_double_list g_key_file_get_comment g_dbus_proxy_get_name_owner g_file_info_get_attribute_as_string g_file_attribute_matcher_to_string g_app_launch_context_get_environment g_app_launch_context_get_startup_notify_id g_filename_completer_get_completion_suffix g_inet_address_mask_to_string g_variant_dup_string g_variant_dup_bytestring g_variant_get_objv g_variant_get_strv g_variant_print g_datalist_id_dup_data g_dir_make_tmp g_filename_from_utf8 g_filename_to_utf8 g_file_read_link g_find_program_in_path g_format_size g_format_size_for_display g_format_size_full g_hostname_to_ascii g_hostname_to_unicode g_locale_from_utf8 g_locale_to_utf8 g_markup_escape_text g_markup_printf_escaped g_markup_vprintf_escaped g_match_info_expand_references g_match_info_fetch g_match_info_fetch_named g_option_context_get_help g_regex_escape_nul g_regex_escape_string g_regex_replace g_regex_replace_eval g_regex_replace_literal g_shell_quote g_shell_unquote g_uri_escape_string g_uri_parse_scheme g_uri_unescape_segment g_uri_unescape_string g_variant_type_dup_string g_value_dup_string g_register_data g_free g_hash_table_new_full g_hash_table_new g_hash_table_ref g_hash_table_destroy g_hash_table_unref g_io_channel_unix_new g_io_channel_win32_new_fd g_io_channel_win32_new_socket g_io_channel_win32_new_messages g_io_channel_new_file g_io_channel_ref g_io_channel_close g_io_channel_shutdown g_io_channel_unref g_emblemed_icon_get_emblems g_list_alloc g_list_copy g_list_copy_deep g_app_info_get_all g_app_info_get_all_for_type g_app_info_get_fallback_for_type g_app_info_get_recommended_for_type g_io_modules_load_all_in_directory g_io_modules_load_all_in_directory_with_scope g_hash_table_get_keys g_hash_table_get_values g_list_free g_list_free_1 g_list_free_full g_regex_new g_regex_ref g_regex_unref g_node_new g_node_copy g_node_copy_deep g_node_destroy g_time_zone_new g_time_zone_new_local g_time_zone_new_utc g_time_zone_ref g_time_zone_unref g_markup_parse_context_new g_markup_parse_context_free g_mapped_file_new g_mapped_file_new_from_fd g_mapped_file_ref g_mapped_file_free g_mapped_file_unref g_mutex_new g_mutex_free g_mem_chunk_new g_mem_chunk_free g_option_group_new g_option_group_free g_option_context_new g_option_context_free g_rand_new g_rand_copy g_rand_new_with_seed g_rand_new_with_seed_array g_rand_free g_queue_new g_queue_copy g_queue_free g_slice_new g_slice_free g_slice_free1 g_sequence_new g_sequence_free g_completion_new g_completion_free g_chunk_new g_chunk_free g_bytes_new g_bytes_new_take g_bytes_new_static g_bytes_new_with_free_func g_bytes_new_from_bytes g_byte_array_free_to_bytes g_memory_output_stream_steal_as_bytes g_variant_get_data_as_bytes g_mapped_file_get_bytes g_bytes_ref g_bytes_unref g_bookmark_file_get_uris g_bookmark_file_get_groups g_bookmark_file_get_applications g_key_file_get_groups g_key_file_get_keys g_strdupv g_strsplit g_strsplit_set g_uri_list_extract_uris g_key_file_get_string_list g_key_file_get_locale_string_list g_file_info_list_attributes g_file_info_get_attribute_stringv g_app_launch_context_get_environment g_filename_completer_get_completions g_io_module_query g_variant_dup_objv g_variant_dup_bytestring_array g_environ_setenv g_environ_unsetenv g_get_environ g_listenv g_match_info_fetch_all g_regex_split g_regex_split_full g_regex_split_simple g_regex_split_simple g_variant_dup_strv g_strfreev g_hmac_new g_hmac_copy g_hmac_ref g_hmac_unref g_hook_alloc g_hook_ref g_hook_unref g_hook_destroy g_hook_free g_date_new g_date_new_dmy g_date_new_julian g_date_free g_variant_builder_new g_variant_builder_ref g_variant_builder_unref g_cond_new g_cond_free g_app_launch_context_new g_app_info_create_from_commandline g_app_info_dup g_app_info_get_default_for_type g_app_info_get_default_for_uri_scheme g_application_new g_application_get_dbus_connection g_application_get_default g_buffered_input_stream_new g_buffered_output_stream_new g_cancellable_new g_charset_converter_new g_converter_input_stream_new g_converter_output_stream_new g_credentials_new g_data_input_stream_new g_data_output_stream_new g_dbus_auth_observer_new g_dbus_connection_new_finish g_dbus_connection_new_sync g_dbus_connection_new_for_address_finish g_dbus_connection_new_for_address_sync g_dbus_message_new g_dbus_message_new_signal g_dbus_message_new_method_call g_dbus_message_new_method_reply g_dbus_message_new_method_error g_dbus_message_new_method_error_valist g_dbus_message_new_method_error_literal g_dbus_object_manager_client_new_finish g_dbus_object_manager_client_new_sync g_dbus_object_manager_client_new_for_bus_finish g_dbus_object_manager_client_new_for_bus_sync g_dbus_object_manager_server_new g_dbus_object_manager_server_get_connection g_dbus_object_proxy_new g_dbus_object_skeleton_new g_dbus_proxy_new_finish g_dbus_proxy_new_sync g_dbus_proxy_new_for_bus_finish g_dbus_proxy_new_for_bus_sync g_emblemed_icon_new g_emblem_new g_emblem_new_with_origin g_file_icon_new g_file_icon_get_file g_file_info_new g_file_info_dup g_file_info_get_icon g_file_info_get_symbolic_icon g_file_info_get_attribute_object g_file_info_get_deletion_date g_filename_completer_new g_inet_address_mask_new g_inet_address_mask_new_from_string g_inet_address_mask_get_address g_inet_socket_address_new g_inet_socket_address_get_address g_initable_new g_initable_new_valist g_initable_newv g_io_module_new g_io_module_scope_new g_keyfile_settings_backend_new g_memory_input_stream_new g_memory_input_stream_new_from_data g_memory_input_stream_new_from_bytes g_memory_output_stream_new g_memory_output_stream_new_resizable g_memory_settings_backend_new g_null_settings_backend_new g_menu_item_new g_menu_item_new_section g_menu_item_new_submenu g_menu_item_new_from_model g_menu_new g_mount_operation_new g_network_address_new g_network_service_new g_object_new g_param_spec_pool_new g_pollable_source_new g_private_new g_proxy_address_new g_ptr_array_sized_new g_relation_new g_scanner_new g_settings_new g_signal_type_cclosure_new g_simple_action_group_new g_simple_action_new g_simple_async_result_new g_simple_permission_new g_socket_client_new g_socket_listener_new g_socket_new g_socket_service_new g_tcp_wrapper_connection_new g_test_dbus_new g_themed_icon_new g_threaded_socket_service_new g_tls_client_connection_new g_tls_file_database_new g_tls_password_new g_tls_server_connection_new g_unix_signal_source_new g_zlib_compressor_new g_zlib_decompressor_new g_object_ref g_object_unref gtk_widget_destroy g_tree_new g_tree_new_full g_tree_new_with_data g_tree_ref g_tree_unref g_file_attribute_matcher_new g_file_attribute_matcher_subtract g_file_attribute_matcher_ref g_file_attribute_matcher_unref true false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false arg1 false false false false false g_value_set_object_take_ownership has been deprecated since version 2.4 and should not be used in newly-written code. Use g_value_take_object() instead. false false false g_value_set_string_take_ownership has been deprecated since version 2.4 and should not be used in newly-written code. Use g_value_take_string() instead. false false false false false false false false false false false false false false false 0:255 false false false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false 0:255 false false false false false 0,2:36 false 0,2:36 false false 0:255 false 0:255 false 0:255 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false 1: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: 0: 0: false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false g_strcasecmp has been deprecated since version 2.2 and should not be used in newly-written code. false arg1 false arg1 false false false false false false false false false g_strncasecmp has been deprecated since version 2.2 and should not be used in newly-written code. 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: 0: false 0: false 0: 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: 0: 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false g_string_down has been deprecated since version 2.2 and should not be used in newly-written code. This function uses the locale-specific tolower() function, which is almost never the right thing. Use g_string_ascii_down() or g_utf8_strdown() instead. false false false false false false false false false false false false false false false false false false false false 0: false g_string_sprintf is deprecated and should not be used in newly-written code. This function has been renamed to g_string_printf(). false g_string_sprintfa is deprecated and should not be used in newly-written code. This function has been renamed to g_string_append_printf() false false g_string_up has been deprecated since version 2.2 and should not be used in newly-written code. This function uses the locale-specific toupper() function, which is almost never the right thing. Use g_string_ascii_up() or g_utf8_strup() instead. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false g_type_class_add_private has been deprecated since version 2.58 and should not be used in newly-written code. Use the G_ADD_PRIVATE() macro with the G_DEFINE_* family of macros to add instance private data to a type. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false true false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false gtk_hbox_new has been deprecated since version 3.2 and should not be used in newly-written code. You can use gtk_box_new() with GTK_ORIENTATION_HORIZONTAL instead, which is a quick and easy change. But the recommendation is to switch to GtkGrid, since GtkBox is going to go away eventually. See Migrating from other containers to GtkGrid. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false gtk_vbox_new has been deprecated since version 3.2 and should not be used in newly-written code. You can use gtk_box_new() with GTK_ORIENTATION_VERTICAL instead, which is a quick and easy change. But the recommendation is to switch to GtkGrid, since GtkBox is going to go away eventually. See Migrating from other containers to GtkGrid. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false true gtk_exit is deprecated and should not be used in newly-written code. Use the standard exit() function instead. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false gtk_signal_connect_object_while_alive is deprecated and should not be used in newly-written code. Use g_signal_connect_object() instead, passing G_CONNECT_SWAPPED as connect_flags. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false gtk_label_get is deprecated and should not be used in newly-written code. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false cppcheck-1.90/cfg/kde.cfg000066400000000000000000000063521357737443600152370ustar00rootroot00000000000000 false cppcheck-1.90/cfg/libcerror.cfg000066400000000000000000000071111357737443600164510ustar00rootroot00000000000000 false false false false false false 0: false cppcheck-1.90/cfg/libcurl.cfg000066400000000000000000000343441357737443600161320ustar00rootroot00000000000000 curl_easy_init curl_easy_duphandle curl_easy_cleanup curl_easy_escape curl_easy_unescape curl_escape curl_unescape curl_free curl_getenv curl_maprintf curl_mvaprintf free false false false 0: false false false false false 0: false false 0: false false false 0: false false 0: false false This function will be removed from the public libcurl API in a near future. It will instead be made "available" by source code access only, and then as curlx_getenv(). false These functions will be removed from the public libcurl API in the future. Do not use them in any new programs or projects. false false false 0: false false Usage of curl_multi_socket is deprecated, whereas the function is equivalent to curl_multi_socket_action with ev_bitmask set to 0. false false These functions will be removed from the public libcurl API in the future. Do not use them in any new programs or projects. false false false 0: false false These functions will be removed from the public libcurl API in a near future. They will instead be made "available" by source code access only, and then as curlx_strequal() and curlx_strenqual(). false These functions will be removed from the public libcurl API in a near future. They will instead be made "available" by source code access only, and then as curlx_strequal() and curlx_strenqual(). 0: false 0: cppcheck-1.90/cfg/libsigc++.cfg000066400000000000000000000047711357737443600162410ustar00rootroot00000000000000 false cppcheck-1.90/cfg/lua.cfg000066400000000000000000000223651357737443600152570ustar00rootroot00000000000000 true false false false false false 1: false false false false false false 0: false false false false false false false false false false false false false false false true cppcheck-1.90/cfg/mfc.cfg000066400000000000000000000301111357737443600152270ustar00rootroot00000000000000 cppcheck-1.90/cfg/microsoft_atl.cfg000066400000000000000000000031221357737443600173310ustar00rootroot00000000000000 cppcheck-1.90/cfg/microsoft_sal.cfg000066400000000000000000000644021357737443600173400ustar00rootroot00000000000000 cppcheck-1.90/cfg/motif.cfg000066400000000000000000000166371357737443600156210ustar00rootroot00000000000000 false false false false false false false false false false MrmCloseHierarchy MrmOpenHierarchy MrmOpenHierarchyPerDisplay XmStringFree XmStringCreateLocalized XmStringCreateSimple XmStringGenerate XmCvtCTToXmString XtFree XmFontListEntryGetTag XmTextGetString XmCvtXmStringToCT XmWidgetGetBaselines XmFontListEntryFree XmFontListCreate XmFontListAppendEntry false false false false false false false XtFree XtMalloc XtCalloc XtRealloc XtNew XtNewString false false false false false false XOpenDisplay XCloseDisplay cppcheck-1.90/cfg/nspr.cfg000066400000000000000000000035441357737443600154560ustar00rootroot00000000000000 cppcheck-1.90/cfg/opencv2.cfg000066400000000000000000000063571357737443600160550ustar00rootroot00000000000000 false false false false cppcheck-1.90/cfg/opengl.cfg000066400000000000000000000530631357737443600157610ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false cppcheck-1.90/cfg/openmp.cfg000066400000000000000000000113651357737443600157720ustar00rootroot00000000000000 omp_target_alloc omp_target_free omp_alloc omp_free false false false false This routine has been deprecated. See OpenMP specification. false false false false false false false false This routine has been deprecated. See OpenMP specification. false 1: false 0: false 0: cppcheck-1.90/cfg/openssl.cfg000066400000000000000000000114211357737443600161500ustar00rootroot00000000000000 EVP_CIPHER_CTX_new EVP_CIPHER_CTX_free false false false false 0: false false false 0: false false false false false false 0: cppcheck-1.90/cfg/posix.cfg000066400000000000000000004556371357737443600156540ustar00rootroot00000000000000 false false false 0: arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A false 0:255 arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' false 0:255 arg1==' ' || arg1=='\t' false 0:255 arg1==0x7F || arg1<=0x1F false 0:255 arg1>='0' && arg1<='9' false 0:255 arg1>=0x21 && arg1<=0x7E false 0:255 arg1>=0x61 && arg1<=0x7A false 0:255 arg1>=0x20 && arg1<=0x7E false 0:255 arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E false 0:255 arg1>=0x09 && arg1<=0x0D || arg1==0x20 false 0:255 arg1>=0 && arg1<=0x7F false 0:255 arg1>=0x41 && arg1<=0x5A false 0:255 arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 false 0:255 false 0: false false dlopen dlclose false 0: 0: false 0: false 0: false 0: false false false false 0: false 0: 0: false false false 0: false 0: false 0: false false 0: false false 0: false false false 0: false 0: false false false false false false false false 0: false 0:999999 Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead. The obsolescent function 'usleep' is called. POSIX.1-2001 declares usleep() function obsolescent and POSIX.1-2008 removes it. It is recommended that new applications use the 'nanosleep' or 'setitimer' function. true false false false false 0: false Non reentrant function 'getrpcent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcent_r'. false Non reentrant function 'getrpcbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcbyname_r'. false Non reentrant function 'getrpcbynumber' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcbynumber_r'. false Non reentrant function 'getprotoent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotoent_r'. false Non reentrant function 'getprotobyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotobyname_r'. false Non reentrant function 'getprotobynumber' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotobynumber_r'. false Non reentrant function 'getservent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservent_r'. false Non reentrant function 'getservbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservbyname_r'. false Non reentrant function 'getservbyport' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservbyport_r'. false Non reentrant function 'getnetent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetent_r'. false Non reentrant function 'getnetbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetbyname_r'. false Non reentrant function 'getnetbyaddr' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetbyaddr_r'. false Non reentrant function 'gethostent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gethostent_r'. false false Non reentrant function 'gethostbyname2' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gethostbyname2_r'. false false false false false false false false 0: false false false false Obsolete function 'mktemp' called. It is recommended to use 'mkstemp' or 'mkdtemp' instead. The function 'mktemp' is considered to be dangerous due to race conditions and some implementations generating only up to 26 different filenames out of each template. This function has been removed in POSIX.1-2008. Use 'mkstemp' or 'mkdtemp' instead. false 0: false false false false false false false false false false false false false false false false false false false false 0: false false false 0: false false false 0: false false false false 0: false 0: false false false false 0: false false false false 0: false 0: false 0: false false false 0: false false false false false false false 0: 0: 0: 1: 0: 0: false 0: 0: 0: 0: 0: 0: 0: 0: false 1: 0: false 1: 0: false 1: false false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false 0: 0: 0: false false false false false false Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'. false Non reentrant function 'getpwnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwnam_r'. Non reentrant function 'strtok' called. For threadsafe applications it is recommended to use the reentrant replacement function 'strtok_r'. false false 0: false Non reentrant function 'getpwuid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwuid_r'. false 0: false false false false false false false 0: false 0: false false false false 0: false 0: false false false 0: false false false false false 0: false false false false false false false false Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'. false false Non reentrant function 'readdir' called. For threadsafe applications it is recommended to use the reentrant replacement function 'readdir_r'. false false 0: false false 0: false false false false Non reentrant function 'gmtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gmtime_r'. false false false false false false false Obsolescent function 'makecontext' called. Applications are recommended to be rewritten to use POSIX threads. false Obsolescent function 'swapcontext' called. Applications are recommended to be rewritten to use POSIX threads. false Obsolescent function 'getcontext' called. Applications are recommended to be rewritten to use POSIX threads. false false false 0: false 0: false 0: false false false false false false false false false false false false false true false false false false false false false false 0: false false false false false false false Non reentrant function 'tempnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'tempnam_r'. false Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'. false 0: Non reentrant function 'ttyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'ttyname_r'. false 0: 0: false Non reentrant function 'getspnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getspnam_r'. false Non reentrant function 'getspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getspent_r'. false Non reentrant function 'fgetspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetspent_r'. false Non reentrant function 'sgetspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'sgetspent_r'. false Non reentrant function 'fgetpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetpwent_r'. false Non reentrant function 'getgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrent_r'. false 0: false Non reentrant function 'fgetgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetgrent_r'. false Non reentrant function 'getnetgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetgrent_r'. false Non reentrant function 'getgrnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrnam_r'. false Non reentrant function 'getgrgid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrgid_r'. false Non reentrant function 'getlogin' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getlogin_r'. false Non reentrant function 'ctermid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'ctermid_r'. false false false false false false false false false false 0: false false false 0: false false false false false false false 0: false 0: false 0: 0: false 0: false false false false true false false false false false false false false false false false false false false false 0: false 0: false 0: false 0: false 0: false 0: false 0: false false 0: false false 0: false false 1: false 0: false 0: valloc free posix_memalign free scandir free strdup strndup wcsdup free mmap mmap64 munmap open mkstemp creat openat socket close opendir fdopendir closedir fdopen fclose popen pclose mq_open mq_close getaddrinfo freeaddrinfo cppcheck-1.90/cfg/python.cfg000066400000000000000000000401611357737443600160110ustar00rootroot00000000000000 PyMem_Malloc PyMem_Calloc PyMem_Realloc PyMem_Free PyMem_RawMalloc PyMem_RawCalloc PyMem_RawRealloc PyMem_RawFree PyObject_Malloc PyObject_Calloc PyObject_Realloc PyObject_Free false false true true false false false false false false 0: false false false false false false false false false NULL false NULL false false false NULL false false false false false false false false false 1: 0: false false 0: false 0: false false false false cppcheck-1.90/cfg/qt.cfg000066400000000000000000005247161357737443600151310ustar00rootroot00000000000000 READ READ WRITE NOTIFY connect invokeMethod false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false 0: false false false false false false false false false false false false false false false false 0,2:36 false 0,2:36 false 0,2:36 false false 0,2:36 false false false false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false false false false false false false false false false false false false false false false false false false false true false false false false false false false false false false false false false false false false false false false false false false false false false :-1,1: 1:12 1:31 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false cppcheck-1.90/cfg/ruby.cfg000066400000000000000000000102111357737443600154420ustar00rootroot00000000000000 false false false false false false false false false false false true cppcheck-1.90/cfg/sdl.cfg000066400000000000000000000245311357737443600152550ustar00rootroot00000000000000 false false false false false false false false false false false false false false false false false false -1 false false false SDL_FreeSurface SDL_CreateRGBSurface SDL_CreateRGBSurfaceFrom SDL_ConvertSurface TTF_RenderUTF8_Blended IMG_LoadPNG_RW IMG_LoadJPG_RW IMG_Load SDL_DestroyMutex SDL_CreateMutex SDL_WaitThread SDL_CreateThread SDL_RWclose SDL_RWFromFile SDL_FreeRW SDL_AllocRW Mix_FreeMusic Mix_LoadMUSType_RW cppcheck-1.90/cfg/sfml.cfg000066400000000000000000000220141357737443600154260ustar00rootroot00000000000000 false false false false false false false false false 0: false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false 0: false 0:100 false false false false cppcheck-1.90/cfg/sqlite3.cfg000066400000000000000000001630521357737443600160610ustar00rootroot00000000000000 sqlite3_malloc sqlite3_malloc64 sqlite3_free sqlite3_str_new sqlite3_str_finish sqlite3_str_finish sqlite3_free sqlite3_mprintf sqlite3_vmprintf sqlite3_free sqlite3_expanded_sql sqlite3_free sqlite3_open sqlite3_open16 sqlite3_open_v2 sqlite3_close sqlite3_close_v2 false 1: 0: false 1: false 1: false 1: false 1: false false false 0: false false 0: false 0: false 0: false 0: false 0: false 0: false 0: false false -1:127 false false false false false false false false false 1: false 1: false false false false false false false false false false false The sqlite3_prepare() interface is legacy and should be avoided false The sqlite3_prepare16() interface is legacy and should be avoided false false false false false false false false false false false false false 0: false false false 0: false false false false false false false false false false false false cppcheck-1.90/cfg/std.cfg000066400000000000000000007342551357737443600153000ustar00rootroot00000000000000 true arg1>0?arg1:-arg1 false arg1>0?arg1:-arg1 false false false false false -1.0:1.0 false -1.0:1.0 false -1.0:1.0 false false false false false 26: false 0.0: false 0.0: false 0.0: false false false false false false false false false false -1.0:1.0 false -1.0:1.0 false -1.0:1.0 false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 1: 0: false false false false false false false false false false false false false false false false false false false false false 26: false false :-1,1: false :-1,1: true false false false false false false false false false false false false false false false false false false false false true false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false 0: 0: false false false false false false false false false false false false false false false 0: false 0: false false false 0: 0: false 0: false 0: false 0: false 0:255 false false false 0: false 0: false false false 0: false false 0:255 false false false Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun if the input data exceeds the size of the buffer. It is recommended to use the functions 'fgets' or 'gets_s' instead. false 0: false arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A false 0:255 arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A false arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' false 0:255 arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' false arg1==' ' || arg1=='\t' false 0:255 arg1==' ' || arg1=='\t' false arg1==0x7F || arg1<=0x1F false 0:255 arg1==0x7F || arg1<=0x1F false false arg1>='0' && arg1<='9' false 0:255 arg1>='0' && arg1<='9' false arg1>=0x21 && arg1<=0x7E false 0:255 arg1>=0x21 && arg1<=0x7E false arg1>=0x61 && arg1<=0x7A false 0:255 arg1>=0x61 && arg1<=0x7A false arg1>=0x20 && arg1<=0x7E false 0:255 arg1>=0x20 && arg1<=0x7E false arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E false 0:255 arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E false arg1>=0x09 && arg1<=0x0D || arg1==0x20 false 0:255 arg1>=0x09 && arg1<=0x0D || arg1==0x20 false arg1>=0x41 && arg1<=0x5A false 0:255 arg1>=0x41 && arg1<=0x5A false arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 false 0:255 arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 false false false false false false false false false arg1>0?arg1:-arg1 false arg1>0?arg1:-arg1 false false false false false false false false false false false false false false false false 0: false :-1,1: false :-1,1: false false false false false false false false false arg1>arg2?1:0 false arg1 >= arg2?1:0 false false false false false arg1<arg2?1:0 false arg1 <= arg2?1:0 false (arg1<arg2 || arg1>arg2)?1:0 false false false false false false false false false false false false false false false false false false false false false false false false false false false false true false 0: false 0: false 0: false 0: 0: false 0: 0: false 0: false 0: false 0: false 0: 0: false 0: false 0: 0: false 0: false 0: false false false false false false false false false false false false false false false false false false false 0: 0: false 0: 0: false 0: false false 0: false 0: false 0: false false false 0: false 0: 0: false false false false false false false false false false false false false false false false false 0: false false false false false 0:255 false false false false 1: false false 0: strlen(arg1) false false 0: false 0: false false 0: false 0: false 0: false 0: false 0: false 0: false false false false 0: false 0: false false false false false false false false false false 0: false false false 0:255 false false 0: false false false false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false false 0: false false 0:255 false 0:255 false false false false false false false false false false false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false 0: 0:255 false false 0: false false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false 0,2:36 false false false 0: false 0: false false 0: false false 0: false 0: false false false false false false false arg1<arg2?arg1:arg2 false arg1>arg2?arg1:arg2 false false false false false false 0: false false false false false false 0: false false false false false false false 1: false 1: false 1: false false false false false false false false false false false 0: false false 0: false false 0: false false false false false 0: false 0: false 0: false 0: false false false false 0: 0: false false false false 0: 0: false false 0: false 0: false 0: false false 0: false 0: false 0: false 0: false 0: false 0: false 2:36 false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false false false false false false false false false false false true false false false false false false false false false false false false false malloc calloc aligned_alloc realloc reallocarray free fopen tmpfile freopen fclose std::insert_iterator std::pair cppcheck-1.90/cfg/tinyxml2.cfg000066400000000000000000000043631357737443600162620ustar00rootroot00000000000000 false false false cppcheck-1.90/cfg/windows.cfg000066400000000000000000022036441357737443600161730ustar00rootroot00000000000000 CreatePen CreateBrushIndirect CreateDIBPatternBrush CreateDIBPatternBrushPt CreateHatchBrush CreatePatternBrush CreateSolidBrush CreateFont CreateFontIndirect CreateFontIndirectEx CreateBitmap CreateBitmapIndirect CreateCompatibleBitmap CreateDIBitmap CreateDIBSection CreateDiscardableBitmap CreateEllipticRgn CreateEllipticRgnIndirect CreatePolygonRgn CreatePolyPolygonRgn CreateRectRgn CreateRectRgnIndirect CreateRoundRectRgn CreateHalftonePalette CreatePalette DeleteObject closesocket socket CreateThread CreateFile CreateFileA CreateFileW OpenFile CreateJobObject CreateRemoteThread CreateConsoleScreenBuffer OpenBackupEventLog OpenEventLog CreateFileMapping CreateFileMappingFromApp CreateFileMappingNuma CreateMemoryResourceNotification OpenFileMapping CreateNamedPipe CreateEvent CreateEventA CreateEventW CreateEventEx CreateEventExA CreateEventExW CreateMutex CreateMutexA CreateMutexW CreateMutexEx CreateMutexExA CreateMutexExW CreateSemaphore CreateSemaphoreA CreateSemaphoreW CreateSemaphoreEx CreateSemaphoreExA CreateSemaphoreExW CreateTimerQueue CreateWaitableTimer OpenEvent OpenEventA OpenEventW OpenMutex OpenMutexA OpenMutexW OpenSemaphore OpenSemaphoreA OpenSemaphoreW OpenWaitableTimer OpenJobObject OpenProcess OpenThread CreateMailslot CloseHandle FindFirstFile FindFirstFileW FindFirstFileA FindFirstFileEx FindFirstFileExW FindFirstFileExA FindFirstFileNameW FindFirstFileNameTransactedW FindFirstStreamTransactedW FindFirstFileTransacted FindFirstFileTransactedA FindFirstFileTransactedW FindFirstStreamW FindClose OpenSCManager OpenService CreateService CloseServiceHandle LockServiceDatabase UnlockServiceDatabase HeapCreate HeapDestroy _wfopen _tfopen _wfopen_s _tfopen_s fclose _fcloseall _open _topen _wopen _close _popen _wpopen _tpopen _pclose LoadLibrary LoadLibraryA LoadLibraryW LoadLibraryEx LoadLibraryExA LoadLibraryExW FreeLibrary FreeLibraryAndExitThread UuidToString UuidToStringA UuidToStringW RpcStringFree ExAllocatePool ExAllocatePoolWithQuota ExAllocatePoolWithQuotaTag ExAllocatePoolWithTag ExAllocatePoolWithTagPriority ExFreePool ExFreePoolWithTag HeapAlloc HeapReAlloc HeapFree IoAllocateErrorLogEntry IoWriteErrorLogEntry IoFreeErrorLogEntry IoAllocateIrp IoFreeIrp IofCallDriver IoCallDriver IoAllocateMdl IoFreeMdl MmAllocateContiguousMemory MmFreeContiguousMemory MmAllocateContiguousMemorySpecifyCache MmAllocateContiguousMemorySpecifyCacheNode MmFreeContiguousMemorySpecifyCache IoAllocateWorkItem IoFreeWorkItem RtlAllocateHeap RtlFreeHeap ExAllocateFromPagedLookasideList ExFreeToPagedLookasideList ExAllocateFromNPagedLookasideList ExFreeToNPagedLookasideList AllocateHeap FreeHeap AllocateLsaHeap FreeLsaHeap AllocatePrivateHeap FreePrivateHeap VirtualAlloc VirtualFree VirtualAllocEx VirtualAllocExNuma VirtualFreeEx LocalAlloc LocalFree GlobalAlloc GlobalFree SetClipboardData MapViewOfFile MapViewOfFileEx MapViewOfFileExNuma MapViewOfFileFromApp UnmapViewOfFile RtlCreateHeap RtlDestroyHeap strdup wcsdup _strdup _wcsdup _mbsdup _tcsdup _malloc_dbg _calloc_dbg _strdup_dbg _wcsdup_dbg _tcsdup_dbg free _free_dbg _aligned_malloc _aligned_malloc_dbg _aligned_offset_malloc _aligned_offset_malloc_dbg _aligned_free _aligned_free_dbg CoTaskMemAlloc CoTaskMemFree _malloca _freea AllocateAndInitializeSid FreeSid false 0: false 0: false 0: false 0: false 0: true true false false false false false false false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false false false false false false false 0: false false false false 0: false 0: false 0: 0: false 0: 0: false false These POSIX functions are deprecated. Use the ISO C++ conformant _strdup, _wcsdup, _mbsdup instead. false These POSIX functions are deprecated. Use the ISO C++ conformant _strdup, _wcsdup, _mbsdup instead. false false 0: false false 0: false 0: false false false false false false false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false arg1>0?arg1:-arg1 false false false false false false false false false false false false false 0: false false false false 0: false 0: false false 0: false false false false false false false 0: 0: false false false 0: false 0: false 0: false 0: false 0: false 0: false false strlen(arg1) false strlen(arg1) false false false false false 0: false 0: false false false 0: false 0: false 0: false 0: false false false false false false false false false false false false 0 false false false false false 0: true false 1: false false false false false false false false false false false false false false Due to security concerns it is not recommended to use this function, see MSDN for details. false Due to security concerns it is not recommended to use this function, see MSDN for details. false 1: false 1: 0 false false 1: false false 0,1 false false false 0: false 0: false arg1 false arg1 false false false false false false false false 0: false 0: false false false false false false false false 1: false false false false 0: false 0: false false false false false false false false false false false false false false false false false 0: false false false false false false false false false 0:255 false 0:255 false false 0:255 false 0:255 false false 0: false 0: false false false false false false false 2:36 false 2:36 false 0: false 0: false false false false false false 0: 0: false false false false false false false false false false false GetVersion may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper functions false GetVersionEx may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper functions false false false false false false false cppcheck-1.90/cfg/wxwidgets.cfg000066400000000000000000020624271357737443600165300ustar00rootroot00000000000000 false false false false false false false false false This is the same as 'wxString::IsEmpty' and is kept for wxWidgets 1.xx compatibility. You should not use it in new code. false This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false false This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false false This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false This is the same as 'wxString::Len' and is kept for wxWidgets 1.xx compatibility. You should not use it in new code. false false false false false false false false false true false false false false false false false false 10,16 false 0: false false false false false false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 1: false false 0: false false false false false false false false false false false false false false false false false false false This function is deprecated and kept mostly for backwards compatibility. Please override 'wxApp::MacOpenFiles' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'PushBack' method instead in any new code. false This function should be used instead of changing 'wxCAL_NO_YEAR_CHANGE' style bit directly. It allows or disallows the user to change the year interactively. Only in generic 'wxCalendarCtrl'. false This function is deprecated and kept mostly for backwards compatibility. Please override 'GetMargins()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'Dismiss()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'Popup()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'SetMargins()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'wxDataViewCustomRenderer::ActivateCell()' method instead in any new code. false This function is deprecated and kept mostly for backwards compatibility. Please override 'wxDataViewCustomRenderer::ActivateCell()' method instead in any new code. false false false This function is deprecated and is replaced by 'wxLog' functionality. false This function is deprecated and is replaced by 'wxLog' functionality. false This function is deprecated. Construct a 'wxFileName' with 'wxPATH_DOS' and then use 'wxFileName::GetFullPath(wxPATH_UNIX)' instead. false This function is deprecated. Please use 'wxFileName::SplitPath()' instead. false This function is deprecated. false This function is deprecated. This function does not free the old sizer which may result in memory leaks, use 'wxSizerItem::AssignSizer' which does free it instead. false false false false false false false false false false This function is deprecated. This function does not free the old sizer which may result in memory leaks, use 'wxSizerItem::AssignSpacer' which does free it instead. false false false false This function is deprecated. Please use 'wxGrid::SetCellAlignment(row, col, horiz, vert)' instead. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false This is the same as wxString::Trim() except that it doesn't change this string. This is a wxWidgets 1.xx compatibility function; you should not use it in new code. false false This function is deprecated since version 3.1.2, dimensions and depth can only be set at construction time. false 0,2:36 false false false false false false false false false false false false false false false false false false false false false false false false false false 0: 0: false false 0: false false false false false false false false false false false false false false false true false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: false 0: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false 0: false 1: false false false false false false false false false false false 0: false false false false false false false false false 1: false false false false false false false false false false false false false false false false false false false false false false 0: 0: false 0: 0: false false false false 0: false false false false false false false false false false false false 0: false false false false 1: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false This is a wxWidgets 1.xx compatibility function. Use 'wxString::Mid' in new applications instead. false false false false false false false false false false false false false false false false false false false false false false false false false false false false arg1<arg2?arg1:arg2 false arg1>arg2?arg1:arg2 false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false false This is a wxWidgets 1.xx compatibility function. Use 'wxString::Find' in new applications instead. false This is a wxWidgets 1.xx compatibility function. You should not use it in new code. false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false This is a wxWidgets 1.xx compatibility function. Use 'wxString::Truncate' in new applications instead. false false This is a wxWidgets 1.xx compatibility function. Use 'wxString::MakeUpper' in new applications instead. false This is a wxWidgets 1.xx compatibility function. Use 'wxString::MakeLower' in new applications instead. false false false false false false false false false false 1: false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false false 0: false false false 0: 0: false false false false false 0: false 0: false 0: false 0: false false false false false false cppcheck-1.90/cfg/zlib.cfg000066400000000000000000001131471357737443600154350ustar00rootroot00000000000000 false false false false false false false false false 0: false false false false false false 0: false false :16 false false false 0: false false false false false false :16 false false false false false false 0: false 0: false 0: false 0: false gzopen gzopen64 gzdopen gzopen_w gzclose gzclose_r gzclose_w false false false 0: false false 0: false 0: 0: false 0: false 0: 0: false false false 0: false 0:255 false false 0:255 false false false false false false false false false false false false false false 0: 0: false 0: 0: false 0: 0: false 0: 0: false 0: 0: false 0: 0: false false false false false false false false cppcheck-1.90/cli/000077500000000000000000000000001357737443600140155ustar00rootroot00000000000000cppcheck-1.90/cli/CMakeLists.txt000066400000000000000000000021761357737443600165630ustar00rootroot00000000000000include_directories(${PROJECT_SOURCE_DIR}/lib/) include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/externals/tinyxml/) include_directories(${PROJECT_SOURCE_DIR}/externals/simplecpp/) file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") file(GLOB mainfile "main.cpp") list(REMOVE_ITEM srcs ${mainfile}) add_library(cli_objs OBJECT ${hdrs} ${srcs}) add_executable(cppcheck ${hdrs} ${mainfile} $ $ $ $) if (HAVE_RULES) find_library(PCRE_LIBRARY pcre) target_link_libraries(cppcheck ${PCRE_LIBRARY}) find_path(PCRE_INCLUDE pcre.h) target_include_directories(cppcheck PUBLIC ${PCRE_INCLUDE}) endif() if (WIN32 AND NOT BORLAND) target_link_libraries(cppcheck Shlwapi.lib) endif() install(TARGETS cppcheck RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) install(FILES ${addons} DESTINATION ${FILESDIR}/addons COMPONENT headers) install(FILES ${cfgs} DESTINATION ${FILESDIR}/cfg COMPONENT headers) install(FILES ${platforms} DESTINATION ${FILESDIR}/platforms COMPONENT headers) cppcheck-1.90/cli/cli.vcxproj000066400000000000000000000641161357737443600162110ustar00rootroot00000000000000 Debug-PCRE Win32 Debug-PCRE x64 Debug Win32 Debug x64 Release-PCRE Win32 Release-PCRE x64 Release Win32 Release x64 {35CBDF51-2456-3EC3-99ED-113C30858883} cli 10.0 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 ..\bin\debug\ ..\bin\debug\ ..\bin\debug\ ..\bin\debug\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ cppcheck cppcheck cppcheck cppcheck true true true true ..\bin\ ..\bin\ ..\bin\ ..\bin\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ cppcheck cppcheck cppcheck cppcheck true true true true true true true true ..\lib;..\externals\..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) false true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4482;4512 true true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true $(TargetDir)cli.pdb true 8000000 8000000 ..\lib;..\externals\..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) false true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4482;4512 true true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true true 8000000 8000000 ..\lib;..\externals\..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4482;4512 true true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true 8000000 8000000 ..\lib;..\externals\..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4482;4512 true true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true 8000000 8000000 ..\lib;..\externals\..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDLL true Level4 AnySuitable true Speed true true 4251;4482;4512 true /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true true 8000000 8000000 ..\lib;..\externals\..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) MultiThreadedDLL true Level4 AnySuitable true Speed true true 4251;4482;4512 true /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true true 8000000 8000000 ..\lib;..\externals\..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true Level4 AnySuitable true Speed true true true 4251;4482;4512 true /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true true true true 8000000 8000000 ..\lib;..\externals\..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true Level4 AnySuitable true Speed true true true 4251;4482;4512 true /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true 8000000 8000000 {c183db5b-ad6c-423d-80ca-1f9549555a1a} cppcheck-1.90/cli/cli.vcxproj.filters000066400000000000000000000035441357737443600176560ustar00rootroot00000000000000 {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx {6d3be647-edb6-43e6-a7eb-3031a2c7b655} Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Resource Files cppcheck-1.90/cli/cmdlineparser.cpp000066400000000000000000001630771357737443600173670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cmdlineparser.h" #include "check.h" #include "cppcheckexecutor.h" #include "filelister.h" #include "importproject.h" #include "path.h" #include "platform.h" #include "settings.h" #include "standards.h" #include "suppressions.h" #include "threadexecutor.h" // Threading model #include "timer.h" #include "utils.h" #include #include #include // EXIT_FAILURE #include #include #include #include #ifdef HAVE_RULES // xml is used for rules #include #endif static void addFilesToList(const std::string& fileList, std::vector& pathNames) { // To keep things initially simple, if the file can't be opened, just be silent and move on. std::istream *files; std::ifstream infile; if (fileList == "-") { // read from stdin files = &std::cin; } else { infile.open(fileList); files = &infile; } if (files && *files) { std::string fileName; while (std::getline(*files, fileName)) { // next line if (!fileName.empty()) { pathNames.push_back(fileName); } } } } static bool addIncludePathsToList(const std::string& fileList, std::list* pathNames) { std::ifstream files(fileList); if (files) { std::string pathName; while (std::getline(files, pathName)) { // next line if (!pathName.empty()) { pathName = Path::removeQuotationMarks(pathName); pathName = Path::fromNativeSeparators(pathName); // If path doesn't end with / or \, add it if (!endsWith(pathName, '/')) pathName += '/'; pathNames->push_back(pathName); } } return true; } return false; } static bool addPathsToSet(const std::string& fileName, std::set* set) { std::list templist; if (!addIncludePathsToList(fileName, &templist)) return false; set->insert(templist.begin(), templist.end()); return true; } CmdLineParser::CmdLineParser(Settings *settings) : mSettings(settings) , mShowHelp(false) , mShowVersion(false) , mShowErrorMessages(false) , mExitAfterPrint(false) { } void CmdLineParser::printMessage(const std::string &message) { std::cout << message << std::endl; } void CmdLineParser::printMessage(const char* message) { std::cout << message << std::endl; } bool CmdLineParser::parseFromArgs(int argc, const char* const argv[]) { bool def = false; bool maxconfigs = false; mSettings->exename = argv[0]; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { if (std::strcmp(argv[i], "--version") == 0) { mShowVersion = true; mExitAfterPrint = true; return true; } else if (std::strncmp(argv[i], "--addon=", 8) == 0) mSettings->addons.emplace_back(argv[i]+8); else if (std::strncmp(argv[i], "--cppcheck-build-dir=", 21) == 0) { mSettings->buildDir = Path::fromNativeSeparators(argv[i] + 21); if (endsWith(mSettings->buildDir, '/')) mSettings->buildDir.erase(mSettings->buildDir.size() - 1U); } // Flag used for various purposes during debugging else if (std::strcmp(argv[i], "--debug-simplified") == 0) mSettings->debugSimplified = true; // Show --debug output after the first simplifications else if (std::strcmp(argv[i], "--debug") == 0 || std::strcmp(argv[i], "--debug-normal") == 0) mSettings->debugnormal = true; // Show debug warnings else if (std::strcmp(argv[i], "--debug-warnings") == 0) mSettings->debugwarnings = true; // Show template information else if (std::strcmp(argv[i], "--debug-template") == 0) mSettings->debugtemplate = true; // dump cppcheck data else if (std::strcmp(argv[i], "--dump") == 0) mSettings->dump = true; // max ctu depth else if (std::strncmp(argv[i], "--max-ctu-depth=", 16) == 0) mSettings->maxCtuDepth = std::atoi(argv[i] + 16); else if (std::strcmp(argv[i], "--experimental-fast") == 0) // TODO: Remove this flag! ; // (Experimental) exception handling inside cppcheck client else if (std::strcmp(argv[i], "--exception-handling") == 0) mSettings->exceptionHandling = true; else if (std::strncmp(argv[i], "--exception-handling=", 21) == 0) { mSettings->exceptionHandling = true; const std::string exceptionOutfilename = &(argv[i][21]); CppCheckExecutor::setExceptionOutput((exceptionOutfilename=="stderr") ? stderr : stdout); } // Inconclusive checking else if (std::strcmp(argv[i], "--inconclusive") == 0) mSettings->inconclusive = true; // Experimental: Safe checking else if (std::strcmp(argv[i], "--safe-classes") == 0) mSettings->safeChecks.classes = true; // Experimental: Safe checking else if (std::strcmp(argv[i], "--safe-functions") == 0) mSettings->safeChecks.externalFunctions = mSettings->safeChecks.internalFunctions = true; // Experimental: Verify else if (std::strcmp(argv[i], "--verify") == 0) mSettings->verification = true; else if (std::strcmp(argv[i], "--debug-verify") == 0) mSettings->debugVerification = true; // Enforce language (--language=, -x) else if (std::strncmp(argv[i], "--language=", 11) == 0 || std::strcmp(argv[i], "-x") == 0) { std::string str; if (argv[i][2]) { str = argv[i]+11; } else { i++; if (i >= argc || argv[i][0] == '-') { printMessage("cppcheck: No language given to '-x' option."); return false; } str = argv[i]; } if (str == "c") mSettings->enforcedLang = Settings::C; else if (str == "c++") mSettings->enforcedLang = Settings::CPP; else { printMessage("cppcheck: Unknown language '" + str + "' enforced."); return false; } } // Filter errors else if (std::strncmp(argv[i], "--exitcode-suppressions=", 24) == 0) { // exitcode-suppressions=filename.txt std::string filename = 24 + argv[i]; std::ifstream f(filename); if (!f.is_open()) { printMessage("cppcheck: Couldn't open the file: \"" + filename + "\"."); return false; } const std::string errmsg(mSettings->nofail.parseFile(f)); if (!errmsg.empty()) { printMessage(errmsg); return false; } } // Filter errors else if (std::strncmp(argv[i], "--suppressions-list=", 20) == 0) { std::string filename = argv[i]+20; std::ifstream f(filename); if (!f.is_open()) { std::string message("cppcheck: Couldn't open the file: \""); message += filename; message += "\"."; if (std::count(filename.begin(), filename.end(), ',') > 0 || std::count(filename.begin(), filename.end(), '.') > 1) { // If user tried to pass multiple files (we can only guess that) // e.g. like this: --suppressions-list=a.txt,b.txt // print more detailed error message to tell user how he can solve the problem message += "\nIf you want to pass two files, you can do it e.g. like this:"; message += "\n cppcheck --suppressions-list=a.txt --suppressions-list=b.txt file.cpp"; } printMessage(message); return false; } const std::string errmsg(mSettings->nomsg.parseFile(f)); if (!errmsg.empty()) { printMessage(errmsg); return false; } } else if (std::strncmp(argv[i], "--suppress-xml=", 15) == 0) { const char * filename = argv[i] + 15; const std::string errmsg(mSettings->nomsg.parseXmlFile(filename)); if (!errmsg.empty()) { printMessage(errmsg); return false; } } else if (std::strncmp(argv[i], "--suppress=", 11) == 0) { const std::string suppression = argv[i]+11; const std::string errmsg(mSettings->nomsg.addSuppressionLine(suppression)); if (!errmsg.empty()) { printMessage(errmsg); return false; } } // Enables inline suppressions. else if (std::strcmp(argv[i], "--inline-suppr") == 0) mSettings->inlineSuppressions = true; // Verbose error messages (configuration info) else if (std::strcmp(argv[i], "-v") == 0 || std::strcmp(argv[i], "--verbose") == 0) mSettings->verbose = true; // Force checking of files that have "too many" configurations else if (std::strcmp(argv[i], "-f") == 0 || std::strcmp(argv[i], "--force") == 0) mSettings->force = true; // Output relative paths else if (std::strcmp(argv[i], "-rp") == 0 || std::strcmp(argv[i], "--relative-paths") == 0) mSettings->relativePaths = true; else if (std::strncmp(argv[i], "-rp=", 4) == 0 || std::strncmp(argv[i], "--relative-paths=", 17) == 0) { mSettings->relativePaths = true; if (argv[i][argv[i][3]=='='?4:17] != 0) { std::string paths = argv[i]+(argv[i][3]=='='?4:17); for (;;) { const std::string::size_type pos = paths.find(';'); if (pos == std::string::npos) { mSettings->basePaths.push_back(Path::fromNativeSeparators(paths)); break; } mSettings->basePaths.push_back(Path::fromNativeSeparators(paths.substr(0, pos))); paths.erase(0, pos + 1); } } else { printMessage("cppcheck: No paths specified for the '" + std::string(argv[i]) + "' option."); return false; } } // Write results in file else if (std::strncmp(argv[i], "--output-file=", 14) == 0) mSettings->outputFile = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 14)); // Write results in results.plist else if (std::strncmp(argv[i], "--plist-output=", 15) == 0) { mSettings->plistOutput = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 15)); if (mSettings->plistOutput.empty()) mSettings->plistOutput = "./"; else if (!endsWith(mSettings->plistOutput,'/')) mSettings->plistOutput += '/'; } // Write results in results.xml else if (std::strcmp(argv[i], "--xml") == 0) mSettings->xml = true; // Define the XML file version (and enable XML output) else if (std::strncmp(argv[i], "--xml-version=", 14) == 0) { const std::string numberString(argv[i]+14); std::istringstream iss(numberString); if (!(iss >> mSettings->xml_version)) { printMessage("cppcheck: argument to '--xml-version' is not a number."); return false; } if (mSettings->xml_version != 2) { // We only have xml version 2 printMessage("cppcheck: '--xml-version' can only be 2."); return false; } // Enable also XML if version is set mSettings->xml = true; } // Only print something when there are errors else if (std::strcmp(argv[i], "-q") == 0 || std::strcmp(argv[i], "--quiet") == 0) mSettings->quiet = true; // Check configuration else if (std::strcmp(argv[i], "--check-config") == 0) { mSettings->checkConfiguration = true; } // Check library definitions else if (std::strcmp(argv[i], "--check-library") == 0) { mSettings->checkLibrary = true; } else if (std::strncmp(argv[i], "--enable=", 9) == 0) { const std::string errmsg = mSettings->addEnabled(argv[i] + 9); if (!errmsg.empty()) { printMessage(errmsg); return false; } // when "style" is enabled, also enable "warning", "performance" and "portability" if (mSettings->isEnabled(Settings::STYLE)) { mSettings->addEnabled("warning"); mSettings->addEnabled("performance"); mSettings->addEnabled("portability"); } } // --error-exitcode=1 else if (std::strncmp(argv[i], "--error-exitcode=", 17) == 0) { const std::string temp = argv[i]+17; std::istringstream iss(temp); if (!(iss >> mSettings->exitCode)) { mSettings->exitCode = 0; printMessage("cppcheck: Argument must be an integer. Try something like '--error-exitcode=1'."); return false; } } // User define else if (std::strncmp(argv[i], "-D", 2) == 0) { std::string define; // "-D define" if (std::strcmp(argv[i], "-D") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printMessage("cppcheck: argument to '-D' is missing."); return false; } define = argv[i]; } // "-Ddefine" else { define = 2 + argv[i]; } // No "=", append a "=1" if (define.find('=') == std::string::npos) define += "=1"; if (!mSettings->userDefines.empty()) mSettings->userDefines += ";"; mSettings->userDefines += define; def = true; } // User undef else if (std::strncmp(argv[i], "-U", 2) == 0) { std::string undef; // "-U undef" if (std::strcmp(argv[i], "-U") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printMessage("cppcheck: argument to '-U' is missing."); return false; } undef = argv[i]; } // "-Uundef" else { undef = 2 + argv[i]; } mSettings->userUndefs.insert(undef); } // -E else if (std::strcmp(argv[i], "-E") == 0) { mSettings->preprocessOnly = true; mSettings->quiet = true; } // Include paths else if (std::strncmp(argv[i], "-I", 2) == 0) { std::string path; // "-I path/" if (std::strcmp(argv[i], "-I") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printMessage("cppcheck: argument to '-I' is missing."); return false; } path = argv[i]; } // "-Ipath/" else { path = 2 + argv[i]; } path = Path::removeQuotationMarks(path); path = Path::fromNativeSeparators(path); // If path doesn't end with / or \, add it if (!endsWith(path,'/')) path += '/'; mSettings->includePaths.push_back(path); } else if (std::strncmp(argv[i], "--include=", 10) == 0) { std::string path = argv[i] + 10; path = Path::fromNativeSeparators(path); mSettings->userIncludes.push_back(path); } else if (std::strncmp(argv[i], "--includes-file=", 16) == 0) { // open this file and read every input file (1 file name per line) const std::string includesFile(16 + argv[i]); if (!addIncludePathsToList(includesFile, &mSettings->includePaths)) { printMessage("Cppcheck: unable to open includes file at '" + includesFile + "'"); return false; } } else if (std::strncmp(argv[i], "--config-exclude=",17) ==0) { std::string path = argv[i] + 17; path = Path::fromNativeSeparators(path); mSettings->configExcludePaths.insert(path); } else if (std::strncmp(argv[i], "--config-excludes-file=", 23) == 0) { // open this file and read every input file (1 file name per line) const std::string cfgExcludesFile(23 + argv[i]); if (!addPathsToSet(cfgExcludesFile, &mSettings->configExcludePaths)) { printMessage("Cppcheck: unable to open config excludes file at '" + cfgExcludesFile + "'"); return false; } } // file list specified else if (std::strncmp(argv[i], "--file-list=", 12) == 0) { // open this file and read every input file (1 file name per line) addFilesToList(12 + argv[i], mPathNames); } // Ignored paths else if (std::strncmp(argv[i], "-i", 2) == 0) { std::string path; // "-i path/" if (std::strcmp(argv[i], "-i") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printMessage("cppcheck: argument to '-i' is missing."); return false; } path = argv[i]; } // "-ipath/" else { path = 2 + argv[i]; } if (!path.empty()) { path = Path::removeQuotationMarks(path); path = Path::fromNativeSeparators(path); path = Path::simplifyPath(path); if (FileLister::isDirectory(path)) { // If directory name doesn't end with / or \, add it if (!endsWith(path, '/')) path += '/'; } mIgnoredPaths.push_back(path); } } // --library else if (std::strncmp(argv[i], "--library=", 10) == 0) { std::string lib(argv[i] + 10); mSettings->libraries.push_back(lib); } // --project else if (std::strncmp(argv[i], "--project=", 10) == 0) { mSettings->checkAllConfigurations = false; // Can be overridden with --max-configs or --force const std::string projectFile = argv[i]+10; ImportProject::Type projType = mSettings->project.import(projectFile, mSettings); if (projType == ImportProject::Type::CPPCHECK_GUI) { mPathNames = mSettings->project.guiProject.pathNames; for (const std::string &lib : mSettings->project.guiProject.libraries) mSettings->libraries.push_back(lib); for (const std::string &ignorePath : mSettings->project.guiProject.excludedPaths) mIgnoredPaths.emplace_back(ignorePath); const std::string platform(mSettings->project.guiProject.platform); if (platform == "win32A") mSettings->platform(Settings::Win32A); else if (platform == "win32W") mSettings->platform(Settings::Win32W); else if (platform == "win64") mSettings->platform(Settings::Win64); else if (platform == "unix32") mSettings->platform(Settings::Unix32); else if (platform == "unix64") mSettings->platform(Settings::Unix64); else if (platform == "native") mSettings->platform(Settings::Native); else if (platform == "unspecified" || platform == "Unspecified" || platform == "") ; else if (!mSettings->loadPlatformFile(argv[0], platform)) { std::string message("cppcheck: error: unrecognized platform: \""); message += platform; message += "\"."; printMessage(message); return false; } if (!mSettings->project.guiProject.projectFile.empty()) projType = mSettings->project.import(mSettings->project.guiProject.projectFile, mSettings); } if (projType == ImportProject::Type::VS_SLN || projType == ImportProject::Type::VS_VCXPROJ) { if (mSettings->project.guiProject.analyzeAllVsConfigs == "false") mSettings->project.selectOneVsConfig(mSettings->platformType); if (!CppCheckExecutor::tryLoadLibrary(mSettings->library, argv[0], "windows.cfg")) { // This shouldn't happen normally. printMessage("cppcheck: Failed to load 'windows.cfg'. Your Cppcheck installation is broken. Please re-install."); return false; } } if (projType == ImportProject::Type::MISSING) { printMessage("cppcheck: Failed to open project '" + projectFile + "'."); return false; } if (projType == ImportProject::Type::UNKNOWN) { printMessage("cppcheck: Failed to load project '" + projectFile + "'. The format is unknown."); return false; } } // Report progress else if (std::strcmp(argv[i], "--report-progress") == 0) { mSettings->reportProgress = true; } // --std else if (std::strcmp(argv[i], "--std=posix") == 0) { printMessage("cppcheck: Option --std=posix is deprecated and will be removed in 1.95."); } else if (std::strcmp(argv[i], "--std=c89") == 0) { mSettings->standards.c = Standards::C89; } else if (std::strcmp(argv[i], "--std=c99") == 0) { mSettings->standards.c = Standards::C99; } else if (std::strcmp(argv[i], "--std=c11") == 0) { mSettings->standards.c = Standards::C11; } else if (std::strcmp(argv[i], "--std=c++03") == 0) { mSettings->standards.cpp = Standards::CPP03; } else if (std::strcmp(argv[i], "--std=c++11") == 0) { mSettings->standards.cpp = Standards::CPP11; } else if (std::strcmp(argv[i], "--std=c++14") == 0) { mSettings->standards.cpp = Standards::CPP14; } else if (std::strcmp(argv[i], "--std=c++17") == 0) { mSettings->standards.cpp = Standards::CPP17; } else if (std::strcmp(argv[i], "--std=c++20") == 0) { mSettings->standards.cpp = Standards::CPP20; } // Output formatter else if (std::strcmp(argv[i], "--template") == 0 || std::strncmp(argv[i], "--template=", 11) == 0) { // "--template format" if (argv[i][10] == '=') mSettings->templateFormat = argv[i] + 11; else if ((i+1) < argc && argv[i+1][0] != '-') { ++i; mSettings->templateFormat = argv[i]; } else { printMessage("cppcheck: argument to '--template' is missing."); return false; } if (mSettings->templateFormat == "gcc") { mSettings->templateFormat = "{file}:{line}:{column}: warning: {message} [{id}]\\n{code}"; mSettings->templateLocation = "{file}:{line}:{column}: note: {info}\\n{code}"; } else if (mSettings->templateFormat == "daca2") { mSettings->templateFormat = "{file}:{line}:{column}: {severity}: {message} [{id}]"; mSettings->templateLocation = "{file}:{line}:{column}: note: {info}"; } else if (mSettings->templateFormat == "vs") mSettings->templateFormat = "{file}({line}): {severity}: {message}"; else if (mSettings->templateFormat == "edit") mSettings->templateFormat = "{file} +{line}: {severity}: {message}"; else if (mSettings->templateFormat == "cppcheck1") mSettings->templateFormat = "{callstack}: ({severity}{inconclusive:, inconclusive}) {message}"; } else if (std::strcmp(argv[i], "--template-location") == 0 || std::strncmp(argv[i], "--template-location=", 20) == 0) { // "--template-location format" if (argv[i][19] == '=') mSettings->templateLocation = argv[i] + 20; else if ((i+1) < argc && argv[i+1][0] != '-') { ++i; mSettings->templateLocation = argv[i]; } else { printMessage("cppcheck: argument to '--template' is missing."); return false; } } // Checking threads else if (std::strncmp(argv[i], "-j", 2) == 0) { std::string numberString; // "-j 3" if (std::strcmp(argv[i], "-j") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printMessage("cppcheck: argument to '-j' is missing."); return false; } numberString = argv[i]; } // "-j3" else numberString = argv[i]+2; std::istringstream iss(numberString); if (!(iss >> mSettings->jobs)) { printMessage("cppcheck: argument to '-j' is not a number."); return false; } if (mSettings->jobs > 10000) { // This limit is here just to catch typos. If someone has // need for more jobs, this value should be increased. printMessage("cppcheck: argument for '-j' is allowed to be 10000 at max."); return false; } } else if (std::strncmp(argv[i], "-l", 2) == 0) { std::string numberString; // "-l 3" if (std::strcmp(argv[i], "-l") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printMessage("cppcheck: argument to '-l' is missing."); return false; } numberString = argv[i]; } // "-l3" else numberString = argv[i]+2; std::istringstream iss(numberString); if (!(iss >> mSettings->loadAverage)) { printMessage("cppcheck: argument to '-l' is not a number."); return false; } } // print all possible error messages.. else if (std::strcmp(argv[i], "--errorlist") == 0) { mShowErrorMessages = true; mSettings->xml = true; mExitAfterPrint = true; } // documentation.. else if (std::strcmp(argv[i], "--doc") == 0) { std::ostringstream doc; // Get documentation.. for (const Check * it : Check::instances()) { const std::string& name(it->name()); const std::string info(it->classInfo()); if (!name.empty() && !info.empty()) doc << "## " << name << " ##\n" << info << "\n"; } std::cout << doc.str(); mExitAfterPrint = true; return true; } // show timing information.. else if (std::strncmp(argv[i], "--showtime=", 11) == 0) { const std::string showtimeMode = argv[i] + 11; if (showtimeMode == "file") mSettings->showtime = SHOWTIME_MODES::SHOWTIME_FILE; else if (showtimeMode == "summary") mSettings->showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY; else if (showtimeMode == "top5") mSettings->showtime = SHOWTIME_MODES::SHOWTIME_TOP5; else if (showtimeMode.empty()) mSettings->showtime = SHOWTIME_MODES::SHOWTIME_NONE; else { std::string message("cppcheck: error: unrecognized showtime mode: \""); message += showtimeMode; message += "\". Supported modes: file, summary, top5."; printMessage(message); return false; } } #ifdef HAVE_RULES // Rule given at command line else if (std::strncmp(argv[i], "--rule=", 7) == 0) { Settings::Rule rule; rule.pattern = 7 + argv[i]; mSettings->rules.push_back(rule); } // Rule file else if (std::strncmp(argv[i], "--rule-file=", 12) == 0) { tinyxml2::XMLDocument doc; if (doc.LoadFile(12+argv[i]) == tinyxml2::XML_SUCCESS) { tinyxml2::XMLElement *node = doc.FirstChildElement(); for (; node && strcmp(node->Value(), "rule") == 0; node = node->NextSiblingElement()) { Settings::Rule rule; tinyxml2::XMLElement *tokenlist = node->FirstChildElement("tokenlist"); if (tokenlist) rule.tokenlist = tokenlist->GetText(); tinyxml2::XMLElement *pattern = node->FirstChildElement("pattern"); if (pattern) { rule.pattern = pattern->GetText(); } tinyxml2::XMLElement *message = node->FirstChildElement("message"); if (message) { tinyxml2::XMLElement *severity = message->FirstChildElement("severity"); if (severity) rule.severity = Severity::fromString(severity->GetText()); tinyxml2::XMLElement *id = message->FirstChildElement("id"); if (id) rule.id = id->GetText(); tinyxml2::XMLElement *summary = message->FirstChildElement("summary"); if (summary) rule.summary = summary->GetText() ? summary->GetText() : ""; } if (!rule.pattern.empty()) mSettings->rules.push_back(rule); } } else { printMessage("cppcheck: error: unable to load rule-file: " + std::string(12+argv[i])); return false; } } #endif // Specify platform else if (std::strncmp(argv[i], "--platform=", 11) == 0) { const std::string platform(11+argv[i]); if (platform == "win32A") mSettings->platform(Settings::Win32A); else if (platform == "win32W") mSettings->platform(Settings::Win32W); else if (platform == "win64") mSettings->platform(Settings::Win64); else if (platform == "unix32") mSettings->platform(Settings::Unix32); else if (platform == "unix64") mSettings->platform(Settings::Unix64); else if (platform == "native") mSettings->platform(Settings::Native); else if (platform == "unspecified") mSettings->platform(Settings::Unspecified); else if (!mSettings->loadPlatformFile(argv[0], platform)) { std::string message("cppcheck: error: unrecognized platform: \""); message += platform; message += "\"."; printMessage(message); return false; } } // Set maximum number of #ifdef configurations to check else if (std::strncmp(argv[i], "--max-configs=", 14) == 0) { mSettings->force = false; std::istringstream iss(14+argv[i]); if (!(iss >> mSettings->maxConfigs)) { printMessage("cppcheck: argument to '--max-configs=' is not a number."); return false; } if (mSettings->maxConfigs < 1) { printMessage("cppcheck: argument to '--max-configs=' must be greater than 0."); return false; } maxconfigs = true; } // Print help else if (std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "--help") == 0) { mPathNames.clear(); mShowHelp = true; mExitAfterPrint = true; break; } else { std::string message("cppcheck: error: unrecognized command line option: \""); message += argv[i]; message += "\"."; printMessage(message); return false; } } else { std::string path = Path::removeQuotationMarks(argv[i]); path = Path::fromNativeSeparators(path); mPathNames.push_back(path); } } // Default template format.. if (mSettings->templateFormat.empty()) { mSettings->templateFormat = "{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}"; if (mSettings->templateLocation.empty()) mSettings->templateLocation = "{file}:{line}:{column}: note: {info}\\n{code}"; } mSettings->project.ignorePaths(mIgnoredPaths); if (mSettings->force || maxconfigs) mSettings->checkAllConfigurations = true; if (mSettings->force) mSettings->maxConfigs = ~0U; else if ((def || mSettings->preprocessOnly) && !maxconfigs) mSettings->maxConfigs = 1U; if (mSettings->isEnabled(Settings::UNUSED_FUNCTION) && mSettings->jobs > 1) { printMessage("cppcheck: unusedFunction check can't be used with '-j' option. Disabling unusedFunction check."); } if (argc <= 1) { mShowHelp = true; mExitAfterPrint = true; } if (mShowHelp) { printHelp(); return true; } // Print error only if we have "real" command and expect files if (!mExitAfterPrint && mPathNames.empty() && mSettings->project.fileSettings.empty()) { printMessage("cppcheck: No C or C++ source files found."); return false; } // Use paths _pathnames if no base paths for relative path output are given if (mSettings->basePaths.empty() && mSettings->relativePaths) mSettings->basePaths = mPathNames; return true; } void CmdLineParser::printHelp() { std::cout << "Cppcheck - A tool for static C/C++ code analysis\n" "\n" "Syntax:\n" " cppcheck [OPTIONS] [files or paths]\n" "\n" "If a directory is given instead of a filename, *.cpp, *.cxx, *.cc, *.c++, *.c,\n" "*.tpp, and *.txx files are checked recursively from the given directory.\n\n" "Options:\n" " --addon=\n" " Execute addon. i.e. cert.\n" " --cppcheck-build-dir=\n" " Analysis output directory. Useful for various data.\n" " Some possible usages are; whole program analysis,\n" " incremental analysis, distributed analysis.\n" " --check-config Check cppcheck configuration. The normal code\n" " analysis is disabled by this flag.\n" " --check-library Show information messages when library files have\n" " incomplete info.\n" " --config-exclude=\n" " Path (prefix) to be excluded from configuration\n" " checking. Preprocessor configurations defined in\n" " headers (but not sources) matching the prefix will not\n" " be considered for evaluation.\n" " --config-excludes-file=\n" " A file that contains a list of config-excludes\n" " --doc Print a list of all available checks.\n" " --dump Dump xml data for each translation unit. The dump\n" " files have the extension .dump and contain ast,\n" " tokenlist, symboldatabase, valueflow.\n" " -D Define preprocessor symbol. Unless --max-configs or\n" " --force is used, Cppcheck will only check the given\n" " configuration when -D is used.\n" " Example: '-DDEBUG=1 -D__cplusplus'.\n" " -U Undefine preprocessor symbol. Use -U to explicitly\n" " hide certain #ifdef code paths from checking.\n" " Example: '-UDEBUG'\n" " -E Print preprocessor output on stdout and don't do any\n" " further processing.\n" " --enable= Enable additional checks. The available ids are:\n" " * all\n" " Enable all checks. It is recommended to only\n" " use --enable=all when the whole program is\n" " scanned, because this enables unusedFunction.\n" " * warning\n" " Enable warning messages\n" " * style\n" " Enable all coding style checks. All messages\n" " with the severities 'style', 'performance' and\n" " 'portability' are enabled.\n" " * performance\n" " Enable performance messages\n" " * portability\n" " Enable portability messages\n" " * information\n" " Enable information messages\n" " * unusedFunction\n" " Check for unused functions. It is recommend\n" " to only enable this when the whole program is\n" " scanned.\n" " * missingInclude\n" " Warn if there are missing includes. For\n" " detailed information, use '--check-config'.\n" " Several ids can be given if you separate them with\n" " commas. See also --std\n" " --error-exitcode= If errors are found, integer [n] is returned instead of\n" " the default '0'. '" << EXIT_FAILURE << "' is returned\n" " if arguments are not valid or if no input files are\n" " provided. Note that your operating system can modify\n" " this value, e.g. '256' can become '0'.\n" " --errorlist Print a list of all the error messages in XML format.\n" " --exitcode-suppressions=\n" " Used when certain messages should be displayed but\n" " should not cause a non-zero exitcode.\n" " --file-list= Specify the files to check in a text file. Add one\n" " filename per line. When file is '-,' the file list will\n" " be read from standard input.\n" " -f, --force Force checking of all configurations in files. If used\n" " together with '--max-configs=', the last option is the\n" " one that is effective.\n" " -h, --help Print this help.\n" " -I Give path to search for include files. Give several -I\n" " parameters to give several paths. First given path is\n" " searched for contained header files first. If paths are\n" " relative to source files, this is not needed.\n" " --includes-file=\n" " Specify directory paths to search for included header\n" " files in a text file. Add one include path per line.\n" " First given path is searched for contained header\n" " files first. If paths are relative to source files,\n" " this is not needed.\n" " --include=\n" " Force inclusion of a file before the checked file. Can\n" " be used for example when checking the Linux kernel,\n" " where autoconf.h needs to be included for every file\n" " compiled. Works the same way as the GCC -include\n" " option.\n" " -i Give a source file or source file directory to exclude\n" " from the check. This applies only to source files so\n" " header files included by source files are not matched.\n" " Directory name is matched to all parts of the path.\n" " --inconclusive Allow that Cppcheck reports even though the analysis is\n" " inconclusive.\n" " There are false positives with this option. Each result\n" " must be carefully investigated before you know if it is\n" " good or bad.\n" " --inline-suppr Enable inline suppressions. Use them by placing one or\n" " more comments, like: '// cppcheck-suppress warningId'\n" " on the lines before the warning to suppress.\n" " -j Start threads to do the checking simultaneously.\n" #ifdef THREADING_MODEL_FORK " -l Specifies that no new threads should be started if\n" " there are other threads running and the load average is\n" " at least .\n" #endif " --language=, -x \n" " Forces cppcheck to check all files as the given\n" " language. Valid values are: c, c++\n" " --library= Load file that contains information about types\n" " and functions. With such information Cppcheck\n" " understands your code better and therefore you\n" " get better results. The std.cfg file that is\n" " distributed with Cppcheck is loaded automatically.\n" " For more information about library files, read the\n" " manual.\n" " --max-ctu-depth=N Max depth in whole program analysis. The default value\n" " is 2. A larger value will mean more errors can be found\n" " but also means the analysis will be slower.\n" " --output-file= Write results to file, rather than standard error.\n" " --project= Run Cppcheck on project. The can be a Visual\n" " Studio Solution (*.sln), Visual Studio Project\n" " (*.vcxproj), compile database (compile_commands.json),\n" " or Borland C++ Builder 6 (*.bpr). The files to analyse,\n" " include paths, defines, platform and undefines in\n" " the specified file will be used.\n" " --max-configs=\n" " Maximum number of configurations to check in a file\n" " before skipping it. Default is '12'. If used together\n" " with '--force', the last option is the one that is\n" " effective.\n" " --platform=, --platform=\n" " Specifies platform specific types and sizes. The\n" " available builtin platforms are:\n" " * unix32\n" " 32 bit unix variant\n" " * unix64\n" " 64 bit unix variant\n" " * win32A\n" " 32 bit Windows ASCII character encoding\n" " * win32W\n" " 32 bit Windows UNICODE character encoding\n" " * win64\n" " 64 bit Windows\n" " * avr8\n" " 8 bit AVR microcontrollers\n" " * native\n" " Type sizes of host system are assumed, but no\n" " further assumptions.\n" " * unspecified\n" " Unknown type sizes\n" " --plist-output=\n" " Generate Clang-plist output files in folder.\n" " -q, --quiet Do not show progress reports.\n" " -rp, --relative-paths\n" " -rp=, --relative-paths=\n" " Use relative paths in output. When given, are\n" " used as base. You can separate multiple paths by ';'.\n" " Otherwise path where source files are searched is used.\n" " We use string comparison to create relative paths, so\n" " using e.g. ~ for home folder does not work. It is\n" " currently only possible to apply the base paths to\n" " files that are on a lower level in the directory tree.\n" " --report-progress Report progress messages while checking a file.\n" #ifdef HAVE_RULES " --rule= Match regular expression.\n" " --rule-file= Use given rule file. For more information, see: \n" " http://sourceforge.net/projects/cppcheck/files/Articles/\n" #endif " --std= Set standard.\n" " The available options are:\n" " * c89\n" " C code is C89 compatible\n" " * c99\n" " C code is C99 compatible\n" " * c11\n" " C code is C11 compatible (default)\n" " * c++03\n" " C++ code is C++03 compatible\n" " * c++11\n" " C++ code is C++11 compatible\n" " * c++14\n" " C++ code is C++14 compatible\n" " * c++17\n" " C++ code is C++17 compatible\n" " * c++20\n" " C++ code is C++20 compatible (default)\n" " --suppress= Suppress warnings that match . The format of\n" " is:\n" " [error id]:[filename]:[line]\n" " The [filename] and [line] are optional. If [error id]\n" " is a wildcard '*', all error ids match.\n" " --suppressions-list=\n" " Suppress warnings listed in the file. Each suppression\n" " is in the same format as above.\n" " --suppress-xml=\n" " Suppress warnings listed in a xml file. XML file should\n" " follow the manual.pdf format specified in section.\n" " `6.4 XML suppressions` .\n" " --template='' Format the error messages. Available fields:\n" " {file} file name\n" " {line} line number\n" " {column} column number\n" " {callstack} show a callstack. Example:\n" " [file.c:1] -> [file.c:100]\n" " {inconclusive:text} if warning is inconclusive, text\n" " is written\n" " {severity} severity\n" " {message} warning message\n" " {id} warning id\n" " {cwe} CWE id (Common Weakness Enumeration)\n" " {code} show the real code\n" " \\t insert tab\n" " \\n insert newline\n" " \\r insert carriage return\n" " Example formats:\n" " '{file}:{line},{severity},{id},{message}' or\n" " '{file}({line}):({severity}) {message}' or\n" " '{callstack} {message}'\n" " Pre-defined templates: gcc (default), cppcheck1 (old default), vs, edit.\n" // Note: template daca2 also exists, but is for internal use (cppcheck scripts). " --template-location=''\n" " Format error message location. If this is not provided\n" " then no extra location info is shown.\n" " Available fields:\n" " {file} file name\n" " {line} line number\n" " {column} column number\n" " {info} location info\n" " {code} show the real code\n" " \\t insert tab\n" " \\n insert newline\n" " \\r insert carriage return\n" " Example format (gcc-like):\n" " '{file}:{line}:{column}: note: {info}\\n{code}'\n" " -v, --verbose Output more detailed error information.\n" " --version Print out version number.\n" " --xml Write results in xml format to error stream (stderr).\n" " --xml-version=\n" " Select the XML file version. Currently only versions 2 is available." "\n" "Example usage:\n" " # Recursively check the current folder. Print the progress on the screen and\n" " # write errors to a file:\n" " cppcheck . 2> err.txt\n" "\n" " # Recursively check ../myproject/ and don't print progress:\n" " cppcheck --quiet ../myproject/\n" "\n" " # Check test.cpp, enable all checks:\n" " cppcheck --enable=all --inconclusive --library=posix test.cpp\n" "\n" " # Check f.cpp and search include files from inc1/ and inc2/:\n" " cppcheck -I inc1/ -I inc2/ f.cpp\n" "\n" "For more information:\n" " http://cppcheck.net/manual.pdf\n" "\n" "Many thanks to the 3rd party libraries we use:\n" " * tinyxml2 -- loading project/library/ctu files.\n" " * picojson -- loading compile database.\n" " * pcre -- rules.\n" " * qt -- used in GUI\n"; } cppcheck-1.90/cli/cmdlineparser.h000066400000000000000000000057221357737443600170240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CMDLINE_PARSER_H #define CMDLINE_PARSER_H #include #include class Settings; /// @addtogroup CLI /// @{ /** * @brief The command line parser. * The command line parser parses options and parameters user gives to * cppcheck command line. * * The parser takes a pointer to Settings instance which it will update * based on options user has given. Couple of options are handled as * class internal options. */ class CmdLineParser { public: /** * The constructor. * @param settings Settings instance that will be modified according to * options user has given. */ explicit CmdLineParser(Settings *settings); /** * Parse given command line. * @return true if command line was ok, false if there was an error. */ bool parseFromArgs(int argc, const char* const argv[]); /** * Return if user wanted to see program version. */ bool getShowVersion() const { return mShowVersion; } /** * Return if user wanted to see list of error messages. */ bool getShowErrorMessages() const { return mShowErrorMessages; } /** * Return the path names user gave to command line. */ const std::vector& getPathNames() const { return mPathNames; } /** * Return if help is shown to user. */ bool getShowHelp() const { return mShowHelp; } /** * Return if we should exit after printing version, help etc. */ bool exitAfterPrinting() const { return mExitAfterPrint; } /** * Return a list of paths user wants to ignore. */ const std::vector& getIgnoredPaths() const { return mIgnoredPaths; } protected: /** * Print help text to the console. */ static void printHelp(); /** * Print message (to console?). */ static void printMessage(const std::string &message); static void printMessage(const char* message); private: std::vector mPathNames; std::vector mIgnoredPaths; Settings *mSettings; bool mShowHelp; bool mShowVersion; bool mShowErrorMessages; bool mExitAfterPrint; }; /// @} #endif // CMDLINE_PARSER_H cppcheck-1.90/cli/cppcheckexecutor.cpp000066400000000000000000001232161357737443600200650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppcheckexecutor.h" #include "analyzerinfo.h" #include "cmdlineparser.h" #include "config.h" #include "cppcheck.h" #include "filelister.h" #include "importproject.h" #include "library.h" #include "path.h" #include "pathmatch.h" #include "preprocessor.h" #include "settings.h" #include "standards.h" #include "suppressions.h" #include "threadexecutor.h" #include "utils.h" #include "checkunusedfunctions.h" #include #include #include // EXIT_SUCCESS and EXIT_FAILURE #include #include #include #include #include #if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__OS2__) #define USE_UNIX_SIGNAL_HANDLING #include #if defined(__APPLE__) # define _XOPEN_SOURCE // ucontext.h APIs can only be used on Mac OSX >= 10.7 if _XOPEN_SOURCE is defined # include # undef _XOPEN_SOURCE #elif !defined(__OpenBSD__) # include #endif #ifdef __linux__ #include #include #endif #endif #if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(USE_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && defined(__GLIBC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__NetBSD__) && !defined(__SVR4) && !defined(__QNX__) #define USE_UNIX_BACKTRACE_SUPPORT #include #include #endif #if defined(_MSC_VER) #define USE_WINDOWS_SEH #include #include #include #include #endif /*static*/ FILE* CppCheckExecutor::mExceptionOutput = stdout; CppCheckExecutor::CppCheckExecutor() : mSettings(nullptr), mLatestProgressOutputTime(0), mErrorOutput(nullptr), mShowAllErrors(false) { } CppCheckExecutor::~CppCheckExecutor() { delete mErrorOutput; } bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* const argv[]) { Settings& settings = cppcheck->settings(); CmdLineParser parser(&settings); const bool success = parser.parseFromArgs(argc, argv); if (success) { if (parser.getShowVersion() && !parser.getShowErrorMessages()) { const char * const extraVersion = CppCheck::extraVersion(); if (*extraVersion != 0) std::cout << "Cppcheck " << CppCheck::version() << " (" << extraVersion << ')' << std::endl; else std::cout << "Cppcheck " << CppCheck::version() << std::endl; } if (parser.getShowErrorMessages()) { mShowAllErrors = true; std::cout << ErrorLogger::ErrorMessage::getXMLHeader(); cppcheck->getErrorMessages(); std::cout << ErrorLogger::ErrorMessage::getXMLFooter() << std::endl; } if (parser.exitAfterPrinting()) { Settings::terminate(); return true; } } else { return false; } // Check that all include paths exist { for (std::list::iterator iter = settings.includePaths.begin(); iter != settings.includePaths.end(); ) { const std::string path(Path::toNativeSeparators(*iter)); if (FileLister::isDirectory(path)) ++iter; else { // If the include path is not found, warn user and remove the non-existing path from the list. if (settings.isEnabled(Settings::INFORMATION)) std::cout << "(information) Couldn't find path given by -I '" << path << '\'' << std::endl; iter = settings.includePaths.erase(iter); } } } // Output a warning for the user if he tries to exclude headers bool warn = false; const std::vector& ignored = parser.getIgnoredPaths(); for (const std::string &i : ignored) { if (Path::isHeader(i)) { warn = true; break; } } if (warn) { std::cout << "cppcheck: filename exclusion does not apply to header (.h and .hpp) files." << std::endl; std::cout << "cppcheck: Please use --suppress for ignoring results from the header files." << std::endl; } const std::vector& pathnames = parser.getPathNames(); #if defined(_WIN32) // For Windows we want case-insensitive path matching const bool caseSensitive = false; #else const bool caseSensitive = true; #endif if (!pathnames.empty()) { // Execute recursiveAddFiles() to each given file parameter const PathMatch matcher(ignored, caseSensitive); for (const std::string &pathname : pathnames) FileLister::recursiveAddFiles(mFiles, Path::toNativeSeparators(pathname), mSettings->library.markupExtensions(), matcher); } if (mFiles.empty() && settings.project.fileSettings.empty()) { std::cout << "cppcheck: error: could not find or open any of the paths given." << std::endl; if (!ignored.empty()) std::cout << "cppcheck: Maybe all paths were ignored?" << std::endl; return false; } return true; } int CppCheckExecutor::check(int argc, const char* const argv[]) { Preprocessor::missingIncludeFlag = false; Preprocessor::missingSystemIncludeFlag = false; CheckUnusedFunctions::clear(); CppCheck cppCheck(*this, true); const Settings& settings = cppCheck.settings(); mSettings = &settings; if (!parseFromArgs(&cppCheck, argc, argv)) { return EXIT_FAILURE; } if (Settings::terminated()) { return EXIT_SUCCESS; } if (cppCheck.settings().exceptionHandling) { return check_wrapper(cppCheck, argc, argv); } return check_internal(cppCheck, argc, argv); } void CppCheckExecutor::setSettings(const Settings &settings) { mSettings = &settings; } /** * Simple helper function: * \return size of array * */ template std::size_t getArrayLength(const T(&)[size]) { return size; } #if defined(USE_UNIX_SIGNAL_HANDLING) /* * Try to print the callstack. * That is very sensitive to the operating system, hardware, compiler and runtime. * The code is not meant for production environment! * One reason is named first: it's using functions not whitelisted for usage in a signal handler function. */ static void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem) { #if defined(USE_UNIX_BACKTRACE_SUPPORT) // 32 vs. 64bit #define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8) const int fd = fileno(output); void *callstackArray[32]= {nullptr}; // the less resources the better... const int currentdepth = backtrace(callstackArray, (int)getArrayLength(callstackArray)); const int offset=2; // some entries on top are within our own exception handling code or libc if (maxdepth<0) maxdepth=currentdepth-offset; else maxdepth = std::min(maxdepth, currentdepth); if (lowMem) { fputs("Callstack (symbols only):\n", output); backtrace_symbols_fd(callstackArray+offset, maxdepth, fd); } else { char **symbolStringList = backtrace_symbols(callstackArray, currentdepth); if (symbolStringList) { fputs("Callstack:\n", output); for (int i = offset; i < maxdepth; ++i) { const char * const symbolString = symbolStringList[i]; char * realnameString = nullptr; const char * const firstBracketName = strchr(symbolString, '('); const char * const firstBracketAddress = strchr(symbolString, '['); const char * const secondBracketAddress = strchr(firstBracketAddress, ']'); const char * const beginAddress = firstBracketAddress+3; const int addressLen = int(secondBracketAddress-beginAddress); const int padLen = int(ADDRESSDISPLAYLENGTH-addressLen); if (demangling && firstBracketName) { const char * const plus = strchr(firstBracketName, '+'); if (plus && (plus>(firstBracketName+1))) { char input_buffer[1024]= {0}; strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1); char output_buffer[2048]= {0}; size_t length = getArrayLength(output_buffer); int status=0; // We're violating the specification - passing stack address instead of malloc'ed heap. // Benefit is that no further heap is required, while there is sufficient stack... realnameString = abi::__cxa_demangle(input_buffer, output_buffer, &length, &status); // non-NULL on success } } const int ordinal=i-offset; fprintf(output, "#%-2d 0x", ordinal); if (padLen>0) fprintf(output, "%0*d", padLen, 0); if (realnameString) { fprintf(output, "%.*s in %s\n", (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, realnameString); } else { fprintf(output, "%.*s in %.*s\n", (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, (int)(firstBracketAddress-symbolString), symbolString); } } free(symbolStringList); } else { fputs("Callstack could not be obtained\n", output); } } #undef ADDRESSDISPLAYLENGTH #else UNUSED(output); UNUSED(demangling); UNUSED(maxdepth); UNUSED(lowMem); #endif } static const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer static char mytstack[MYSTACKSIZE]= {0}; // alternative stack for signal handler static bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space. See CppCheckExecutor::check_wrapper() /** * \param[in] ptr address to be examined. * \return true if address is supposed to be on stack (contrary to heap). If ptr is 0 false will be returned. * If unknown better return false. */ static bool IsAddressOnStack(const void* ptr) { if (nullptr==ptr) return false; char a; if (bStackBelowHeap) return ptr < &a; else return ptr > &a; } /* (declare this list here, so it may be used in signal handlers in addition to main()) * A list of signals available in ISO C * Check out http://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html * For now we only want to detect abnormal behaviour for a few selected signals: */ #define DECLARE_SIGNAL(x) std::make_pair(x, #x) typedef std::map Signalmap_t; static const Signalmap_t listofsignals = { DECLARE_SIGNAL(SIGABRT), DECLARE_SIGNAL(SIGBUS), DECLARE_SIGNAL(SIGFPE), DECLARE_SIGNAL(SIGILL), DECLARE_SIGNAL(SIGINT), DECLARE_SIGNAL(SIGQUIT), DECLARE_SIGNAL(SIGSEGV), DECLARE_SIGNAL(SIGSYS), // don't care: SIGTERM DECLARE_SIGNAL(SIGUSR1), //DECLARE_SIGNAL(SIGUSR2) no usage currently }; #undef DECLARE_SIGNAL /* * Entry pointer for signal handlers * It uses functions which are not safe to be called from a signal handler, * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04 has a whitelist) * but when ending up here something went terribly wrong anyway. * And all which is left is just printing some information and terminate. */ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context) { int type = -1; pid_t killid; #if defined(__linux__) && defined(REG_ERR) const ucontext_t* const uc = reinterpret_cast(context); killid = (pid_t) syscall(SYS_gettid); if (uc) { type = (int)uc->uc_mcontext.gregs[REG_ERR] & 2; } #else UNUSED(context); killid = getpid(); #endif const Signalmap_t::const_iterator it=listofsignals.find(signo); const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str(); bool printCallstack=true; // try to print a callstack? bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then. bool unexpectedSignal=true; // unexpected indicates program failure bool terminate=true; // exit process/thread const bool isAddressOnStack = IsAddressOnStack(info->si_addr); FILE* output = CppCheckExecutor::getExceptionOutput(); switch (signo) { case SIGABRT: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); fputs( #ifdef NDEBUG " - out of memory?\n", #else " - out of memory or assertion?\n", #endif output); lowMem=true; // educated guess break; case SIGBUS: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); switch (info->si_code) { case BUS_ADRALN: // invalid address alignment fputs(" - BUS_ADRALN", output); break; case BUS_ADRERR: // nonexistent physical address fputs(" - BUS_ADRERR", output); break; case BUS_OBJERR: // object-specific hardware error fputs(" - BUS_OBJERR", output); break; #ifdef BUS_MCEERR_AR case BUS_MCEERR_AR: // Hardware memory error consumed on a machine check; fputs(" - BUS_MCEERR_AR", output); break; #endif #ifdef BUS_MCEERR_AO case BUS_MCEERR_AO: // Hardware memory error detected in process but not consumed fputs(" - BUS_MCEERR_AO", output); break; #endif default: break; } fprintf(output, " (at 0x%lx).\n", (unsigned long)info->si_addr); break; case SIGFPE: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); switch (info->si_code) { case FPE_INTDIV: // integer divide by zero fputs(" - FPE_INTDIV", output); break; case FPE_INTOVF: // integer overflow fputs(" - FPE_INTOVF", output); break; case FPE_FLTDIV: // floating-point divide by zero fputs(" - FPE_FLTDIV", output); break; case FPE_FLTOVF: // floating-point overflow fputs(" - FPE_FLTOVF", output); break; case FPE_FLTUND: // floating-point underflow fputs(" - FPE_FLTUND", output); break; case FPE_FLTRES: // floating-point inexact result fputs(" - FPE_FLTRES", output); break; case FPE_FLTINV: // floating-point invalid operation fputs(" - FPE_FLTINV", output); break; case FPE_FLTSUB: // subscript out of range fputs(" - FPE_FLTSUB", output); break; default: break; } fprintf(output, " (at 0x%lx).\n", (unsigned long)info->si_addr); break; case SIGILL: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); switch (info->si_code) { case ILL_ILLOPC: // illegal opcode fputs(" - ILL_ILLOPC", output); break; case ILL_ILLOPN: // illegal operand fputs(" - ILL_ILLOPN", output); break; case ILL_ILLADR: // illegal addressing mode fputs(" - ILL_ILLADR", output); break; case ILL_ILLTRP: // illegal trap fputs(" - ILL_ILLTRP", output); break; case ILL_PRVOPC: // privileged opcode fputs(" - ILL_PRVOPC", output); break; case ILL_PRVREG: // privileged register fputs(" - ILL_PRVREG", output); break; case ILL_COPROC: // coprocessor error fputs(" - ILL_COPROC", output); break; case ILL_BADSTK: // internal stack error fputs(" - ILL_BADSTK", output); break; default: break; } fprintf(output, " (at 0x%lx).%s\n", (unsigned long)info->si_addr, (isAddressOnStack)?" Stackoverflow?":""); break; case SIGINT: unexpectedSignal=false; // legal usage: interrupt application via CTRL-C fputs("cppcheck received signal ", output); fputs(signame, output); printCallstack=true; fputs(".\n", output); break; case SIGSEGV: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); switch (info->si_code) { case SEGV_MAPERR: // address not mapped to object fputs(" - SEGV_MAPERR", output); break; case SEGV_ACCERR: // invalid permissions for mapped object fputs(" - SEGV_ACCERR", output); break; default: break; } fprintf(output, " (%sat 0x%lx).%s\n", // cppcheck-suppress knownConditionTrueFalse ; FP (type==-1)? "" : (type==0) ? "reading " : "writing ", (unsigned long)info->si_addr, (isAddressOnStack)?" Stackoverflow?":"" ); break; case SIGUSR1: fputs("cppcheck received signal ", output); fputs(signame, output); fputs(".\n", output); terminate=false; break; default: fputs("Internal error: cppcheck received signal ", output); fputs(signame, output); fputs(".\n", output); break; } if (printCallstack) { print_stacktrace(output, true, -1, lowMem); } if (unexpectedSignal) { fputs("\nPlease report this to the cppcheck developers!\n", output); } fflush(output); if (terminate) { // now let things proceed, shutdown and hopefully dump core for post-mortem analysis struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler=SIG_DFL; sigaction(signo, &act, nullptr); kill(killid, signo); } } #endif #ifdef USE_WINDOWS_SEH namespace { const ULONG maxnamelength = 512; struct IMAGEHLP_SYMBOL64_EXT : public IMAGEHLP_SYMBOL64 { TCHAR nameExt[maxnamelength]; // actually no need to worry about character encoding here }; typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64); fpStackWalk64 pStackWalk64; typedef DWORD64(WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64); fpSymGetModuleBase64 pSymGetModuleBase64; typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); fpSymGetSymFromAddr64 pSymGetSymFromAddr64; typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64); fpSymGetLineFromAddr64 pSymGetLineFromAddr64; typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD) ; fpUnDecorateSymbolName pUnDecorateSymbolName; typedef PVOID(WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64); fpSymFunctionTableAccess64 pSymFunctionTableAccess64; typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL); fpSymInitialize pSymInitialize; HMODULE hLibDbgHelp; // avoid explicit dependency on Dbghelp.dll bool loadDbgHelp() { hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll"); if (!hLibDbgHelp) return false; pStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLibDbgHelp, "StackWalk64"); pSymGetModuleBase64 = (fpSymGetModuleBase64) ::GetProcAddress(hLibDbgHelp, "SymGetModuleBase64"); pSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) ::GetProcAddress(hLibDbgHelp, "SymGetSymFromAddr64"); pSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)::GetProcAddress(hLibDbgHelp, "SymGetLineFromAddr64"); pSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)::GetProcAddress(hLibDbgHelp, "SymFunctionTableAccess64"); pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize"); pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName"); return true; } void printCallstack(FILE* outputFile, PEXCEPTION_POINTERS ex) { if (!loadDbgHelp()) return; const HANDLE hProcess = GetCurrentProcess(); const HANDLE hThread = GetCurrentThread(); pSymInitialize( hProcess, nullptr, TRUE ); CONTEXT context = *(ex->ContextRecord); STACKFRAME64 stack= {0}; #ifdef _M_IX86 stack.AddrPC.Offset = context.Eip; stack.AddrPC.Mode = AddrModeFlat; stack.AddrStack.Offset = context.Esp; stack.AddrStack.Mode = AddrModeFlat; stack.AddrFrame.Offset = context.Ebp; stack.AddrFrame.Mode = AddrModeFlat; #else stack.AddrPC.Offset = context.Rip; stack.AddrPC.Mode = AddrModeFlat; stack.AddrStack.Offset = context.Rsp; stack.AddrStack.Mode = AddrModeFlat; stack.AddrFrame.Offset = context.Rsp; stack.AddrFrame.Mode = AddrModeFlat; #endif IMAGEHLP_SYMBOL64_EXT symbol; symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); symbol.MaxNameLength = maxnamelength; DWORD64 displacement = 0; int beyond_main=-1; // emergency exit, see below for (ULONG frame = 0; ; frame++) { BOOL result = pStackWalk64 ( #ifdef _M_IX86 IMAGE_FILE_MACHINE_I386, #else IMAGE_FILE_MACHINE_AMD64, #endif hProcess, hThread, &stack, &context, nullptr, pSymFunctionTableAccess64, pSymGetModuleBase64, nullptr ); if (!result) // official end... break; pSymGetSymFromAddr64(hProcess, (ULONG64)stack.AddrPC.Offset, &displacement, &symbol); TCHAR undname[maxnamelength]= {0}; pUnDecorateSymbolName((const TCHAR*)symbol.Name, (PTSTR)undname, (DWORD)getArrayLength(undname), UNDNAME_COMPLETE); if (beyond_main>=0) ++beyond_main; if (_tcscmp(undname, _T("main"))==0) beyond_main=0; fprintf(outputFile, "%lu. 0x%08I64X in ", frame, (ULONG64)stack.AddrPC.Offset); fputs((const char *)undname, outputFile); fputc('\n', outputFile); if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end... break; } FreeLibrary(hLibDbgHelp); hLibDbgHelp=nullptr; } void writeMemoryErrorDetails(FILE* outputFile, PEXCEPTION_POINTERS ex, const char* description) { fputs(description, outputFile); fprintf(outputFile, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress); // Using %p for ULONG_PTR later on, so it must have size identical to size of pointer // This is not the universally portable solution but good enough for Win32/64 C_ASSERT(sizeof(void*) == sizeof(ex->ExceptionRecord->ExceptionInformation[1])); switch (ex->ExceptionRecord->ExceptionInformation[0]) { case 0: fprintf(outputFile, "reading from 0x%p", reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); break; case 1: fprintf(outputFile, "writing to 0x%p", reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); break; case 8: fprintf(outputFile, "data execution prevention at 0x%p", reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); break; default: break; } } /* * Any evaluation of the exception needs to be done here! */ int filterException(int code, PEXCEPTION_POINTERS ex) { FILE *outputFile = stdout; fputs("Internal error: ", outputFile); switch (ex->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: writeMemoryErrorDetails(outputFile, ex, "Access violation"); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: fputs("Out of array bounds", outputFile); break; case EXCEPTION_BREAKPOINT: fputs("Breakpoint", outputFile); break; case EXCEPTION_DATATYPE_MISALIGNMENT: fputs("Misaligned data", outputFile); break; case EXCEPTION_FLT_DENORMAL_OPERAND: fputs("Denormalized floating-point value", outputFile); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: fputs("Floating-point divide-by-zero", outputFile); break; case EXCEPTION_FLT_INEXACT_RESULT: fputs("Inexact floating-point value", outputFile); break; case EXCEPTION_FLT_INVALID_OPERATION: fputs("Invalid floating-point operation", outputFile); break; case EXCEPTION_FLT_OVERFLOW: fputs("Floating-point overflow", outputFile); break; case EXCEPTION_FLT_STACK_CHECK: fputs("Floating-point stack overflow", outputFile); break; case EXCEPTION_FLT_UNDERFLOW: fputs("Floating-point underflow", outputFile); break; case EXCEPTION_GUARD_PAGE: fputs("Page-guard access", outputFile); break; case EXCEPTION_ILLEGAL_INSTRUCTION: fputs("Illegal instruction", outputFile); break; case EXCEPTION_IN_PAGE_ERROR: writeMemoryErrorDetails(outputFile, ex, "Invalid page access"); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: fputs("Integer divide-by-zero", outputFile); break; case EXCEPTION_INT_OVERFLOW: fputs("Integer overflow", outputFile); break; case EXCEPTION_INVALID_DISPOSITION: fputs("Invalid exception dispatcher", outputFile); break; case EXCEPTION_INVALID_HANDLE: fputs("Invalid handle", outputFile); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: fputs("Non-continuable exception", outputFile); break; case EXCEPTION_PRIV_INSTRUCTION: fputs("Invalid instruction", outputFile); break; case EXCEPTION_SINGLE_STEP: fputs("Single instruction step", outputFile); break; case EXCEPTION_STACK_OVERFLOW: fputs("Stack overflow", outputFile); break; default: fprintf(outputFile, "Unknown exception (%d)\n", code); break; } fputc('\n', outputFile); printCallstack(outputFile, ex); fflush(outputFile); return EXCEPTION_EXECUTE_HANDLER; } } #endif /** * Signal/SEH handling * Has to be clean for using with SEH on windows, i.e. no construction of C++ object instances is allowed! * TODO Check for multi-threading issues! * */ int CppCheckExecutor::check_wrapper(CppCheck& cppcheck, int argc, const char* const argv[]) { #ifdef USE_WINDOWS_SEH FILE *outputFile = stdout; __try { return check_internal(cppcheck, argc, argv); } __except (filterException(GetExceptionCode(), GetExceptionInformation())) { // reporting to stdout may not be helpful within a GUI application... fputs("Please report this to the cppcheck developers!\n", outputFile); return -1; } #elif defined(USE_UNIX_SIGNAL_HANDLING) // determine stack vs. heap char stackVariable; char *heapVariable=(char*)malloc(1); bStackBelowHeap = &stackVariable < heapVariable; free(heapVariable); // set up alternative stack for signal handler stack_t segv_stack; segv_stack.ss_sp = mytstack; segv_stack.ss_flags = 0; segv_stack.ss_size = MYSTACKSIZE; sigaltstack(&segv_stack, nullptr); // install signal handler struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_flags=SA_SIGINFO|SA_ONSTACK; act.sa_sigaction=CppcheckSignalHandler; for (std::map::const_iterator sig=listofsignals.begin(); sig!=listofsignals.end(); ++sig) { sigaction(sig->first, &act, nullptr); } return check_internal(cppcheck, argc, argv); #else return check_internal(cppcheck, argc, argv); #endif } /* * That is a method which gets called from check_wrapper * */ int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const char* const argv[]) { Settings& settings = cppcheck.settings(); mSettings = &settings; const bool std = tryLoadLibrary(settings.library, argv[0], "std.cfg"); for (const std::string &lib : settings.libraries) { if (!tryLoadLibrary(settings.library, argv[0], lib.c_str())) { const std::string msg("Failed to load the library " + lib); const std::list callstack; ErrorLogger::ErrorMessage errmsg(callstack, emptyString, Severity::information, msg, "failedToLoadCfg", false); reportErr(errmsg); return EXIT_FAILURE; } } bool posix = true; if (settings.posix()) posix = tryLoadLibrary(settings.library, argv[0], "posix.cfg"); bool windows = true; if (settings.isWindowsPlatform()) windows = tryLoadLibrary(settings.library, argv[0], "windows.cfg"); if (!std || !posix || !windows) { const std::list callstack; const std::string msg("Failed to load " + std::string(!std ? "std.cfg" : !posix ? "posix.cfg" : "windows.cfg") + ". Your Cppcheck installation is broken, please re-install."); #ifdef FILESDIR const std::string details("The Cppcheck binary was compiled with FILESDIR set to \"" FILESDIR "\" and will therefore search for " "std.cfg in " FILESDIR "/cfg."); #else const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(argv[0])) + "cfg"); const std::string details("The Cppcheck binary was compiled without FILESDIR set. Either the " "std.cfg should be available in " + cfgfolder + " or the FILESDIR " "should be configured."); #endif ErrorLogger::ErrorMessage errmsg(callstack, emptyString, Severity::information, msg+" "+details, "failedToLoadCfg", false); reportErr(errmsg); return EXIT_FAILURE; } if (settings.reportProgress) mLatestProgressOutputTime = std::time(nullptr); if (!settings.outputFile.empty()) { mErrorOutput = new std::ofstream(settings.outputFile); } if (settings.xml) { reportErr(ErrorLogger::ErrorMessage::getXMLHeader()); } if (!settings.buildDir.empty()) { std::list fileNames; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) fileNames.push_back(i->first); AnalyzerInformation::writeFilesTxt(settings.buildDir, fileNames, settings.project.fileSettings); } unsigned int returnValue = 0; if (settings.jobs == 1) { // Single process settings.jointSuppressionReport = true; std::size_t totalfilesize = 0; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { totalfilesize += i->second; } std::size_t processedsize = 0; unsigned int c = 0; if (settings.project.fileSettings.empty()) { for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { if (!mSettings->library.markupFile(i->first) || !mSettings->library.processMarkupAfterCode(i->first)) { returnValue += cppcheck.check(i->first); processedsize += i->second; if (!settings.quiet) reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize); c++; } } } else { // filesettings c = 0; for (const ImportProject::FileSettings &fs : settings.project.fileSettings) { returnValue += cppcheck.check(fs); ++c; if (!settings.quiet) reportStatus(c, settings.project.fileSettings.size(), c, settings.project.fileSettings.size()); } } // second loop to parse all markup files which may not work until all // c/cpp files have been parsed and checked for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { if (mSettings->library.markupFile(i->first) && mSettings->library.processMarkupAfterCode(i->first)) { returnValue += cppcheck.check(i->first); processedsize += i->second; if (!settings.quiet) reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize); c++; } } if (cppcheck.analyseWholeProgram()) returnValue++; } else if (!ThreadExecutor::isEnabled()) { std::cout << "No thread support yet implemented for this platform." << std::endl; } else { // Multiple processes ThreadExecutor executor(mFiles, settings, *this); returnValue = executor.check(); } cppcheck.analyseWholeProgram(mSettings->buildDir, mFiles); if (settings.isEnabled(Settings::INFORMATION) || settings.checkConfiguration) { const bool enableUnusedFunctionCheck = cppcheck.isUnusedFunctionCheckEnabled(); if (settings.jointSuppressionReport) { for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { const bool err = reportUnmatchedSuppressions(settings.nomsg.getUnmatchedLocalSuppressions(i->first, enableUnusedFunctionCheck)); if (err && returnValue == 0) returnValue = settings.exitCode; } } const bool err = reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions(enableUnusedFunctionCheck)); if (err && returnValue == 0) returnValue = settings.exitCode; } if (!settings.checkConfiguration) { cppcheck.tooManyConfigsError("",0U); if (settings.isEnabled(Settings::MISSING_INCLUDE) && (Preprocessor::missingIncludeFlag || Preprocessor::missingSystemIncludeFlag)) { const std::list callStack; ErrorLogger::ErrorMessage msg(callStack, emptyString, Severity::information, "Cppcheck cannot find all the include files (use --check-config for details)\n" "Cppcheck cannot find all the include files. Cppcheck can check the code without the " "include files found. But the results will probably be more accurate if all the include " "files are found. Please check your project's include directories and add all of them " "as include directories for Cppcheck. To see what files Cppcheck cannot find use " "--check-config.", Preprocessor::missingIncludeFlag ? "missingInclude" : "missingIncludeSystem", false); reportInfo(msg); } } if (settings.xml) { reportErr(ErrorLogger::ErrorMessage::getXMLFooter()); } mSettings = nullptr; if (returnValue) return settings.exitCode; return 0; } #ifdef _WIN32 // fix trac ticket #439 'Cppcheck reports wrong filename for filenames containing 8-bit ASCII' static inline std::string ansiToOEM(const std::string &msg, bool doConvert) { if (doConvert) { const unsigned msglength = msg.length(); // convert ANSI strings to OEM strings in two steps std::vector wcContainer(msglength); std::string result(msglength, '\0'); // ansi code page characters to wide characters MultiByteToWideChar(CP_ACP, 0, msg.data(), msglength, wcContainer.data(), msglength); // wide characters to oem codepage characters WideCharToMultiByte(CP_OEMCP, 0, wcContainer.data(), msglength, const_cast(result.data()), msglength, nullptr, nullptr); return result; // hope for return value optimization } return msg; } #else // no performance regression on non-windows systems #define ansiToOEM(msg, doConvert) (msg) #endif void CppCheckExecutor::reportErr(const std::string &errmsg) { // Alert only about unique errors if (mShownErrors.find(errmsg) != mShownErrors.end()) return; mShownErrors.insert(errmsg); if (mErrorOutput) *mErrorOutput << errmsg << std::endl; else { std::cerr << ansiToOEM(errmsg, (mSettings == nullptr) ? true : !mSettings->xml) << std::endl; } } void CppCheckExecutor::reportOut(const std::string &outmsg) { std::cout << ansiToOEM(outmsg, true) << std::endl; } void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value) { (void)filename; if (!mLatestProgressOutputTime) return; // Report progress messages every 10 seconds const std::time_t currentTime = std::time(nullptr); if (currentTime >= (mLatestProgressOutputTime + 10)) { mLatestProgressOutputTime = currentTime; // format a progress message std::ostringstream ostr; ostr << "progress: " << stage << ' ' << value << '%'; // Report progress message reportOut(ostr.str()); } } void CppCheckExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg) { reportErr(msg); } void CppCheckExecutor::reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal) { if (filecount > 1) { std::ostringstream oss; const long percentDone = (sizetotal > 0) ? static_cast(static_cast(sizedone) / sizetotal * 100) : 0; oss << fileindex << '/' << filecount << " files checked " << percentDone << "% done"; std::cout << oss.str() << std::endl; } } void CppCheckExecutor::reportErr(const ErrorLogger::ErrorMessage &msg) { if (mShowAllErrors) { reportOut(msg.toXML()); } else if (mSettings->xml) { reportErr(msg.toXML()); } else { reportErr(msg.toString(mSettings->verbose, mSettings->templateFormat, mSettings->templateLocation)); } } void CppCheckExecutor::setExceptionOutput(FILE* exceptionOutput) { mExceptionOutput = exceptionOutput; } FILE* CppCheckExecutor::getExceptionOutput() { return mExceptionOutput; } bool CppCheckExecutor::tryLoadLibrary(Library& destination, const char* basepath, const char* filename) { const Library::Error err = destination.load(basepath, filename); if (err.errorcode == Library::UNKNOWN_ELEMENT) std::cout << "cppcheck: Found unknown elements in configuration file '" << filename << "': " << err.reason << std::endl; else if (err.errorcode != Library::OK) { std::string errmsg; switch (err.errorcode) { case Library::OK: break; case Library::FILE_NOT_FOUND: errmsg = "File not found"; break; case Library::BAD_XML: errmsg = "Bad XML"; break; case Library::UNKNOWN_ELEMENT: errmsg = "Unexpected element"; break; case Library::MISSING_ATTRIBUTE: errmsg = "Missing attribute"; break; case Library::BAD_ATTRIBUTE_VALUE: errmsg = "Bad attribute value"; break; case Library::UNSUPPORTED_FORMAT: errmsg = "File is of unsupported format version"; break; case Library::DUPLICATE_PLATFORM_TYPE: errmsg = "Duplicate platform type"; break; case Library::PLATFORM_TYPE_REDEFINED: errmsg = "Platform type redefined"; break; } if (!err.reason.empty()) errmsg += " '" + err.reason + "'"; std::cout << "cppcheck: Failed to load library configuration file '" << filename << "'. " << errmsg << std::endl; return false; } return true; } cppcheck-1.90/cli/cppcheckexecutor.h000066400000000000000000000132011357737443600175220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CPPCHECKEXECUTOR_H #define CPPCHECKEXECUTOR_H #include "errorlogger.h" #include #include #include #include #include class CppCheck; class Library; class Settings; /** * This class works as an example of how CppCheck can be used in external * programs without very little knowledge of the internal parts of the * program itself. If you wish to use cppcheck e.g. as a part of IDE, * just rewrite this class for your needs and possibly use other methods * from CppCheck class instead the ones used here. */ class CppCheckExecutor : public ErrorLogger { public: /** * Constructor */ CppCheckExecutor(); /** * Destructor */ ~CppCheckExecutor() OVERRIDE; /** * Starts the checking. * * @param argc from main() * @param argv from main() * @return EXIT_FAILURE if arguments are invalid or no input files * were found. * If errors are found and --error-exitcode is used, * given value is returned instead of default 0. * If no errors are found, 0 is returned. */ int check(int argc, const char* const argv[]); /** * Information about progress is directed here. This should be * called by the CppCheck class only. * * @param outmsg Progress message e.g. "Checking main.cpp..." */ void reportOut(const std::string &outmsg) OVERRIDE; /** xml output of errors */ void reportErr(const ErrorLogger::ErrorMessage &msg) OVERRIDE; void reportProgress(const std::string &filename, const char stage[], const std::size_t value) OVERRIDE; /** * Output information messages. */ void reportInfo(const ErrorLogger::ErrorMessage &msg) OVERRIDE; /** * Information about how many files have been checked * * @param fileindex This many files have been checked. * @param filecount This many files there are in total. * @param sizedone The sum of sizes of the files checked. * @param sizetotal The total sizes of the files. */ static void reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal); /** * @param exception_output Output file */ static void setExceptionOutput(FILE* exceptionOutput); /** * @return file name to be used for output from exception handler. Has to be either "stdout" or "stderr". */ static FILE* getExceptionOutput(); /** * Tries to load a library and prints warning/error messages * @return false, if an error occurred (except unknown XML elements) */ static bool tryLoadLibrary(Library& destination, const char* basepath, const char* filename); protected: /** * Helper function to print out errors. Appends a line change. * @param errmsg String printed to error stream */ void reportErr(const std::string &errmsg); /** * @brief Parse command line args and get settings and file lists * from there. * * @param cppcheck cppcheck instance * @param argc argc from main() * @param argv argv from main() * @return false when errors are found in the input */ bool parseFromArgs(CppCheck *cppcheck, int argc, const char* const argv[]); /** * Helper function to supply settings. This can be used for testing. * @param settings Reference to an Settings instance */ void setSettings(const Settings &settings); private: /** * Wrapper around check_internal * - installs optional platform dependent signal handling * * @param cppcheck cppcheck instance * @param argc from main() * @param argv from main() **/ int check_wrapper(CppCheck& cppcheck, int argc, const char* const argv[]); /** * Starts the checking. * * @param cppcheck cppcheck instance * @param argc from main() * @param argv from main() * @return EXIT_FAILURE if arguments are invalid or no input files * were found. * If errors are found and --error-exitcode is used, * given value is returned instead of default 0. * If no errors are found, 0 is returned. */ int check_internal(CppCheck& cppcheck, int argc, const char* const argv[]); /** * Pointer to current settings; set while check() is running. */ const Settings* mSettings; /** * Used to filter out duplicate error messages. */ std::set mShownErrors; /** * Filename associated with size of file */ std::map mFiles; /** * Report progress time */ std::time_t mLatestProgressOutputTime; /** * Output file name for exception handler */ static FILE* mExceptionOutput; /** * Error output */ std::ofstream *mErrorOutput; /** * Has --errorlist been given? */ bool mShowAllErrors; }; #endif // CPPCHECKEXECUTOR_H cppcheck-1.90/cli/filelister.cpp000066400000000000000000000204461357737443600166710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filelister.h" #include "path.h" #include "pathmatch.h" #include "utils.h" #include #include #ifdef _WIN32 /////////////////////////////////////////////////////////////////////////////// ////// This code is WIN32 systems ///////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #include #ifndef __BORLANDC__ #include #endif // Here is the catch: cppcheck core is Ansi code (using char type). // When compiling Unicode targets WinAPI automatically uses *W Unicode versions // of called functions. Thus, we explicitly call *A versions of the functions. static BOOL myIsDirectory(const std::string& path) { #ifdef __BORLANDC__ return (GetFileAttributes(path.c_str()) & FILE_ATTRIBUTE_DIRECTORY); #else // See http://msdn.microsoft.com/en-us/library/bb773621(VS.85).aspx return PathIsDirectoryA(path.c_str()); #endif } static HANDLE myFindFirstFile(const std::string& path, LPWIN32_FIND_DATAA findData) { HANDLE hFind = FindFirstFileA(path.c_str(), findData); return hFind; } static BOOL myFileExists(const std::string& path) { #ifdef __BORLANDC__ DWORD fa = GetFileAttributes(path.c_str()); BOOL result = FALSE; if (fa != INVALID_FILE_ATTRIBUTES && !(fa & FILE_ATTRIBUTE_DIRECTORY)) result = TRUE; #else const BOOL result = PathFileExistsA(path.c_str()); #endif return result; } void FileLister::recursiveAddFiles(std::map &files, const std::string &path, const std::set &extra, const PathMatch& ignored) { addFiles(files, path, extra, true, ignored); } void FileLister::addFiles(std::map &files, const std::string &path, const std::set &extra, bool recursive, const PathMatch& ignored) { const std::string cleanedPath = Path::toNativeSeparators(path); // basedir is the base directory which is used to form pathnames. // It always has a trailing backslash available for concatenation. std::string basedir; // searchPattern is the search string passed into FindFirst and FindNext. std::string searchPattern = cleanedPath; // The user wants to check all files in a dir const bool checkAllFilesInDir = (myIsDirectory(cleanedPath) != FALSE); if (checkAllFilesInDir) { const char c = cleanedPath.back(); switch (c) { case '\\': searchPattern += '*'; basedir = cleanedPath; break; case '*': basedir = cleanedPath.substr(0, cleanedPath.length() - 1); break; default: searchPattern += "\\*"; if (cleanedPath != ".") basedir = cleanedPath + '\\'; } } else { const std::string::size_type pos = cleanedPath.find_last_of('\\'); if (std::string::npos != pos) { basedir = cleanedPath.substr(0, pos + 1); } } WIN32_FIND_DATAA ffd; HANDLE hFind = myFindFirstFile(searchPattern, &ffd); if (INVALID_HANDLE_VALUE == hFind) return; do { if (ffd.cFileName[0] == '.' || ffd.cFileName[0] == '\0') continue; const char* ansiFfd = ffd.cFileName; if (std::strchr(ansiFfd,'?')) { ansiFfd = ffd.cAlternateFileName; } const std::string fname(basedir + ansiFfd); if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // File if ((!checkAllFilesInDir || Path::acceptFile(fname, extra)) && !ignored.match(fname)) { const std::string nativename = Path::fromNativeSeparators(fname); // Limitation: file sizes are assumed to fit in a 'size_t' #ifdef _WIN64 files[nativename] = (static_cast(ffd.nFileSizeHigh) << 32) | ffd.nFileSizeLow; #else files[nativename] = ffd.nFileSizeLow; #endif } } else { // Directory if (recursive) { if (!ignored.match(fname)) FileLister::recursiveAddFiles(files, fname, extra, ignored); } } } while (FindNextFileA(hFind, &ffd) != FALSE); FindClose(hFind); } bool FileLister::isDirectory(const std::string &path) { return (myIsDirectory(path) != FALSE); } bool FileLister::fileExists(const std::string &path) { return (myFileExists(path) != FALSE); } #else /////////////////////////////////////////////////////////////////////////////// ////// This code is POSIX-style systems /////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// #if defined(__CYGWIN__) #undef __STRICT_ANSI__ #endif #include #include static void addFiles2(std::map &files, const std::string &path, const std::set &extra, bool recursive, const PathMatch& ignored ) { struct stat file_stat; if (stat(path.c_str(), &file_stat) != -1) { if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { DIR * dir = opendir(path.c_str()); if (!dir) return; dirent entry; dirent * dir_result; std::string new_path; new_path.reserve(path.length() + 100);// prealloc some memory to avoid constant new/deletes in loop while ((readdir_r(dir, &entry, &dir_result) == 0) && (dir_result != nullptr)) { if ((std::strcmp(dir_result->d_name, ".") == 0) || (std::strcmp(dir_result->d_name, "..") == 0)) continue; new_path = path + '/' + dir_result->d_name; #if defined(_DIRENT_HAVE_D_TYPE) || defined(_BSD_SOURCE) bool path_is_directory = (dir_result->d_type == DT_DIR || (dir_result->d_type == DT_UNKNOWN && FileLister::isDirectory(new_path))); #else bool path_is_directory = FileLister::isDirectory(new_path); #endif if (path_is_directory) { if (recursive && !ignored.match(new_path)) { addFiles2(files, new_path, extra, recursive, ignored); } } else { if (Path::acceptFile(new_path, extra) && !ignored.match(new_path)) { stat(new_path.c_str(), &file_stat); files[new_path] = file_stat.st_size; } } } closedir(dir); } else files[path] = file_stat.st_size; } } void FileLister::recursiveAddFiles(std::map &files, const std::string &path, const std::set &extra, const PathMatch& ignored) { addFiles(files, path, extra, true, ignored); } void FileLister::addFiles(std::map &files, const std::string &path, const std::set &extra, bool recursive, const PathMatch& ignored) { if (!path.empty()) { std::string corrected_path = path; if (endsWith(corrected_path, '/')) corrected_path.erase(corrected_path.end() - 1); addFiles2(files, corrected_path, extra, recursive, ignored); } } bool FileLister::isDirectory(const std::string &path) { struct stat file_stat; return (stat(path.c_str(), &file_stat) != -1 && (file_stat.st_mode & S_IFMT) == S_IFDIR); } bool FileLister::fileExists(const std::string &path) { struct stat file_stat; return (stat(path.c_str(), &file_stat) != -1 && (file_stat.st_mode & S_IFMT) == S_IFREG); } #endif cppcheck-1.90/cli/filelister.h000066400000000000000000000063201357737443600163310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef filelisterH #define filelisterH #include #include #include #include class PathMatch; /// @addtogroup CLI /// @{ /** @brief Cross-platform FileLister */ class FileLister { public: /** * @brief Recursively add source files to a map. * Add source files from given directory and all subdirectries to the * given map. Only files with accepted extensions * (*.c;*.cpp;*.cxx;*.c++;*.cc;*.txx) are added. * @param files output map that associates the size of each file with its name * @param path root path * @param ignored ignored paths */ static void recursiveAddFiles(std::map &files, const std::string &path, const PathMatch& ignored) { const std::set extra; recursiveAddFiles(files, path, extra, ignored); } /** * @brief Recursively add source files to a map. * Add source files from given directory and all subdirectries to the * given map. Only files with accepted extensions * (*.c;*.cpp;*.cxx;*.c++;*.cc;*.txx) are added. * @param files output map that associates the size of each file with its name * @param path root path * @param extra Extra file extensions * @param ignored ignored paths */ static void recursiveAddFiles(std::map &files, const std::string &path, const std::set &extra, const PathMatch& ignored); /** * @brief (Recursively) add source files to a map. * Add source files from given directory and all subdirectries to the * given map. Only files with accepted extensions * (*.c;*.cpp;*.cxx;*.c++;*.cc;*.txx) are added. * @param files output map that associates the size of each file with its name * @param path root path * @param extra Extra file extensions * @param recursive Enable recursion * @param ignored ignored paths */ static void addFiles(std::map &files, const std::string &path, const std::set &extra, bool recursive, const PathMatch& ignored); /** * @brief Is given path a directory? * @return returns true if the path is a directory */ static bool isDirectory(const std::string &path); /** * @brief Check if the given path is a file and if it exists? * @return true if path points to file and the file exists. */ static bool fileExists(const std::string &path); }; /// @} #endif // #ifndef filelisterH cppcheck-1.90/cli/main.cpp000066400000000000000000000100371357737443600154460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * * @mainpage Cppcheck * @version 1.90 * * @section overview_sec Overview * Cppcheck is a simple tool for static analysis of C/C++ code. * * When you write a checker you have access to: * - %Token list - the tokenized code * - Syntax tree - Syntax tree of each expression * - %SymbolDatabase - Information about all types/variables/functions/etc * in the current translation unit * - Library - Configuration of functions/types * - Value flow analysis - Data flow analysis that determine possible values for each token * * Use --debug-normal on the command line to see debug output for the token list * and the syntax tree. If both --debug-normal and --verbose is used, the symbol * database is also written. * * The checks are written in C++. * * @section detailed_overview_sec Detailed overview * This happens when you execute cppcheck from the command line: * -# CppCheckExecutor::check this function executes the Cppcheck * -# CmdLineParser::parseFromArgs parse command line arguments * - The Settings class is used to maintain settings * - Use FileLister and command line arguments to get files to check * -# ThreadExecutor create more instances of CppCheck if needed * -# CppCheck::check is called for each file. It checks a single file * -# Preprocess the file (through Preprocessor) * - Comments are removed * - Macros are expanded * -# Tokenize the file (see Tokenizer) * -# Run the runChecks of all check classes. * -# Simplify the tokenlist (Tokenizer::simplifyTokenList2) * -# Run the runSimplifiedChecks of all check classes * * When errors are found, they are reported back to the CppCheckExecutor through the ErrorLogger interface. */ #include "cppcheckexecutor.h" #include #ifdef _WIN32 #include static char exename[1024] = {0}; #endif /** * Main function of cppcheck * * @param argc Passed to CppCheck::parseFromArgs() * @param argv Passed to CppCheck::parseFromArgs() * @return What CppCheckExecutor::check() returns. */ int main(int argc, char* argv[]) { // MS Visual C++ memory leak debug tracing #if defined(_MSC_VER) && defined(_DEBUG) _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); #endif CppCheckExecutor exec; #ifdef _WIN32 GetModuleFileNameA(nullptr, exename, sizeof(exename)/sizeof(exename[0])-1); argv[0] = exename; #endif #ifdef NDEBUG try { #endif return exec.check(argc, argv); #ifdef NDEBUG } catch (const InternalError& e) { std::cout << e.errorMessage << std::endl; } catch (const std::exception& error) { std::cout << error.what() << std::endl; } catch (...) { std::cout << "Unknown exception" << std::endl; } return EXIT_FAILURE; #endif } // Warn about deprecated compilers #ifdef __clang__ # if ( __clang_major__ < 2 || ( __clang_major__ == 2 && __clang_minor__ < 9)) # warning "Using Clang 2.8 or earlier. Support for this version has been removed." # endif #elif defined(__GNUC__) # if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)) # warning "Using GCC 4.5 or earlier. Support for this version has been removed." # endif #elif defined(_MSC_VER) # if (_MSC_VER < 1800) # pragma message("WARNING: Using Visual Studio 2012 or earlier. Support for this version has been removed.") # endif #endif cppcheck-1.90/cli/threadexecutor.cpp000066400000000000000000000445431357737443600175610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "threadexecutor.h" #include "config.h" #include "cppcheck.h" #include "cppcheckexecutor.h" #include "importproject.h" #include "settings.h" #include "suppressions.h" #include #include #include #include #include #include #ifdef __SVR4 // Solaris #include #endif #ifdef THREADING_MODEL_FORK #include #include #include #include #endif #ifdef THREADING_MODEL_WIN #include #include #endif // required for FD_ZERO using std::memset; ThreadExecutor::ThreadExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger) : mFiles(files), mSettings(settings), mErrorLogger(errorLogger), mFileCount(0) // Not initialized mFileSync, mErrorSync, mReportSync { #if defined(THREADING_MODEL_FORK) mWpipe = 0; #elif defined(THREADING_MODEL_WIN) mProcessedFiles = 0; mTotalFiles = 0; mProcessedSize = 0; mTotalFileSize = 0; #endif } ThreadExecutor::~ThreadExecutor() { //dtor } /////////////////////////////////////////////////////////////////////////////// ////// This code is for platforms that support fork() only //////////////////// /////////////////////////////////////////////////////////////////////////////// #if defined(THREADING_MODEL_FORK) void ThreadExecutor::addFileContent(const std::string &path, const std::string &content) { mFileContents[ path ] = content; } int ThreadExecutor::handleRead(int rpipe, unsigned int &result) { char type = 0; if (read(rpipe, &type, 1) <= 0) { if (errno == EAGAIN) return 0; return -1; } if (type != REPORT_OUT && type != REPORT_ERROR && type != REPORT_INFO && type != CHILD_END) { std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(0); } unsigned int len = 0; if (read(rpipe, &len, sizeof(len)) <= 0) { std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(0); } // Don't rely on incoming data being null-terminated. // Allocate +1 element and null-terminate the buffer. char *buf = new char[len + 1]; const ssize_t readIntoBuf = read(rpipe, buf, len); if (readIntoBuf <= 0) { std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl; std::exit(0); } buf[readIntoBuf] = 0; if (type == REPORT_OUT) { mErrorLogger.reportOut(buf); } else if (type == REPORT_ERROR || type == REPORT_INFO) { ErrorLogger::ErrorMessage msg; msg.deserialize(buf); if (!mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) { // Alert only about unique errors std::string errmsg = msg.toString(mSettings.verbose); if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) == mErrorList.end()) { mErrorList.push_back(errmsg); if (type == REPORT_ERROR) mErrorLogger.reportErr(msg); else mErrorLogger.reportInfo(msg); } } } else if (type == CHILD_END) { std::istringstream iss(buf); unsigned int fileResult = 0; iss >> fileResult; result += fileResult; delete [] buf; return -1; } delete [] buf; return 1; } bool ThreadExecutor::checkLoadAverage(size_t nchildren) { #if defined(__CYGWIN__) || defined(__QNX__) // getloadavg() is unsupported on Cygwin, Qnx. return true; #else if (!nchildren || !mSettings.loadAverage) { return true; } double sample(0); if (getloadavg(&sample, 1) != 1) { // disable load average checking on getloadavg error return true; } else if (sample < mSettings.loadAverage) { return true; } return false; #endif } unsigned int ThreadExecutor::check() { mFileCount = 0; unsigned int result = 0; std::size_t totalfilesize = 0; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { totalfilesize += i->second; } std::list rpipes; std::map childFile; std::map pipeFile; std::size_t processedsize = 0; std::map::const_iterator iFile = mFiles.begin(); std::list::const_iterator iFileSettings = mSettings.project.fileSettings.begin(); for (;;) { // Start a new child size_t nchildren = rpipes.size(); if ((iFile != mFiles.end() || iFileSettings != mSettings.project.fileSettings.end()) && nchildren < mSettings.jobs && checkLoadAverage(nchildren)) { int pipes[2]; if (pipe(pipes) == -1) { std::cerr << "#### ThreadExecutor::check, pipe() failed: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } int flags = 0; if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0) { std::cerr << "#### ThreadExecutor::check, fcntl(F_GETFL) failed: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0) { std::cerr << "#### ThreadExecutor::check, fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid < 0) { // Error std::cerr << "#### ThreadExecutor::check, Failed to create child process: "<< std::strerror(errno) << std::endl; std::exit(EXIT_FAILURE); } else if (pid == 0) { close(pipes[0]); mWpipe = pipes[1]; CppCheck fileChecker(*this, false); fileChecker.settings() = mSettings; unsigned int resultOfCheck = 0; if (iFileSettings != mSettings.project.fileSettings.end()) { resultOfCheck = fileChecker.check(*iFileSettings); } else if (!mFileContents.empty() && mFileContents.find(iFile->first) != mFileContents.end()) { // File content was given as a string resultOfCheck = fileChecker.check(iFile->first, mFileContents[ iFile->first ]); } else { // Read file from a file resultOfCheck = fileChecker.check(iFile->first); } std::ostringstream oss; oss << resultOfCheck; writeToPipe(CHILD_END, oss.str()); std::exit(0); } close(pipes[1]); rpipes.push_back(pipes[0]); if (iFileSettings != mSettings.project.fileSettings.end()) { childFile[pid] = iFileSettings->filename + ' ' + iFileSettings->cfg; pipeFile[pipes[0]] = iFileSettings->filename + ' ' + iFileSettings->cfg; ++iFileSettings; } else { childFile[pid] = iFile->first; pipeFile[pipes[0]] = iFile->first; ++iFile; } } else if (!rpipes.empty()) { fd_set rfds; FD_ZERO(&rfds); for (std::list::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp) FD_SET(*rp, &rfds); struct timeval tv; // for every second polling of load average condition tv.tv_sec = 1; tv.tv_usec = 0; int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, nullptr, nullptr, &tv); if (r > 0) { std::list::iterator rp = rpipes.begin(); while (rp != rpipes.end()) { if (FD_ISSET(*rp, &rfds)) { int readRes = handleRead(*rp, result); if (readRes == -1) { std::size_t size = 0; std::map::iterator p = pipeFile.find(*rp); if (p != pipeFile.end()) { std::string name = p->second; pipeFile.erase(p); std::map::const_iterator fs = mFiles.find(name); if (fs != mFiles.end()) { size = fs->second; } } mFileCount++; processedsize += size; if (!mSettings.quiet) CppCheckExecutor::reportStatus(mFileCount, mFiles.size() + mSettings.project.fileSettings.size(), processedsize, totalfilesize); close(*rp); rp = rpipes.erase(rp); } else ++rp; } else ++rp; } } int stat = 0; pid_t child = waitpid(0, &stat, WNOHANG); if (child > 0) { std::string childname; std::map::iterator c = childFile.find(child); if (c != childFile.end()) { childname = c->second; childFile.erase(c); } if (WIFSIGNALED(stat)) { std::ostringstream oss; oss << "Internal error: Child process crashed with signal " << WTERMSIG(stat); std::list locations; locations.emplace_back(childname, 0, 0); const ErrorLogger::ErrorMessage errmsg(locations, emptyString, Severity::error, oss.str(), "cppcheckError", false); if (!mSettings.nomsg.isSuppressed(errmsg.toSuppressionsErrorMessage())) mErrorLogger.reportErr(errmsg); } } } else { // All done break; } } return result; } void ThreadExecutor::writeToPipe(PipeSignal type, const std::string &data) { unsigned int len = static_cast(data.length() + 1); char *out = new char[ len + 1 + sizeof(len)]; out[0] = static_cast(type); std::memcpy(&(out[1]), &len, sizeof(len)); std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len); if (write(mWpipe, out, len + 1 + sizeof(len)) <= 0) { delete [] out; out = nullptr; std::cerr << "#### ThreadExecutor::writeToPipe, Failed to write to pipe" << std::endl; std::exit(0); } delete [] out; } void ThreadExecutor::reportOut(const std::string &outmsg) { writeToPipe(REPORT_OUT, outmsg); } void ThreadExecutor::reportErr(const ErrorLogger::ErrorMessage &msg) { writeToPipe(REPORT_ERROR, msg.serialize()); } void ThreadExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg) { writeToPipe(REPORT_INFO, msg.serialize()); } #elif defined(THREADING_MODEL_WIN) void ThreadExecutor::addFileContent(const std::string &path, const std::string &content) { mFileContents[path] = content; } unsigned int ThreadExecutor::check() { HANDLE *threadHandles = new HANDLE[mSettings.jobs]; mItNextFile = mFiles.begin(); mItNextFileSettings = mSettings.project.fileSettings.begin(); mProcessedFiles = 0; mProcessedSize = 0; mTotalFiles = mFiles.size() + mSettings.project.fileSettings.size(); mTotalFileSize = 0; for (std::map::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) { mTotalFileSize += i->second; } InitializeCriticalSection(&mFileSync); InitializeCriticalSection(&mErrorSync); InitializeCriticalSection(&mReportSync); for (unsigned int i = 0; i < mSettings.jobs; ++i) { threadHandles[i] = (HANDLE)_beginthreadex(nullptr, 0, threadProc, this, 0, nullptr); if (!threadHandles[i]) { std::cerr << "#### ThreadExecutor::check error, errno :" << errno << std::endl; exit(EXIT_FAILURE); } } const DWORD waitResult = WaitForMultipleObjects(mSettings.jobs, threadHandles, TRUE, INFINITE); if (waitResult != WAIT_OBJECT_0) { if (waitResult == WAIT_FAILED) { std::cerr << "#### ThreadExecutor::check wait failed, result: " << waitResult << " error: " << GetLastError() << std::endl; exit(EXIT_FAILURE); } else { std::cerr << "#### ThreadExecutor::check wait failed, result: " << waitResult << std::endl; exit(EXIT_FAILURE); } } unsigned int result = 0; for (unsigned int i = 0; i < mSettings.jobs; ++i) { DWORD exitCode; if (!GetExitCodeThread(threadHandles[i], &exitCode)) { std::cerr << "#### ThreadExecutor::check get exit code failed, error:" << GetLastError() << std::endl; exit(EXIT_FAILURE); } result += exitCode; if (!CloseHandle(threadHandles[i])) { std::cerr << "#### ThreadExecutor::check close handle failed, error:" << GetLastError() << std::endl; exit(EXIT_FAILURE); } } DeleteCriticalSection(&mFileSync); DeleteCriticalSection(&mErrorSync); DeleteCriticalSection(&mReportSync); delete[] threadHandles; return result; } unsigned int __stdcall ThreadExecutor::threadProc(void *args) { unsigned int result = 0; ThreadExecutor *threadExecutor = static_cast(args); std::map::const_iterator &itFile = threadExecutor->mItNextFile; std::list::const_iterator &itFileSettings = threadExecutor->mItNextFileSettings; // guard static members of CppCheck against concurrent access EnterCriticalSection(&threadExecutor->mFileSync); CppCheck fileChecker(*threadExecutor, false); fileChecker.settings() = threadExecutor->mSettings; for (;;) { if (itFile == threadExecutor->mFiles.end() && itFileSettings == threadExecutor->mSettings.project.fileSettings.end()) { LeaveCriticalSection(&threadExecutor->mFileSync); break; } std::size_t fileSize = 0; if (itFile != threadExecutor->mFiles.end()) { const std::string &file = itFile->first; fileSize = itFile->second; ++itFile; LeaveCriticalSection(&threadExecutor->mFileSync); const std::map::const_iterator fileContent = threadExecutor->mFileContents.find(file); if (fileContent != threadExecutor->mFileContents.end()) { // File content was given as a string result += fileChecker.check(file, fileContent->second); } else { // Read file from a file result += fileChecker.check(file); } } else { // file settings.. const ImportProject::FileSettings &fs = *itFileSettings; ++itFileSettings; LeaveCriticalSection(&threadExecutor->mFileSync); result += fileChecker.check(fs); } EnterCriticalSection(&threadExecutor->mFileSync); threadExecutor->mProcessedSize += fileSize; threadExecutor->mProcessedFiles++; if (!threadExecutor->mSettings.quiet) { EnterCriticalSection(&threadExecutor->mReportSync); CppCheckExecutor::reportStatus(threadExecutor->mProcessedFiles, threadExecutor->mTotalFiles, threadExecutor->mProcessedSize, threadExecutor->mTotalFileSize); LeaveCriticalSection(&threadExecutor->mReportSync); } } return result; } void ThreadExecutor::reportOut(const std::string &outmsg) { EnterCriticalSection(&mReportSync); mErrorLogger.reportOut(outmsg); LeaveCriticalSection(&mReportSync); } void ThreadExecutor::reportErr(const ErrorLogger::ErrorMessage &msg) { report(msg, MessageType::REPORT_ERROR); } void ThreadExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg) { report(msg, MessageType::REPORT_INFO); } void ThreadExecutor::report(const ErrorLogger::ErrorMessage &msg, MessageType msgType) { if (mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) return; // Alert only about unique errors bool reportError = false; const std::string errmsg = msg.toString(mSettings.verbose); EnterCriticalSection(&mErrorSync); if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) == mErrorList.end()) { mErrorList.push_back(errmsg); reportError = true; } LeaveCriticalSection(&mErrorSync); if (reportError) { EnterCriticalSection(&mReportSync); switch (msgType) { case MessageType::REPORT_ERROR: mErrorLogger.reportErr(msg); break; case MessageType::REPORT_INFO: mErrorLogger.reportInfo(msg); break; } LeaveCriticalSection(&mReportSync); } } #else void ThreadExecutor::addFileContent(const std::string &/*path*/, const std::string &/*content*/) { } unsigned int ThreadExecutor::check() { return 0; } void ThreadExecutor::reportOut(const std::string &/*outmsg*/) { } void ThreadExecutor::reportErr(const ErrorLogger::ErrorMessage &/*msg*/) { } void ThreadExecutor::reportInfo(const ErrorLogger::ErrorMessage &/*msg*/) { } #endif cppcheck-1.90/cli/threadexecutor.h000066400000000000000000000104711357737443600172170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef THREADEXECUTOR_H #define THREADEXECUTOR_H #include "errorlogger.h" #include "importproject.h" #include #include #include #include #if (defined(__GNUC__) || defined(__sun)) && !defined(__MINGW32__) && !defined(__CYGWIN__) #define THREADING_MODEL_FORK #elif defined(_WIN32) #define THREADING_MODEL_WIN #include #endif class Settings; /// @addtogroup CLI /// @{ /** * This class will take a list of filenames and settings and check then * all files using threads. */ class ThreadExecutor : public ErrorLogger { public: ThreadExecutor(const std::map &files, Settings &settings, ErrorLogger &errorLogger); ThreadExecutor(const ThreadExecutor &) = delete; ~ThreadExecutor() OVERRIDE; void operator=(const ThreadExecutor &) = delete; unsigned int check(); void reportOut(const std::string &outmsg) OVERRIDE; void reportErr(const ErrorLogger::ErrorMessage &msg) OVERRIDE; void reportInfo(const ErrorLogger::ErrorMessage &msg) OVERRIDE; /** * @brief Add content to a file, to be used in unit testing. * * @param path File name (used as a key to link with real file). * @param content If the file would be a real file, this should be * the content of the file. */ void addFileContent(const std::string &path, const std::string &content); private: const std::map &mFiles; Settings &mSettings; ErrorLogger &mErrorLogger; unsigned int mFileCount; #if defined(THREADING_MODEL_FORK) /** @brief Key is file name, and value is the content of the file */ std::map mFileContents; private: enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', REPORT_INFO='3', CHILD_END='4'}; /** * Read from the pipe, parse and handle what ever is in there. *@return -1 in case of error * 0 if there is nothing in the pipe to be read * 1 if we did read something */ int handleRead(int rpipe, unsigned int &result); void writeToPipe(PipeSignal type, const std::string &data); /** * Write end of status pipe, different for each child. * Not used in master process. */ std::list mErrorList; int mWpipe; /** * @brief Check load average condition * @param nchildren - count of currently ran children * @return true - if new process can be started */ bool checkLoadAverage(size_t nchildren); public: /** * @return true if support for threads exist. */ static bool isEnabled() { return true; } #elif defined(THREADING_MODEL_WIN) private: enum class MessageType {REPORT_ERROR, REPORT_INFO}; std::map mFileContents; std::map::const_iterator mItNextFile; std::list::const_iterator mItNextFileSettings; std::size_t mProcessedFiles; std::size_t mTotalFiles; std::size_t mProcessedSize; std::size_t mTotalFileSize; CRITICAL_SECTION mFileSync; std::list mErrorList; CRITICAL_SECTION mErrorSync; CRITICAL_SECTION mReportSync; void report(const ErrorLogger::ErrorMessage &msg, MessageType msgType); static unsigned __stdcall threadProc(void*); public: /** * @return true if support for threads exist. */ static bool isEnabled() { return true; } #else public: /** * @return true if support for threads exist. */ static bool isEnabled() { return false; } #endif }; /// @} #endif // THREADEXECUTOR_H cppcheck-1.90/cli/version.rc000066400000000000000000000016441357737443600160350ustar00rootroot00000000000000#include "../lib/version.h" #include "winresrc.h" VS_VERSION_INFO VERSIONINFO FILEVERSION CPPCHECK_VERSION PRODUCTVERSION CPPCHECK_VERSION FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS (0x1L|VS_FF_PRERELEASE) #else FILEFLAGS (0x0L|VS_FF_PRERELEASE) #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "cppcheck Application" VALUE "FileVersion", CPPCHECK_VERSION_STRING VALUE "InternalName", "cppcheck" VALUE "LegalCopyright", LEGALCOPYRIGHT VALUE "OriginalFilename", "cppcheck.exe" VALUE "ProductName", "cppcheck Application" VALUE "ProductVersion", CPPCHECK_VERSION_STRING END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END cppcheck-1.90/cmake/000077500000000000000000000000001357737443600143265ustar00rootroot00000000000000cppcheck-1.90/cmake/buildFiles.cmake000066400000000000000000000003461357737443600174150ustar00rootroot00000000000000CONFIGURE_FILE("${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake") cppcheck-1.90/cmake/cmake_uninstall.cmake.in000066400000000000000000000012651357737443600211120ustar00rootroot00000000000000# ----------------------------------------------- # File that provides "make uninstall" target # We use the file 'install_manifest.txt' # ----------------------------------------------- if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") execute_process(COMMAND rm $ENV{DESTDIR}${file}) endforeach(file) cppcheck-1.90/cmake/compilerDefinitions.cmake000066400000000000000000000003721357737443600213400ustar00rootroot00000000000000if (UNIX) if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") add_definitions(-D_GLIBCXX_DEBUG) endif() if (HAVE_RULES) add_definitions(-DHAVE_RULES -DTIXML_USE_STL) endif() add_definitions(-DFILESDIR="${FILESDIR}") endif() cppcheck-1.90/cmake/compileroptions.cmake000066400000000000000000000142701357737443600205620ustar00rootroot00000000000000set(EXTRA_C_FLAGS "") set(EXTRA_C_FLAGS_RELEASE "-DNDEBUG") set(EXTRA_C_FLAGS_DEBUG "-DDEBUG") if (USE_CLANG) set (CMAKE_C_COMPILER_ID "Clang") set (CMAKE_CXX_COMPILER_ID "Clang") set (CMAKE_C_COMPILER "/usr/bin/clang") set (CMAKE_CXX_COMPILER "/usr/bin/clang++") set (CMAKE_C_FLAGS "-std=c99") set (CMAKE_C_FLAGS_DEBUG "-g") set (CMAKE_C_FLAGS_RELEASE "-O2") set (CMAKE_CXX_FLAGS "") set (CMAKE_CXX_FLAGS_DEBUG "-g") set (CMAKE_CXX_FLAGS_RELEASE "-O2") endif() if (USE_ANALYZE) set (CMAKE_C_COMPILER_ID "ccc-analyzer") set (CMAKE_CXX_COMPILER_ID "c++-analyzer") set (CMAKE_C_COMPILER "/usr/share/clang/scan-build/ccc-analyzer") set (CMAKE_CXX_COMPILER "/usr/share/clang/scan-build/c++-analyzer") set (CMAKE_C_FLAGS "-Wall -std=c99") set (CMAKE_C_FLAGS_DEBUG "-g") set (CMAKE_C_FLAGS_RELEASE "-O2") set (CMAKE_CXX_FLAGS "-Wall") set (CMAKE_CXX_FLAGS_DEBUG "-g") set (CMAKE_CXX_FLAGS_RELEASE "-O2") endif() set(CMAKE_CXX_FLAGS_ASAN "-g -fsanitize=address,undefined -fno-sanitize-recover=all" CACHE STRING "Compiler flags in asan build" FORCE) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (NOT (GCC_VERSION VERSION_GREATER 4.6 OR GCC_VERSION VERSION_EQUAL 4.6)) message(FATAL_ERROR "${PROJECT_NAME} c++11 support requires g++ 4.6 or greater, but it is ${GCC_VERSION}") endif () set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wcast-qual") # Cast for removing type qualifiers set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-conversion") # Implicit conversions that may alter a value set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wfloat-equal") # Floating values used in equality comparisons set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Winline") # If a inline declared function couldn't be inlined set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wmissing-declarations") # If a global function is defined without a previous declaration set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wmissing-format-attribute") # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Woverloaded-virtual") # when a function declaration hides virtual functions from a base class set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wpacked") # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wredundant-decls") # if anything is declared more than once in the same scope set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-shadow") # whenever a local variable or type declaration shadows another one set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-sign-promo") # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-missing-field-initializers") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-missing-braces") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-sign-compare") if(WARNINGS_ANSI_ISO) # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Werror=return-type") # # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wstrict-aliasing=3") else() set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-narrowing") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-delete-non-virtual-dtor") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-unnamed-type-template-args") endif() elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") if(NOT EXISTS ${CMAKE_CXX_COMPILER}) MESSAGE( FATAL_ERROR "Clang++ not found. " ) endif() set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-four-char-constants") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-missing-braces") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-missing-field-initializers") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-multichar") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-sign-compare") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-unused-function") if(ENABLE_COVERAGE OR ENABLE_COVERAGE_XML) MESSAGE(FATAL_ERROR "Not use clang for generate code coverage. Use gcc. ") endif() elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "c++-analyzer") if(NOT EXISTS ${CMAKE_CXX_COMPILER}) MESSAGE( FATAL_ERROR "c++-analyzer not found. " ) endif() if(ENABLE_COVERAGE) MESSAGE(FATAL_ERROR "Not use c++-analyzer for generate code coverage. Use gcc. ") endif() endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "c++-analyzer" ) if(WARNINGS_ANSI_ISO) set(EXTRA_C_FLAGS "-Wextra -pedantic ${EXTRA_C_FLAGS}") # set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wlogical-op") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-long-long") # Don't warn about long long usage. endif() if(WARNINGS_ARE_ERRORS) set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Werror") endif() set(EXTRA_C_FLAGS "-Wall ${EXTRA_C_FLAGS}") set(EXTRA_C_FLAGS_DEBUG "${EXTRA_C_FLAGS_DEBUG} -O0") endif() if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -U_GLIBCXX_DEBUG") endif() if (MSVC) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8000000") endif() include(cmake/dynamic_analyzer_options.cmake REQUIRED) # Add user supplied extra options (optimization, etc...) # ========================================================== set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS}" CACHE INTERNAL "Extra compiler options") set(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE}" CACHE INTERNAL "Extra compiler options for Release build") set(EXTRA_C_FLAGS_DEBUG "${EXTRA_C_FLAGS_DEBUG}" CACHE INTERNAL "Extra compiler options for Debug build") #combine all "extra" options set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${EXTRA_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${EXTRA_C_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_C_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${EXTRA_C_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${EXTRA_C_FLAGS_DEBUG}") cppcheck-1.90/cmake/cxx11.cmake000066400000000000000000000006671357737443600163050ustar00rootroot00000000000000macro(use_cxx11) if (CMAKE_VERSION VERSION_LESS "3.1") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif () else () set (CMAKE_CXX_STANDARD 11) if (POLICY CMP0025) cmake_policy(SET CMP0025 NEW) endif () endif () endmacro(use_cxx11) cppcheck-1.90/cmake/dynamic_analyzer_options.cmake000066400000000000000000000016661357737443600224450ustar00rootroot00000000000000IF (USE_CLANG) SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fdiagnostics-show-category=name") ENDIF() IF(ANALYZE_MEMORY) SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fsanitize=memory") SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fsanitize-memory-track-origins=2") SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fno-omit-frame-pointer") # NOTE: tail call elimination -fno-optimize-sibling-calls ELSEIF(ANALYZE_ADDRESS) SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fsanitize=address") SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fno-omit-frame-pointer") ELSEIF(ANALYZE_THREAD) SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fsanitize=thread") ENDIF() IF(ANALYZE_UNDEFINED) SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fsanitize=undefined-trap") SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fsanitize-undefined-trap-on-error") SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fno-sanitize-recover") ENDIF() IF(ANALYZE_DATAFLOW) SET(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -fsanitize=dataflow") ENDIF() cppcheck-1.90/cmake/findDependencies.cmake000066400000000000000000000013631357737443600205620ustar00rootroot00000000000000if (BUILD_GUI) if (NOT WITH_QCHART) find_package(Qt5 COMPONENTS Core Gui Widgets PrintSupport LinguistTools REQUIRED) else() find_package(Qt5 COMPONENTS Core Gui Widgets PrintSupport LinguistTools Charts REQUIRED) endif() endif() if (HAVE_RULES) find_library(PCRE pcre) if (NOT PCRE) message(FATAL_ERROR "pcre dependency for RULES has not been found") endif() endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC OFF) if (NOT ${USE_MATCHCOMPILER_OPT} STREQUAL "Off") find_package(PythonInterp) if (NOT ${PYTHONINTERP_FOUND}) message(WARNING "No python interpreter found. Therefore, the match compiler is switched off.") set(USE_MATCHCOMPILER_OPT "Off") endif() endif() cppcheck-1.90/cmake/options.cmake000066400000000000000000000053721357737443600170320ustar00rootroot00000000000000#------------------------------------------------------ # Build type #------------------------------------------------------ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel" CACHE STRING "Configs" FORCE) if(DEFINED CMAKE_BUILD_TYPE) SET_PROPERTY(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${CMAKE_CONFIGURATION_TYPES}) endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif() # ---------------------------------------------------------------------------- # PROJECT CONFIGURATION # ---------------------------------------------------------------------------- option(USE_CLANG "Use Clang compiler" OFF) option(USE_ANALYZE "Use Clang compiler with analyze mode" OFF) option(ANALYZE_MEMORY "Clang dynamic analyzer: detector of uninitialized reads." OFF) option(ANALYZE_ADDRESS "Clang dynamic analyzer: fast memory error detector. " OFF) option(ANALYZE_THREAD "Clang dynamic analyzer: tool that detects data races. " OFF) option(ANALYZE_UNDEFINED "Clang dynamic analyzer: undefined behavior checker. " OFF) option(ANALYZE_DATAFLOW "Clang dynamic analyzer: general dynamic dataflow analysis." OFF) option(WARNINGS_ARE_ERRORS "Treat warnings as errors" OFF) option(WARNINGS_ANSI_ISO "Issue all the mandatory diagnostics Listed in C standard" ON) set(USE_MATCHCOMPILER "Auto" CACHE STRING "Usage of match compiler") set_property(CACHE USE_MATCHCOMPILER PROPERTY STRINGS Auto Off On Verify) if (USE_MATCHCOMPILER STREQUAL "Auto") if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") set(USE_MATCHCOMPILER_OPT "On") else() set(USE_MATCHCOMPILER_OPT "Off") endif() else() set(USE_MATCHCOMPILER_OPT ${USE_MATCHCOMPILER}) endif() option(BUILD_TESTS "Build tests" OFF) option(BUILD_GUI "Build the qt application" OFF) option(WITH_QCHART "When building GUI(need BUILD_GUI=ON), use Qt5 Charts" OFF) option(HAVE_RULES "Usage of rules (needs PCRE library and headers)" OFF) set(CMAKE_INCLUDE_DIRS_CONFIGCMAKE ${CMAKE_INSTALL_PREFIX}/include CACHE PATH "Output directory for headers") set(CMAKE_LIB_DIRS_CONFIGCMAKE ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "Output directory for libraries") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(FILESDIR ${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME} CACHE STRING "Cppcheck files directory") cppcheck-1.90/cmake/printInfo.cmake000066400000000000000000000054071357737443600173060ustar00rootroot00000000000000message( STATUS "------------------ General configuration for ${PROJECT_NAME} ${VERSION} -----------------") message( STATUS ) message( STATUS "CMake Generator = ${CMAKE_GENERATOR}") message( STATUS "Compiler = ${CMAKE_CXX_COMPILER_ID}") message( STATUS "Build type = ${CMAKE_BUILD_TYPE}") message( STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}" ) message( STATUS "C++ flags (General) = ${CMAKE_CXX_FLAGS}") message( STATUS "C++ flags (Release) = ${CMAKE_CXX_FLAGS_RELEASE}") message( STATUS "C++ flags (Debug) = ${CMAKE_CXX_FLAGS_DEBUG}") get_directory_property( DirDefs DIRECTORY ${CMAKE_SOURCE_DIR} COMPILE_DEFINITIONS ) foreach( d ${DirDefs} ) message( STATUS "Found Define: " ${d} ) endforeach() message( STATUS ) message( STATUS "---------------------------------------------------------" ) message( STATUS "USE_CLANG = ${USE_CLANG}" ) message( STATUS "USE_ANALYZE = ${USE_ANALYZE}" ) message( STATUS "ANALYZE_MEMORY = ${ANALYZE_MEMORY}" ) message( STATUS "ANALYZE_ADDRESS = ${ANALYZE_ADDRESS}" ) message( STATUS "ANALYZE_THREAD = ${ANALYZE_THREAD}" ) message( STATUS "ANALYZE_UNDEFINED = ${ANALYZE_UNDEFINED}" ) message( STATUS "ANALYZE_DATAFLOW = ${ANALYZE_DATAFLOW}" ) message( STATUS "WARNINGS_ANSI_ISO = ${WARNINGS_ANSI_ISO}" ) message( STATUS "WARNINGS_ARE_ERRORS = ${WARNINGS_ARE_ERRORS}" ) message( STATUS ) message( STATUS "USE_MATCHCOMPILER = ${USE_MATCHCOMPILER}" ) message( STATUS "USE_MATCHCOMPILER_OPT = ${USE_MATCHCOMPILER_OPT}" ) message( STATUS ) if(NOT DEFINED BUILD_SHARED_LIBS) message( STATUS "BUILD_SHARED_LIBS = OFF" ) else() message( STATUS "BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}" ) endif(NOT DEFINED BUILD_SHARED_LIBS) message( STATUS "BUILD_TESTS = ${BUILD_TESTS}" ) message( STATUS "BUILD_GUI = ${BUILD_GUI}" ) message( STATUS "WITH_QCHART = ${WITH_QCHART}" ) message( STATUS ) message( STATUS "HAVE_RULES = ${HAVE_RULES}" ) message( STATUS ) message( STATUS "Change a value with: cmake -D=" ) message( STATUS ) if(${USE_ANALYZE}) message( STATUS "------------------- Run static analyzer ----------------------" ) message( STATUS "##############################") message( STATUS "RUN: scan-build make" ) message( STATUS "##############################") message( STATUS ) endif() if(${ANALYZE_ADDRESS}) message("##########################################################") message(STATUS "For better visualization change environment variable: ASAN_SYMBOLIZER_PATH=/path/to/llvm-symbolizer") message(STATUS "Detect memory leaks, change environment variable: ASAN_OPTIONS=\"detect_leaks=1\"") message("##########################################################") endif() cppcheck-1.90/cmake/versions.cmake000066400000000000000000000005221357737443600171770ustar00rootroot00000000000000# Version for libraries CPP SET(VERSION "1.90") STRING(REGEX MATCHALL "[0-9]" VERSION_PARTS "${VERSION}") LIST(GET VERSION_PARTS 0 VERSION_MAJOR) LIST(GET VERSION_PARTS 1 VERSION_MINOR) LIST(GET VERSION_PARTS 2 VERSION_PATCH) SET(SOVERSION "${VERSION_MAJOR}.${VERSION_MINOR}") # Postfix of so's: SET(DLLVERSION "") SET(DEBUG_POSTFIX "") cppcheck-1.90/console_common.pri000066400000000000000000000010731357737443600167750ustar00rootroot00000000000000# console_common.pri # These are common definitions for console builds. win32 { CONFIG += embed_manifest_exe console DEFINES += _CRT_SECURE_NO_WARNINGS LIBS += -lshlwapi } # Add more strict compiling flags for GCC contains(QMAKE_CXX, g++) { QMAKE_CXXFLAGS_WARN_ON += -Wextra -pedantic -Wfloat-equal -Wcast-qual -Wlogical-op -Wno-long-long } # Change Visual Studio compiler (CL) warning level to W4 contains(QMAKE_CXX, cl) { QMAKE_CXXFLAGS_WARN_ON -= -W3 QMAKE_CXXFLAGS_WARN_ON += -W4 } CONFIG(release, debug|release) { DEFINES += NDEBUG } cppcheck-1.90/cppcheck-errors.rng000066400000000000000000000046631357737443600170610ustar00rootroot00000000000000 0 0 0 cppcheck-1.90/cppcheck.cbp000066400000000000000000000212521357737443600155160ustar00rootroot00000000000000 cppcheck-1.90/cppcheck.cppcheck000066400000000000000000000011231357737443600165250ustar00rootroot00000000000000 out1 true cppcheck-1.90/cppcheck.sln000066400000000000000000000123501357737443600155450ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29020.237 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "cli\cli.vcxproj", "{35CBDF51-2456-3EC3-99ED-113C30858883}" ProjectSection(ProjectDependencies) = postProject {C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testrunner", "test\testrunner.vcxproj", "{4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}" ProjectSection(ProjectDependencies) = postProject {C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cppcheck", "lib\cppcheck.vcxproj", "{C183DB5B-AD6C-423D-80CA-1F9549555A1A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Debug-PCRE|Win32 = Debug-PCRE|Win32 Debug-PCRE|x64 = Debug-PCRE|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 Release-PCRE|Win32 = Release-PCRE|Win32 Release-PCRE|x64 = Release-PCRE|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug|Win32.ActiveCfg = Debug|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug|Win32.Build.0 = Debug|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug|x64.ActiveCfg = Debug|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug|x64.Build.0 = Debug|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|Win32.ActiveCfg = Debug-PCRE|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|Win32.Build.0 = Debug-PCRE|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|x64.ActiveCfg = Debug-PCRE|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|x64.Build.0 = Debug-PCRE|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release|Win32.ActiveCfg = Release|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release|Win32.Build.0 = Release|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release|x64.ActiveCfg = Release|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release|x64.Build.0 = Release|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|Win32.ActiveCfg = Release-PCRE|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|Win32.Build.0 = Release-PCRE|Win32 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|x64.ActiveCfg = Release-PCRE|x64 {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|x64.Build.0 = Release-PCRE|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug|Win32.ActiveCfg = Debug|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug|Win32.Build.0 = Debug|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug|x64.ActiveCfg = Debug|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug|x64.Build.0 = Debug|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|Win32.ActiveCfg = Debug|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|Win32.Build.0 = Debug|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|x64.ActiveCfg = Debug|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|x64.Build.0 = Debug|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release|Win32.ActiveCfg = Release|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release|Win32.Build.0 = Release|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release|x64.ActiveCfg = Release|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release|x64.Build.0 = Release|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|Win32.ActiveCfg = Release|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|Win32.Build.0 = Release|Win32 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|x64.ActiveCfg = Release|x64 {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|x64.Build.0 = Release|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug|Win32.ActiveCfg = Debug|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug|Win32.Build.0 = Debug|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug|x64.ActiveCfg = Debug|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug|x64.Build.0 = Debug|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|Win32.ActiveCfg = Debug-PCRE|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|Win32.Build.0 = Debug-PCRE|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|x64.ActiveCfg = Debug-PCRE|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|x64.Build.0 = Debug-PCRE|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release|Win32.ActiveCfg = Release|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release|Win32.Build.0 = Release|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release|x64.ActiveCfg = Release|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release|x64.Build.0 = Release|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|Win32.ActiveCfg = Release-PCRE|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|Win32.Build.0 = Release-PCRE|Win32 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|x64.ActiveCfg = Release-PCRE|x64 {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|x64.Build.0 = Release-PCRE|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal cppcheck-1.90/createrelease000077500000000000000000000061771357737443600160130ustar00rootroot00000000000000#!/bin/bash # # A script for creating release packages. The release packages are create in the home directory. # # self check: # ./cppcheck -D__CPPCHECK__ --library=cppcheck-lib --enable=style --inconclusive --suppress=bitwiseOnBoolean --suppress=shadowFunction --suppress=useStlAlgorithm --suppress=*:externals/picojson.h cli gui/*.cpp lib # # Update translations # lupdate gui.pro # # Update copyright year # git diff 1.86 | grep '^diff --git a/' | sed 's|.* b/||' | xargs sed -i 's/Copyright (C) 2007-201./Copyright (C) 2007-2019/' # git diff | grep '^diff --git a/' # # Make sure "cppcheck --errorlist" works. For example with: # make test # ./cppcheck --errorlist > errlist.xml && xmllint --noout errlist.xml # # Update AUTHORS using output from: # git log --format='%aN' 1.81..HEAD | sort -u > AUTHORS2 && diff -y AUTHORS AUTHORS2 | less # # Update version numbers in: # sed -i "s/1.[0-9][0-9].99/1.89/" cli/main.cpp # sed -i "s/1.[0-9][0-9].99/1.89/" cmake/versions.cmake # sed -i "s/MINOR [0-9][0-9]/MINOR 89/" lib/version.h # sed -i "s/1.[0-9][0-9] dev/1.89/" win_installer/productInfo.wxi # sed -i "s/1.[0-9][0-9].99/1.89/" win_installer/productInfo.wxi # sed -i "s/Version 1.[0-9][0-9]/Version 1.89/" */*.md # Verify: # grep '\.99' */*.[ch]* && grep '[0-9][0-9] dev' */*.[ch]* # git commit -a -m "1.43: Set versions" # # Update the Makefile: # make dmake && ./dmake --release # git commit -a -m "1.43: Updated Makefile" # # Build and test the windows installer # # Tag: # git tag 1.43 # git push --tags # # Create a release folder on sourceforge: # https://sourceforge.net/projects/cppcheck/files/cppcheck/ # # Create release: # ./createrelease 1.43 # # Restore the Makefile: # make dmake && ./dmake # git commit -a -m "Makefile: Set debug mode" # # Update download link on index.php main page # # # write a news # # save "cppcheck --doc" output on wiki, write new release date on wikis # # compile new democlient: # ssh -t danielmarjamaki,cppcheck@shell.sourceforge.net create # ./build-cppcheck.sh 1.43 # # run daca with new release # 1. edit OLD_VERSION # 2. scp tools/donate-cpu-server.py danielmarjamaki@cppcheck.osuosl.org:daca@home/ # Tag to use tag=$1 # Name of release releasename=cppcheck-$tag set -e cd ~/cppcheck mkdir upload # Create archives.. git archive --format=tar --prefix=$releasename/ $tag | gzip > upload/$releasename.tar.gz git archive --format=tar --prefix=$releasename/ $tag | bzip2 > upload/$releasename.tar.bz2 git archive --format=zip -9 --prefix=$releasename/ $tag > upload/$releasename.zip cd upload wget https://www.pkeus.de/~philipp/Temp/$releasename-x64-Setup.msi #wget https://www.pkeus.de/~philipp/Temp/$releasename-x86-Setup.msi scp $releasename.* danielmarjamaki,cppcheck@frs.sourceforge.net:/home/frs/project/c/cp/cppcheck/cppcheck/$tag/ rm $releasename.* cd .. # Generate the manual.pdf, manual.html and version.txt make -j4 ./cppcheck --version > upload/version.txt cd ~/cppcheck/man ./buildman.sh mv manual.pdf ../upload/ mv manual.html ../upload/ mv reference-cfg-format.pdf ../upload/ mv reference-cfg-format.html ../upload/ cd ~/cppcheck/upload scp * danielmarjamaki,cppcheck@web.sourceforge.net:htdocs/ cd ~/cppcheck rm -rf upload cppcheck-1.90/cve-test-suite/000077500000000000000000000000001357737443600161275ustar00rootroot00000000000000cppcheck-1.90/cve-test-suite/cve-2018-1000618.cpp000066400000000000000000000005661357737443600206240ustar00rootroot00000000000000 // Reduced source code. Inspired by this fix: // https://github.com/EOSIO/eos/pull/4112/commits/ef62761c5e388880e8bb1bb41e8b512a5187f255 #include class C { std::set typedefs; bool is_type(int type) const { if (typedefs.find(type) != typedefs.end()) return is_type(type); // BUG: endless recursion return false; } }; cppcheck-1.90/cve-test-suite/cve-2018-11360.c000066400000000000000000000004601357737443600201100ustar00rootroot00000000000000 // CVE: CVE-2018-6836 // This is a simplified code example based on CVE-2018-11360. void *malloc(unsigned long); void free(void *); void f(int size) { char *ia5_string = malloc(size); // Hint: Off by one for (int i = 0; i <= size; i++) ia5_string[i]=0; // BUG free(ia5_string); } cppcheck-1.90/cve-test-suite/cve-2018-5334.c000066400000000000000000000002441357737443600200340ustar00rootroot00000000000000 // CVE-2018-5334 #define LEN 100 void f(const int *m_ptr, int sig_off, int rec_size) { if (m_ptr[sig_off] == 0xdd && (sig_off + 15 <= (rec_size - LEN))) {} } cppcheck-1.90/cve-test-suite/cve-2018-6836.c000066400000000000000000000012411357737443600200420ustar00rootroot00000000000000// Bug: free uninitialized pointer // Fix: https://code.wireshark.org/review/gitweb?p=wireshark.git;a=commit;h=28960d79cca262ac6b974f339697b299a1e28fef void *malloc(unsigned long); void free(void *); struct comment { int *data; }; struct table { struct comment *com; }; void destroy_table(struct table *comment_table) { free(comment_table->com->data); free(comment_table->com); free(comment_table); } void f() { struct table *comment_table = (struct table *)malloc(sizeof(struct table)); struct comment *comment_rec = (struct comment *)malloc(sizeof(struct comment)); comment_table->com = comment_rec; destroy_table(comment_table); } cppcheck-1.90/cve-test-suite/download.sh000077500000000000000000000023461357737443600203020ustar00rootroot00000000000000#!/bin/bash # Fetch CVE issues that are interesting to look at echo "CVE" > cve.txt for i in `seq 1 20`; do echo "page $i" # CVE 119 issues: # https://www.cvedetails.com/vulnerability-list/cweid-119/vulnerabilities.html # Use curl to get page $i: curl -s "https://www.cvedetails.com/vulnerability-list.php?vendor_id=0&product_id=0&version_id=0&page=$i&hasexp=0&opdos=0&opec=0&opov=0&opcsrf=0&opgpriv=0&opsqli=0&opxss=0&opdirt=0&opmemc=0&ophttprs=0&opbyp=0&opfileinc=0&opginf=0&cvssscoremin=0&cvssscoremax=0&year=0&month=0&cweid=119&order=1&trc=11185&sha=a76f56dbb935840fc028b135d550322223547356" > v.html # for each cve: for cve in $(grep /cve/CVE-2018- v.html | sed 's|.*/cve/CVE-2018-\([0-9]*\).*|CVE-2018-\1|'); do echo "$cve" >> cve.txt curl -s "https://www.cvedetails.com/cve/$cve/" > download-cve # cve type cat download-cve | grep '>Overflow<' >> cve.txt # is there a code reference? cat download-cve | grep 'https*://.*[a-f0-9]\{30,50\}' | sed 's|.*\(https*://[^ ]*[a-f0-9]\{30,\}\).*|\1|' >> cve.txt # is there a pull request reference? cat download-cve | grep 'https*://github.com/[^ ]*/pull/' | sed 's|.*\(https*://github.com/[^ ]*\).*|\1|' >> cve.txt done done rm v.html rm download-cve cppcheck-1.90/cve-test-suite/readme.txt000066400000000000000000000024731357737443600201330ustar00rootroot00000000000000 Background ========== The CVE database contains known vulnerabilities in various source code projects. For instance, to list known "overflow" vulnerabilities, this link can be used: https://www.cvedetails.com/vulnerability-list/cweid-119/vulnerabilities.html Many issues in the CVE database are "out of reach" for static analysis because of required domain knowledge etc. However there are also issues that could be "possible" to detect with static analysis. For each such issue that we see that we think is "possible" to detect with static analysis, we can create a file in this folder. The filename is the CVE id. The contents of the file should contain this info: * Recommended: URL that can be used to download source code, file with bug * Description * Reduced example code. The code should be plain C/C++ without dependencies. Possible usages: ================ The test cases can inspire future Cppcheck development. These files could be used for a quick and easy tool evaluation. For Cppcheck and other tools. Because only plain C/C++ is used, tools should have all info they need, so hopefully no extra configuration is needed. An extended tool evaluation can use the real source code. It's possible to lookup the real source code using the CWE id. However in such tool evaluation, the tools must be configured properly. cppcheck-1.90/democlient/000077500000000000000000000000001357737443600153715ustar00rootroot00000000000000cppcheck-1.90/democlient/build.sh000077500000000000000000000013101357737443600170220ustar00rootroot00000000000000#!/bin/bash # this script downloads and builds the democlient # syntax: # ./build 1.60.1 # cppcheck lib folder cppchecklib=cppcheck-$1/lib echo Downloading... wget http://downloads.sourceforge.net/project/cppcheck/cppcheck/$1/cppcheck-$1.tar.bz2 echo Unpacking... tar xjvf cppcheck-$1.tar.bz2 rm cppcheck-$1.tar.bz2 rm cppcheck-$1/Changelog echo Building... g++ -O2 -o democlient-$1.cgi -I$cppchecklib -Icppcheck-$1/externals/tinyxml cppcheck-$1/democlient/democlient.cpp $cppchecklib/*.cpp cppcheck-$1/externals/tinyxml/tinyxml2.cpp echo Copy cgi to webspace... cp democlient-$1.cgi /home/project-web/cppcheck/cgi-bin/democlient.cgi chmod +rx /home/project-web/cppcheck/cgi-bin/democlient.cgi echo Done! cppcheck-1.90/democlient/democlient.cpp000066400000000000000000000055071357737443600202270ustar00rootroot00000000000000#include #include #include #include #include #include "cppcheck.h" #include "version.h" static void unencode(const char *src, char *dest) { for (; *src; src++, dest++) { if (*src == '+') *dest = ' '; else if (*src == '%') { unsigned int code; if (std::sscanf(src+1, "%2x", &code) != 1) code = '?'; *dest = code; src += 2; } else *dest = *src; } *dest = '\0'; } static FILE *logfile = nullptr; class CppcheckExecutor : public ErrorLogger { private: const std::time_t stoptime; CppCheck cppcheck; public: CppcheckExecutor() : ErrorLogger() , stoptime(std::time(nullptr)+2U) , cppcheck(*this, false) { cppcheck.settings().addEnabled("all"); cppcheck.settings().inconclusive = true; } void run(const char code[]) { cppcheck.check("test.cpp", code); } void reportOut(const std::string &outmsg) override { } void reportErr(const ErrorLogger::ErrorMessage &msg) override { const std::string s = msg.toString(true); std::cout << s << std::endl; if (logfile != nullptr) std::fprintf(logfile, "%s\n", s.c_str()); } void reportProgress(const std::string& filename, const char stage[], const std::size_t value) override { if (std::time(nullptr) >= stoptime) { std::cout << "Time to analyse the code exceeded 2 seconds. Terminating.\n\n"; cppcheck.terminate(); } } }; int main() { std::cout << "Content-type: text/html\r\n\r\n" << "\n"; char data[4096] = {0}; const char *query_string = std::getenv("QUERY_STRING"); if (query_string) std::strncpy(data, query_string, sizeof(data)-2); const char *lenstr = std::getenv("CONTENT_LENGTH"); if (lenstr) { int len = std::min(1 + std::atoi(lenstr), (int)(sizeof(data) - 2)); std::fgets(data, len, stdin); } if (data[4000] != '\0') { std::cout << "For performance reasons the code must be shorter than 1000 chars."; return EXIT_SUCCESS; } const char *pdata = data; if (std::strncmp(pdata, "code=", 5)==0) pdata += 5; char code[4096] = {0}; unencode(pdata, code); logfile = std::fopen("democlient.log", "at"); if (logfile != nullptr) std::fprintf(logfile, "===========================================================\n%s\n", code); std::cout << "Cppcheck " CPPCHECK_VERSION_STRING "
";

    CppcheckExecutor cppcheckExecutor;
    cppcheckExecutor.run(code);

    std::fclose(logfile);

    std::cout << "
Done!"; return EXIT_SUCCESS; } cppcheck-1.90/doxyfile000066400000000000000000002365171357737443600150320ustar00rootroot00000000000000# Doxyfile 1.8.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed # in front of the TAG it is preceding . # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follows. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = Cppcheck # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doxyoutput # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, # Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, # Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. Note that you specify absolute paths here, but also # relative paths, which will be relative from the directory where doxygen is # started. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, # and language is one of the parsers supported by doxygen: IDL, Java, # Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, # C++. For instance to make doxygen treat .inc files as Fortran files (default # is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note # that for custom extensions you also need to set FILE_PATTERNS otherwise the # files are not read by doxygen. EXTENSION_MAPPING = # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES (the # default) will make doxygen replace the get and set methods by a property in # the documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields or simple typedef fields will be shown # inline in the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO (the default), structs, classes, and unions are shown on a separate # page (for HTML and Man pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can # be an expensive process and often the same symbol appear multiple times in # the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too # small doxygen will become slower. If the cache is too large, memory is wasted. # The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid # range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 # symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if section-label ... \endif # and \cond section-label ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. Do not use # file names with spaces, bibtex cannot handle them. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = cli/ \ gui/ \ lib/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be ignored. # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = # If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES # If CLANG_ASSISTED_PARSING is set to YES, then doxygen will use the clang parser # for more accurate parsing at the cost of reduced performance. This can be # particularly helpful with template rich C++ code for which doxygen's built-in # parser lacks the necessary type information. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified at INPUT and INCLUDE_PATH. CLANG_OPTIONS = #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If left blank doxygen will # generate a default style sheet. Note that it is recommended to use # HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this # tag will in the future become obsolete. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional # user-defined cascading style sheet that is included after the standard # style sheets created by doxygen. Using this option one can overrule # certain style aspects. This is preferred over using HTML_STYLESHEET # since it does not replace the standard style sheet and is therefore more # robust against future updates. Doxygen will copy the style sheet file to # the output directory. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely # identify the documentation publisher. This should be a reverse domain-name # style string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and # SVG. The default value is HTML-CSS, which is slower, but has the best # compatibility. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript # pieces of code that will be used on startup of the MathJax code. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a web server instead of a web client using Javascript. # There are two flavours of web server based search depending on the # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # searching and an index file used by the script. When EXTERNAL_SEARCH is # enabled the indexing and searching needs to be provided by external tools. # See the manual for details. SERVER_BASED_SEARCH = NO # When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP # script for searching. Instead the search results are written to an XML file # which needs to be processed by an external indexer. Doxygen will invoke an # external search engine pointed to by the SEARCHENGINE_URL option to obtain # the search results. Doxygen ships with an example indexer (doxyindexer) and # search engine (doxysearch.cgi) which are based on the open source search # engine library Xapian. See the manual for configuration details. EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will returned the search results when EXTERNAL_SEARCH is enabled. # Doxygen ships with an example search engine (doxysearch) which is based on # the open source search engine library Xapian. See the manual for configuration # details. SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the # SEARCHDATA_FILE tag the name of this file can be specified. SEARCHDATA_FILE = searchdata.xml # When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the # EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is # useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple # projects and redirect the results back to the right project. EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are # all added to the same external search index. Each project needs to have a # unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id # of to a relative location where the documentation can be found. # The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4 will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images # or other source files which should be copied to the LaTeX output directory. # Note that the files will be copied as-is; there are no commands or markers # available. LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- # If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files # that can be used to generate PDF. GENERATE_DOCBOOK = NO # The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be put in # front of it. If left blank docbook will be used as the default path. DOCBOOK_OUTPUT = docbook #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # If the EXTERNAL_PAGES tag is set to YES all external pages will be listed # in the related pages index. If set to NO, only the current project's # pages will be listed. EXTERNAL_PAGES = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # manageable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES cppcheck-1.90/externals/000077500000000000000000000000001357737443600152535ustar00rootroot00000000000000cppcheck-1.90/externals/externals.pri000066400000000000000000000004141357737443600177730ustar00rootroot00000000000000INCLUDEPATH += $${PWD} \ $${PWD}/simplecpp \ $${PWD}/tinyxml HEADERS += $${PWD}/simplecpp/simplecpp.h \ $${PWD}/tinyxml/tinyxml2.h SOURCES += $${PWD}/simplecpp/simplecpp.cpp \ $${PWD}/tinyxml/tinyxml2.cpp cppcheck-1.90/externals/picojson.h000066400000000000000000000627661357737443600172710ustar00rootroot00000000000000/* * Copyright 2009-2010 Cybozu Labs, Inc. * Copyright 2011-2014 Kazuho Oku * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef picojson_h #define picojson_h #include #include #include #include #include #include #include #include #include #include #include // for isnan/isinf #if __cplusplus>=201103L # include #else extern "C" { # ifdef _MSC_VER # include # elif defined(__INTEL_COMPILER) # include # else # include # endif } #endif // experimental support for int64_t (see README.mkdn for detail) #ifdef PICOJSON_USE_INT64 # define __STDC_FORMAT_MACROS # include # include #endif // to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 #ifndef PICOJSON_USE_LOCALE # define PICOJSON_USE_LOCALE 1 #endif #if PICOJSON_USE_LOCALE extern "C" { # include } #endif #ifndef PICOJSON_ASSERT # define PICOJSON_ASSERT(e) do { if (! (e)) throw std::runtime_error(#e); } while (0) #endif #ifdef _MSC_VER #define SNPRINTF _snprintf_s #pragma warning(push) #pragma warning(disable : 4244) // conversion from int to char #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4702) // unreachable code #else #define SNPRINTF snprintf #endif namespace picojson { enum { null_type, boolean_type, number_type, string_type, array_type, object_type #ifdef PICOJSON_USE_INT64 , int64_type #endif }; enum { INDENT_WIDTH = 2 }; struct null {}; class value { public: typedef std::vector array; typedef std::map object; union _storage { bool boolean_; double number_; #ifdef PICOJSON_USE_INT64 int64_t int64_; #endif std::string* string_; array* array_; object* object_; }; protected: int type_; _storage u_; public: value(); value(int type, bool); explicit value(bool b); #ifdef PICOJSON_USE_INT64 explicit value(int64_t i); #endif explicit value(double n); explicit value(const std::string& s); explicit value(const array& a); explicit value(const object& o); explicit value(const char* s); value(const char* s, size_t len); ~value(); value(const value& x); value& operator=(const value& x); void swap(value& x); template bool is() const; template const T& get() const; template T& get(); bool evaluate_as_boolean() const; const value& get(size_t idx) const; const value& get(const std::string& key) const; value& get(size_t idx); value& get(const std::string& key); bool contains(size_t idx) const; bool contains(const std::string& key) const; std::string to_str() const; template void serialize(Iter os, bool prettify = false) const; std::string serialize(bool prettify = false) const; private: template value(const T*); // intentionally defined to block implicit conversion of pointer to bool template static void _indent(Iter os, int indent); template void _serialize(Iter os, int indent) const; std::string _serialize(int indent) const; }; typedef value::array array; typedef value::object object; inline value::value() : type_(null_type) {} inline value::value(int type, bool) : type_(type) { switch (type) { #define INIT(p, v) case p##type: u_.p = v; break INIT(boolean_, false); INIT(number_, 0.0); #ifdef PICOJSON_USE_INT64 INIT(int64_, 0); #endif INIT(string_, new std::string()); INIT(array_, new array()); INIT(object_, new object()); #undef INIT default: break; } } inline value::value(bool b) : type_(boolean_type) { u_.boolean_ = b; } #ifdef PICOJSON_USE_INT64 inline value::value(int64_t i) : type_(int64_type) { u_.int64_ = i; } #endif inline value::value(double n) : type_(number_type) { if ( #ifdef _MSC_VER ! _finite(n) #elif __cplusplus>=201103L || !(defined(isnan) && defined(isinf)) std::isnan(n) || std::isinf(n) #else isnan(n) || isinf(n) #endif ) { throw std::overflow_error(""); } u_.number_ = n; } inline value::value(const std::string& s) : type_(string_type) { u_.string_ = new std::string(s); } inline value::value(const array& a) : type_(array_type) { u_.array_ = new array(a); } inline value::value(const object& o) : type_(object_type) { u_.object_ = new object(o); } inline value::value(const char* s) : type_(string_type) { u_.string_ = new std::string(s); } inline value::value(const char* s, size_t len) : type_(string_type) { u_.string_ = new std::string(s, len); } inline value::~value() { switch (type_) { #define DEINIT(p) case p##type: delete u_.p; break DEINIT(string_); DEINIT(array_); DEINIT(object_); #undef DEINIT default: break; } } inline value::value(const value& x) : type_(x.type_) { switch (type_) { #define INIT(p, v) case p##type: u_.p = v; break INIT(string_, new std::string(*x.u_.string_)); INIT(array_, new array(*x.u_.array_)); INIT(object_, new object(*x.u_.object_)); #undef INIT default: u_ = x.u_; break; } } inline value& value::operator=(const value& x) { if (this != &x) { value t(x); swap(t); } return *this; } inline void value::swap(value& x) { std::swap(type_, x.type_); std::swap(u_, x.u_); } #define IS(ctype, jtype) \ template <> inline bool value::is() const { \ return type_ == jtype##_type; \ } IS(null, null) IS(bool, boolean) #ifdef PICOJSON_USE_INT64 IS(int64_t, int64) #endif IS(std::string, string) IS(array, array) IS(object, object) #undef IS template <> inline bool value::is() const { return type_ == number_type #ifdef PICOJSON_USE_INT64 || type_ == int64_type #endif ; } #define GET(ctype, var) \ template <> inline const ctype& value::get() const { \ PICOJSON_ASSERT("type mismatch! call is() before get()" \ && is()); \ return var; \ } \ template <> inline ctype& value::get() { \ PICOJSON_ASSERT("type mismatch! call is() before get()" \ && is()); \ return var; \ } GET(bool, u_.boolean_) GET(std::string, *u_.string_) GET(array, *u_.array_) GET(object, *u_.object_) #ifdef PICOJSON_USE_INT64 GET(double, (type_ == int64_type && (const_cast(this)->type_ = number_type, const_cast(this)->u_.number_ = u_.int64_), u_.number_)) GET(int64_t, u_.int64_) #else GET(double, u_.number_) #endif #undef GET inline bool value::evaluate_as_boolean() const { switch (type_) { case null_type: return false; case boolean_type: return u_.boolean_; case number_type: return u_.number_ != 0; case string_type: return ! u_.string_->empty(); default: return true; } } inline const value& value::get(size_t idx) const { static value s_null; PICOJSON_ASSERT(is()); return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } inline value& value::get(size_t idx) { static value s_null; PICOJSON_ASSERT(is()); return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; } inline const value& value::get(const std::string& key) const { static value s_null; PICOJSON_ASSERT(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } inline value& value::get(const std::string& key) { static value s_null; PICOJSON_ASSERT(is()); object::iterator i = u_.object_->find(key); return i != u_.object_->end() ? i->second : s_null; } inline bool value::contains(size_t idx) const { PICOJSON_ASSERT(is()); return idx < u_.array_->size(); } inline bool value::contains(const std::string& key) const { PICOJSON_ASSERT(is()); object::const_iterator i = u_.object_->find(key); return i != u_.object_->end(); } inline std::string value::to_str() const { switch (type_) { case null_type: return "null"; case boolean_type: return u_.boolean_ ? "true" : "false"; #ifdef PICOJSON_USE_INT64 case int64_type: { char buf[sizeof("-9223372036854775808")]; SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); return buf; } #endif case number_type: { char buf[256]; double tmp; SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); #if PICOJSON_USE_LOCALE char *decimal_point = localeconv()->decimal_point; if (strcmp(decimal_point, ".") != 0) { size_t decimal_point_len = strlen(decimal_point); for (char *p = buf; *p != '\0'; ++p) { if (strncmp(p, decimal_point, decimal_point_len) == 0) { return std::string(buf, p) + "." + (p + decimal_point_len); } } } #endif return buf; } case string_type: return *u_.string_; case array_type: return "array"; case object_type: return "object"; default: PICOJSON_ASSERT(0); #ifdef _MSC_VER __assume(0); #endif } return std::string(); } template void copy(const std::string& s, Iter oi) { std::copy(s.begin(), s.end(), oi); } template void serialize_str(const std::string& s, Iter oi) { *oi++ = '"'; for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) { switch (*i) { #define MAP(val, sym) case val: copy(sym, oi); break MAP('"', "\\\""); MAP('\\', "\\\\"); MAP('/', "\\/"); MAP('\b', "\\b"); MAP('\f', "\\f"); MAP('\n', "\\n"); MAP('\r', "\\r"); MAP('\t', "\\t"); #undef MAP default: if (static_cast(*i) < 0x20 || *i == 0x7f) { char buf[7]; SNPRINTF(buf, sizeof(buf), "\\u%04x", *i & 0xff); copy(buf, buf + 6, oi); } else { *oi++ = *i; } break; } } *oi++ = '"'; } template void value::serialize(Iter oi, bool prettify) const { return _serialize(oi, prettify ? 0 : -1); } inline std::string value::serialize(bool prettify) const { return _serialize(prettify ? 0 : -1); } template void value::_indent(Iter oi, int indent) { *oi++ = '\n'; for (int i = 0; i < indent * INDENT_WIDTH; ++i) { *oi++ = ' '; } } template void value::_serialize(Iter oi, int indent) const { switch (type_) { case string_type: serialize_str(*u_.string_, oi); break; case array_type: { *oi++ = '['; if (indent != -1) { ++indent; } for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) { if (i != u_.array_->begin()) { *oi++ = ','; } if (indent != -1) { _indent(oi, indent); } i->_serialize(oi, indent); } if (indent != -1) { --indent; if (! u_.array_->empty()) { _indent(oi, indent); } } *oi++ = ']'; break; } case object_type: { *oi++ = '{'; if (indent != -1) { ++indent; } for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) { if (i != u_.object_->begin()) { *oi++ = ','; } if (indent != -1) { _indent(oi, indent); } serialize_str(i->first, oi); *oi++ = ':'; if (indent != -1) { *oi++ = ' '; } i->second._serialize(oi, indent); } if (indent != -1) { --indent; if (! u_.object_->empty()) { _indent(oi, indent); } } *oi++ = '}'; break; } default: copy(to_str(), oi); break; } if (indent == 0) { *oi++ = '\n'; } } inline std::string value::_serialize(int indent) const { std::string s; _serialize(std::back_inserter(s), indent); return s; } template class input { protected: Iter cur_, end_; int last_ch_; bool ungot_; int line_; public: input(const Iter& first, const Iter& last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {} int getc() { if (ungot_) { ungot_ = false; return last_ch_; } if (cur_ == end_) { last_ch_ = -1; return -1; } if (last_ch_ == '\n') { line_++; } last_ch_ = *cur_ & 0xff; ++cur_; return last_ch_; } void ungetc() { if (last_ch_ != -1) { PICOJSON_ASSERT(! ungot_); ungot_ = true; } } Iter cur() const { return cur_; } int line() const { return line_; } void skip_ws() { while (1) { int ch = getc(); if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { ungetc(); break; } } } bool expect(int expect) { skip_ws(); if (getc() != expect) { ungetc(); return false; } return true; } bool match(const std::string& pattern) { for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { if (getc() != *pi) { ungetc(); return false; } } return true; } }; template inline int _parse_quadhex(input &in) { int uni_ch = 0, hex; for (int i = 0; i < 4; i++) { if ((hex = in.getc()) == -1) { return -1; } if ('0' <= hex && hex <= '9') { hex -= '0'; } else if ('A' <= hex && hex <= 'F') { hex -= 'A' - 0xa; } else if ('a' <= hex && hex <= 'f') { hex -= 'a' - 0xa; } else { in.ungetc(); return -1; } uni_ch = uni_ch * 16 + hex; } return uni_ch; } template inline bool _parse_codepoint(String& out, input& in) { int uni_ch; if ((uni_ch = _parse_quadhex(in)) == -1) { return false; } if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { if (0xdc00 <= uni_ch) { // a second 16-bit of a surrogate pair appeared return false; } // first 16-bit of surrogate pair, get the next one if (in.getc() != '\\' || in.getc() != 'u') { in.ungetc(); return false; } int second = _parse_quadhex(in); if (! (0xdc00 <= second && second <= 0xdfff)) { return false; } uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); uni_ch += 0x10000; } if (uni_ch < 0x80) { out.push_back(uni_ch); } else { if (uni_ch < 0x800) { out.push_back(0xc0 | (uni_ch >> 6)); } else { if (uni_ch < 0x10000) { out.push_back(0xe0 | (uni_ch >> 12)); } else { out.push_back(0xf0 | (uni_ch >> 18)); out.push_back(0x80 | ((uni_ch >> 12) & 0x3f)); } out.push_back(0x80 | ((uni_ch >> 6) & 0x3f)); } out.push_back(0x80 | (uni_ch & 0x3f)); } return true; } template inline bool _parse_string(String& out, input& in) { while (1) { int ch = in.getc(); if (ch < ' ') { in.ungetc(); return false; } else if (ch == '"') { return true; } else if (ch == '\\') { if ((ch = in.getc()) == -1) { return false; } switch (ch) { #define MAP(sym, val) case sym: out.push_back(val); break MAP('"', '\"'); MAP('\\', '\\'); MAP('/', '/'); MAP('b', '\b'); MAP('f', '\f'); MAP('n', '\n'); MAP('r', '\r'); MAP('t', '\t'); #undef MAP case 'u': if (! _parse_codepoint(out, in)) { return false; } break; default: return false; } } else { out.push_back(ch); } } return false; } template inline bool _parse_array(Context& ctx, input& in) { if (! ctx.parse_array_start()) { return false; } size_t idx = 0; if (in.expect(']')) { return ctx.parse_array_stop(idx); } do { if (! ctx.parse_array_item(in, idx)) { return false; } idx++; } while (in.expect(',')); return in.expect(']') && ctx.parse_array_stop(idx); } template inline bool _parse_object(Context& ctx, input& in) { if (! ctx.parse_object_start()) { return false; } if (in.expect('}')) { return true; } do { std::string key; if (! in.expect('"') || ! _parse_string(key, in) || ! in.expect(':')) { return false; } if (! ctx.parse_object_item(in, key)) { return false; } } while (in.expect(',')); return in.expect('}'); } template inline std::string _parse_number(input& in) { std::string num_str; while (1) { int ch = in.getc(); if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') { num_str.push_back(ch); } else if (ch == '.') { #if PICOJSON_USE_LOCALE num_str += localeconv()->decimal_point; #else num_str.push_back('.'); #endif } else { in.ungetc(); break; } } return num_str; } template inline bool _parse(Context& ctx, input& in) { in.skip_ws(); int ch = in.getc(); switch (ch) { #define IS(ch, text, op) case ch: \ if (in.match(text) && op) { \ return true; \ } else { \ return false; \ } IS('n', "ull", ctx.set_null()); IS('f', "alse", ctx.set_bool(false)); IS('t', "rue", ctx.set_bool(true)); #undef IS case '"': return ctx.parse_string(in); case '[': return _parse_array(ctx, in); case '{': return _parse_object(ctx, in); default: if (('0' <= ch && ch <= '9') || ch == '-') { double f; char *endp; in.ungetc(); std::string num_str = _parse_number(in); if (num_str.empty()) { return false; } #ifdef PICOJSON_USE_INT64 { errno = 0; intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); if (errno == 0 && std::numeric_limits::min() <= ival && ival <= std::numeric_limits::max() && endp == num_str.c_str() + num_str.size()) { ctx.set_int64(ival); return true; } } #endif f = strtod(num_str.c_str(), &endp); if (endp == num_str.c_str() + num_str.size()) { ctx.set_number(f); return true; } return false; } break; } in.ungetc(); return false; } class deny_parse_context { public: bool set_null() { return false; } bool set_bool(bool) { return false; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t) { return false; } #endif bool set_number(double) { return false; } template bool parse_string(input&) { return false; } bool parse_array_start() { return false; } template bool parse_array_item(input&, size_t) { return false; } bool parse_array_stop(size_t) { return false; } bool parse_object_start() { return false; } template bool parse_object_item(input&, const std::string&) { return false; } }; class default_parse_context { protected: value* out_; public: default_parse_context(value* out) : out_(out) {} bool set_null() { *out_ = value(); return true; } bool set_bool(bool b) { *out_ = value(b); return true; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t i) { *out_ = value(i); return true; } #endif bool set_number(double f) { *out_ = value(f); return true; } template bool parse_string(input& in) { *out_ = value(string_type, false); return _parse_string(out_->get(), in); } bool parse_array_start() { *out_ = value(array_type, false); return true; } template bool parse_array_item(input& in, size_t) { array& a = out_->get(); a.push_back(value()); default_parse_context ctx(&a.back()); return _parse(ctx, in); } bool parse_array_stop(size_t) { return true; } bool parse_object_start() { *out_ = value(object_type, false); return true; } template bool parse_object_item(input& in, const std::string& key) { object& o = out_->get(); default_parse_context ctx(&o[key]); return _parse(ctx, in); } private: default_parse_context(const default_parse_context&); default_parse_context& operator=(const default_parse_context&); }; class null_parse_context { public: struct dummy_str { void push_back(int) {} }; public: null_parse_context() {} bool set_null() { return true; } bool set_bool(bool) { return true; } #ifdef PICOJSON_USE_INT64 bool set_int64(int64_t) { return true; } #endif bool set_number(double) { return true; } template bool parse_string(input& in) { dummy_str s; return _parse_string(s, in); } bool parse_array_start() { return true; } template bool parse_array_item(input& in, size_t) { return _parse(*this, in); } bool parse_array_stop(size_t) { return true; } bool parse_object_start() { return true; } template bool parse_object_item(input& in, const std::string&) { return _parse(*this, in); } private: null_parse_context(const null_parse_context&); null_parse_context& operator=(const null_parse_context&); }; // obsolete, use the version below template inline std::string parse(value& out, Iter& pos, const Iter& last) { std::string err; pos = parse(out, pos, last, &err); return err; } template inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) { input in(first, last); if (! _parse(ctx, in) && err != NULL) { char buf[64]; SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); *err = buf; while (1) { int ch = in.getc(); if (ch == -1 || ch == '\n') { break; } else if (ch >= ' ') { err->push_back(ch); } } } return in.cur(); } template inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) { default_parse_context ctx(&out); return _parse(ctx, first, last, err); } inline std::string parse(value& out, const std::string& s) { std::string err; parse(out, s.begin(), s.end(), &err); return err; } inline std::string parse(value& out, std::istream& is) { std::string err; parse(out, std::istreambuf_iterator(is.rdbuf()), std::istreambuf_iterator(), &err); return err; } template struct last_error_t { static std::string s; }; template std::string last_error_t::s; inline void set_last_error(const std::string& s) { last_error_t::s = s; } inline const std::string& get_last_error() { return last_error_t::s; } inline bool operator==(const value& x, const value& y) { if (x.is()) return y.is(); #define PICOJSON_CMP(type) \ if (x.is()) \ return y.is() && x.get() == y.get() PICOJSON_CMP(bool); PICOJSON_CMP(double); PICOJSON_CMP(std::string); PICOJSON_CMP(array); PICOJSON_CMP(object); #undef PICOJSON_CMP PICOJSON_ASSERT(0); #ifdef _MSC_VER __assume(0); #endif return false; } inline bool operator!=(const value& x, const value& y) { return ! (x == y); } } namespace std { template<> inline void swap(picojson::value& x, picojson::value& y) { x.swap(y); } } inline std::istream& operator>>(std::istream& is, picojson::value& x) { picojson::set_last_error(std::string()); std::string err = picojson::parse(x, is); if (! err.empty()) { picojson::set_last_error(err); is.setstate(std::ios::failbit); } return is; } inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) { x.serialize(std::ostream_iterator(os)); return os; } #ifdef _MSC_VER #pragma warning(pop) #endif #endif cppcheck-1.90/externals/simplecpp/000077500000000000000000000000001357737443600172475ustar00rootroot00000000000000cppcheck-1.90/externals/simplecpp/CMakeLists.txt000066400000000000000000000001441357737443600220060ustar00rootroot00000000000000file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") add_library(simplecpp_objs OBJECT ${srcs} ${hdrs}) cppcheck-1.90/externals/simplecpp/LICENSE000066400000000000000000000167441357737443600202700ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. cppcheck-1.90/externals/simplecpp/simplecpp.cpp000066400000000000000000003241351357737443600217570ustar00rootroot00000000000000/* * simplecpp - A simple and high-fidelity C/C++ preprocessor library * Copyright (C) 2016 Daniel Marjamäki. * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) #define SIMPLECPP_WINDOWS #define NOMINMAX #endif #include "simplecpp.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef SIMPLECPP_WINDOWS #include #undef ERROR #undef TRUE #endif static bool isHex(const std::string &s) { return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0); } static bool isOct(const std::string &s) { return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8'); } static const simplecpp::TokenString DEFINE("define"); static const simplecpp::TokenString UNDEF("undef"); static const simplecpp::TokenString INCLUDE("include"); static const simplecpp::TokenString ERROR("error"); static const simplecpp::TokenString WARNING("warning"); static const simplecpp::TokenString IF("if"); static const simplecpp::TokenString IFDEF("ifdef"); static const simplecpp::TokenString IFNDEF("ifndef"); static const simplecpp::TokenString DEFINED("defined"); static const simplecpp::TokenString ELSE("else"); static const simplecpp::TokenString ELIF("elif"); static const simplecpp::TokenString ENDIF("endif"); static const simplecpp::TokenString PRAGMA("pragma"); static const simplecpp::TokenString ONCE("once"); template static std::string toString(T t) { std::ostringstream ostr; ostr << t; return ostr.str(); } static long long stringToLL(const std::string &s) { long long ret; const bool hex = isHex(s); const bool oct = isOct(s); std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); if (hex) istr >> std::hex; else if (oct) istr >> std::oct; istr >> ret; return ret; } static unsigned long long stringToULL(const std::string &s) { unsigned long long ret; const bool hex = isHex(s); const bool oct = isOct(s); std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); if (hex) istr >> std::hex; else if (oct) istr >> std::oct; istr >> ret; return ret; } static bool startsWith(const std::string &str, const std::string &s) { return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0); } static bool endsWith(const std::string &s, const std::string &e) { return (s.size() >= e.size() && s.compare(s.size() - e.size(), e.size(), e) == 0); } static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) { return tok1 && tok2 && tok1->location.sameline(tok2->location); } static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt) { return (tok->name && tok->str() == alt && tok->previous && tok->next && (tok->previous->number || tok->previous->name || tok->previous->op == ')') && (tok->next->number || tok->next->name || tok->next->op == '(')); } static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt) { return ((tok->name && tok->str() == alt) && (!tok->previous || tok->previous->op == '(') && (tok->next && (tok->next->name || tok->next->number))); } const std::string simplecpp::Location::emptyFileName; void simplecpp::Location::adjust(const std::string &str) { if (str.find_first_of("\r\n") == std::string::npos) { col += str.size(); return; } for (std::size_t i = 0U; i < str.size(); ++i) { col++; if (str[i] == '\n' || str[i] == '\r') { col = 1; line++; if (str[i] == '\r' && (i+1)previous) tok = tok->previous; for (; tok; tok = tok->next) { if (tok->previous) { std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); } std::cout << tok->str(); } std::cout << std::endl; } void simplecpp::Token::printOut() const { for (const Token *tok = this; tok; tok = tok->next) { if (tok != this) { std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); } std::cout << tok->str(); } std::cout << std::endl; } simplecpp::TokenList::TokenList(std::vector &filenames) : frontToken(NULL), backToken(NULL), files(filenames) {} simplecpp::TokenList::TokenList(std::istream &istr, std::vector &filenames, const std::string &filename, OutputList *outputList) : frontToken(NULL), backToken(NULL), files(filenames) { readfile(istr,filename,outputList); } simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(NULL), backToken(NULL), files(other.files) { *this = other; } #if __cplusplus >= 201103L simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(NULL), backToken(NULL), files(other.files) { *this = std::move(other); } #endif simplecpp::TokenList::~TokenList() { clear(); } simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other) { if (this != &other) { clear(); for (const Token *tok = other.cfront(); tok; tok = tok->next) push_back(new Token(*tok)); sizeOfType = other.sizeOfType; } return *this; } #if __cplusplus >= 201103L simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other) { if (this != &other) { clear(); backToken = other.backToken; other.backToken = NULL; frontToken = other.frontToken; other.frontToken = NULL; sizeOfType = std::move(other.sizeOfType); } return *this; } #endif void simplecpp::TokenList::clear() { backToken = NULL; while (frontToken) { Token *next = frontToken->next; delete frontToken; frontToken = next; } sizeOfType.clear(); } void simplecpp::TokenList::push_back(Token *tok) { if (!frontToken) frontToken = tok; else backToken->next = tok; tok->previous = backToken; backToken = tok; } void simplecpp::TokenList::dump() const { std::cout << stringify() << std::endl; } std::string simplecpp::TokenList::stringify() const { std::ostringstream ret; Location loc(files); for (const Token *tok = cfront(); tok; tok = tok->next) { if (tok->location.line < loc.line || tok->location.fileIndex != loc.fileIndex) { ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n"; loc = tok->location; } while (tok->location.line > loc.line) { ret << '\n'; loc.line++; } if (sameline(tok->previous, tok)) ret << ' '; ret << tok->str(); loc.adjust(tok->str()); } return ret.str(); } static unsigned char readChar(std::istream &istr, unsigned int bom) { unsigned char ch = (unsigned char)istr.get(); // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the // character is non-ASCII character then replace it with 0xff if (bom == 0xfeff || bom == 0xfffe) { const unsigned char ch2 = (unsigned char)istr.get(); const int ch16 = (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch); ch = (unsigned char)((ch16 >= 0x80) ? 0xff : ch16); } // Handling of newlines.. if (ch == '\r') { ch = '\n'; if (bom == 0 && (char)istr.peek() == '\n') (void)istr.get(); else if (bom == 0xfeff || bom == 0xfffe) { int c1 = istr.get(); int c2 = istr.get(); int ch16 = (bom == 0xfeff) ? (c1<<8 | c2) : (c2<<8 | c1); if (ch16 != '\n') { istr.unget(); istr.unget(); } } } return ch; } static unsigned char peekChar(std::istream &istr, unsigned int bom) { unsigned char ch = (unsigned char)istr.peek(); // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the // character is non-ASCII character then replace it with 0xff if (bom == 0xfeff || bom == 0xfffe) { (void)istr.get(); const unsigned char ch2 = (unsigned char)istr.peek(); istr.unget(); const int ch16 = (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch); ch = (unsigned char)((ch16 >= 0x80) ? 0xff : ch16); } // Handling of newlines.. if (ch == '\r') ch = '\n'; return ch; } static void ungetChar(std::istream &istr, unsigned int bom) { istr.unget(); if (bom == 0xfeff || bom == 0xfffe) istr.unget(); } static unsigned short getAndSkipBOM(std::istream &istr) { const int ch1 = istr.peek(); // The UTF-16 BOM is 0xfffe or 0xfeff. if (ch1 >= 0xfe) { unsigned short bom = ((unsigned char)istr.get() << 8); if (istr.peek() >= 0xfe) return bom | (unsigned char)istr.get(); istr.unget(); return 0; } // Skip UTF-8 BOM 0xefbbbf if (ch1 == 0xef) { (void)istr.get(); if (istr.get() == 0xbb && istr.peek() == 0xbf) { (void)istr.get(); } else { istr.unget(); istr.unget(); } } return 0; } static bool isNameChar(unsigned char ch) { return std::isalnum(ch) || ch == '_' || ch == '$'; } static std::string escapeString(const std::string &str) { std::ostringstream ostr; ostr << '\"'; for (std::size_t i = 1U; i < str.size() - 1; ++i) { char c = str[i]; if (c == '\\' || c == '\"' || c == '\'') ostr << '\\'; ostr << c; } ostr << '\"'; return ostr.str(); } static void portabilityBackslash(simplecpp::OutputList *outputList, const std::vector &files, const simplecpp::Location &location) { if (!outputList) return; simplecpp::Output err(files); err.type = simplecpp::Output::PORTABILITY_BACKSLASH; err.location = location; err.msg = "Combination 'backslash space newline' is not portable."; outputList->push_back(err); } static bool isStringLiteralPrefix(const std::string &str) { return str == "u" || str == "U" || str == "L" || str == "u8" || str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R"; } void simplecpp::TokenList::lineDirective(unsigned int fileIndex, unsigned int line, Location *location) { if (fileIndex != location->fileIndex || line >= location->line) { location->fileIndex = fileIndex; location->line = line; return; } if (line + 2 >= location->line) { location->line = line; while (cback()->op != '#') deleteToken(back()); deleteToken(back()); return; } } void simplecpp::TokenList::readfile(std::istream &istr, const std::string &filename, OutputList *outputList) { std::stack loc; unsigned int multiline = 0U; const Token *oldLastToken = NULL; const unsigned short bom = getAndSkipBOM(istr); Location location(files); location.fileIndex = fileIndex(filename); location.line = 1U; location.col = 1U; while (istr.good()) { unsigned char ch = readChar(istr,bom); if (!istr.good()) break; if (ch < ' ' && ch != '\t' && ch != '\n' && ch != '\r') ch = ' '; if (ch >= 0x80) { if (outputList) { simplecpp::Output err(files); err.type = simplecpp::Output::UNHANDLED_CHAR_ERROR; err.location = location; std::ostringstream s; s << (int)ch; err.msg = "The code contains unhandled character(s) (character code=" + s.str() + "). Neither unicode nor extended ascii is supported."; outputList->push_back(err); } clear(); return; } if (ch == '\n') { if (cback() && cback()->op == '\\') { if (location.col > cback()->location.col + 1U) portabilityBackslash(outputList, files, cback()->location); ++multiline; deleteToken(back()); } else { location.line += multiline + 1; multiline = 0U; } if (!multiline) location.col = 1; if (oldLastToken != cback()) { oldLastToken = cback(); const std::string lastline(lastLine()); if (lastline == "# file %str%") { loc.push(location); location.fileIndex = fileIndex(cback()->str().substr(1U, cback()->str().size() - 2U)); location.line = 1U; } else if (lastline == "# line %num%") { lineDirective(location.fileIndex, std::atol(cback()->str().c_str()), &location); } else if (lastline == "# %num% %str%" || lastline == "# line %num% %str%") { lineDirective(fileIndex(cback()->str().substr(1U, cback()->str().size() - 2U)), std::atol(cback()->previous->str().c_str()), &location); } // #endfile else if (lastline == "# endfile" && !loc.empty()) { location = loc.top(); loc.pop(); } } continue; } if (std::isspace(ch)) { location.col++; continue; } TokenString currentToken; if (cback() && cback()->location.line == location.line && cback()->previous && cback()->previous->op == '#' && (lastLine() == "# error" || lastLine() == "# warning")) { char prev = ' '; while (istr.good() && (prev == '\\' || (ch != '\r' && ch != '\n'))) { currentToken += ch; prev = ch; ch = readChar(istr, bom); } ungetChar(istr, bom); push_back(new Token(currentToken, location)); location.adjust(currentToken); continue; } // number or name if (isNameChar(ch)) { const bool num = std::isdigit(ch); while (istr.good() && isNameChar(ch)) { currentToken += ch; ch = readChar(istr,bom); if (num && ch=='\'' && isNameChar(peekChar(istr,bom))) ch = readChar(istr,bom); } ungetChar(istr,bom); } // comment else if (ch == '/' && peekChar(istr,bom) == '/') { while (istr.good() && ch != '\r' && ch != '\n') { currentToken += ch; ch = readChar(istr, bom); } const std::string::size_type pos = currentToken.find_last_not_of(" \t"); if (pos < currentToken.size() - 1U && currentToken[pos] == '\\') portabilityBackslash(outputList, files, location); if (currentToken[currentToken.size() - 1U] == '\\') { ++multiline; currentToken.erase(currentToken.size() - 1U); } else { ungetChar(istr, bom); } } // comment else if (ch == '/' && peekChar(istr,bom) == '*') { currentToken = "/*"; (void)readChar(istr,bom); ch = readChar(istr,bom); while (istr.good()) { currentToken += ch; if (currentToken.size() >= 4U && endsWith(currentToken, "*/")) break; ch = readChar(istr,bom); } // multiline.. std::string::size_type pos = 0; while ((pos = currentToken.find("\\\n",pos)) != std::string::npos) { currentToken.erase(pos,2); ++multiline; } if (multiline || startsWith(lastLine(10),"# ")) { pos = 0; while ((pos = currentToken.find('\n',pos)) != std::string::npos) { currentToken.erase(pos,1); ++multiline; } } } // string / char literal else if (ch == '\"' || ch == '\'') { std::string prefix; if (cback() && cback()->name && isStringLiteralPrefix(cback()->str()) && ((cback()->location.col + cback()->str().size()) == location.col) && (cback()->location.line == location.line)) { prefix = cback()->str(); } // C++11 raw string literal if (ch == '\"' && !prefix.empty() && *cback()->str().rbegin() == 'R') { std::string delim; currentToken = ch; prefix.resize(prefix.size() - 1); ch = readChar(istr,bom); while (istr.good() && ch != '(' && ch != '\n') { delim += ch; ch = readChar(istr,bom); } if (!istr.good() || ch == '\n') { if (outputList) { Output err(files); err.type = Output::SYNTAX_ERROR; err.location = location; err.msg = "Invalid newline in raw string delimiter."; outputList->push_back(err); } return; } const std::string endOfRawString(')' + delim + currentToken); while (istr.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1)) currentToken += readChar(istr,bom); if (!endsWith(currentToken, endOfRawString)) { if (outputList) { Output err(files); err.type = Output::SYNTAX_ERROR; err.location = location; err.msg = "Raw string missing terminating delimiter."; outputList->push_back(err); } return; } currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U); currentToken = escapeString(currentToken); currentToken.insert(0, prefix); back()->setstr(currentToken); location.adjust(currentToken); if (currentToken.find_first_of("\r\n") == std::string::npos) location.col += 2 + 2 * delim.size(); else location.col += 1 + delim.size(); continue; } currentToken = readUntil(istr,location,ch,ch,outputList,bom); if (currentToken.size() < 2U) // Error is reported by readUntil() return; std::string s = currentToken; std::string::size_type pos; int newlines = 0; while ((pos = s.find_first_of("\r\n")) != std::string::npos) { s.erase(pos,1); newlines++; } if (prefix.empty()) push_back(new Token(s, location)); // push string without newlines else back()->setstr(prefix + s); if (newlines > 0 && lastLine().compare(0,9,"# define ") == 0) { multiline += newlines; location.adjust(s); } else { location.adjust(currentToken); } continue; } else { currentToken += ch; } if (currentToken == "<" && lastLine() == "# include") { currentToken = readUntil(istr, location, '<', '>', outputList, bom); if (currentToken.size() < 2U) return; } push_back(new Token(currentToken, location)); if (multiline) location.col += currentToken.size(); else location.adjust(currentToken); } combineOperators(); } void simplecpp::TokenList::constFold() { while (cfront()) { // goto last '(' Token *tok = back(); while (tok && tok->op != '(') tok = tok->previous; // no '(', goto first token if (!tok) tok = front(); // Constant fold expression constFoldUnaryNotPosNeg(tok); constFoldMulDivRem(tok); constFoldAddSub(tok); constFoldShift(tok); constFoldComparison(tok); constFoldBitwise(tok); constFoldLogicalOp(tok); constFoldQuestionOp(&tok); // If there is no '(' we are done with the constant folding if (tok->op != '(') break; if (!tok->next || !tok->next->next || tok->next->next->op != ')') break; tok = tok->next; deleteToken(tok->previous); deleteToken(tok->next); } } static bool isFloatSuffix(const simplecpp::Token *tok) { if (!tok || tok->str().size() != 1U) return false; const char c = std::tolower(tok->str()[0]); return c == 'f' || c == 'l'; } void simplecpp::TokenList::combineOperators() { std::stack executableScope; executableScope.push(false); for (Token *tok = front(); tok; tok = tok->next) { if (tok->op == '{') { if (executableScope.top()) { executableScope.push(true); continue; } const Token *prev = tok->previous; while (prev && prev->isOneOf(";{}()")) prev = prev->previous; executableScope.push(prev && prev->op == ')'); continue; } if (tok->op == '}') { if (executableScope.size() > 1) executableScope.pop(); continue; } if (tok->op == '.') { // ellipsis ... if (tok->next && tok->next->op == '.' && tok->next->location.col == (tok->location.col + 1) && tok->next->next && tok->next->next->op == '.' && tok->next->next->location.col == (tok->location.col + 2)) { tok->setstr("..."); deleteToken(tok->next); deleteToken(tok->next); continue; } // float literals.. if (tok->previous && tok->previous->number) { tok->setstr(tok->previous->str() + '.'); deleteToken(tok->previous); if (isFloatSuffix(tok->next) || (tok->next && tok->next->startsWithOneOf("AaBbCcDdEeFfPp"))) { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } } if (tok->next && tok->next->number) { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } } // match: [0-9.]+E [+-] [0-9]+ const char lastChar = tok->str()[tok->str().size() - 1]; if (tok->number && !isOct(tok->str()) && ((!isHex(tok->str()) && (lastChar == 'E' || lastChar == 'e')) || (isHex(tok->str()) && (lastChar == 'P' || lastChar == 'p'))) && tok->next && tok->next->isOneOf("+-") && tok->next->next && tok->next->next->number) { tok->setstr(tok->str() + tok->next->op + tok->next->next->str()); deleteToken(tok->next); deleteToken(tok->next); } if (tok->op == '\0' || !tok->next || tok->next->op == '\0') continue; if (!sameline(tok,tok->next)) continue; if (tok->location.col + 1U != tok->next->location.col) continue; if (tok->next->op == '=' && tok->isOneOf("=!<>+-*/%&|^")) { if (tok->op == '&' && !executableScope.top()) { // don't combine &= if it is a anonymous reference parameter with default value: // void f(x&=2) int indentlevel = 0; const Token *start = tok; while (indentlevel >= 0 && start) { if (start->op == ')') ++indentlevel; else if (start->op == '(') --indentlevel; else if (start->isOneOf(";{}")) break; start = start->previous; } if (indentlevel == -1 && start) { const Token *ftok = start; bool isFuncDecl = ftok->name; while (isFuncDecl) { if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&') isFuncDecl = false; if (!start->previous) break; if (start->previous->isOneOf(";{}:")) break; start = start->previous; } isFuncDecl &= start != ftok && start->name; if (isFuncDecl) { // TODO: we could loop through the parameters here and check if they are correct. continue; } } } tok->setstr(tok->str() + "="); deleteToken(tok->next); } else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } else if (tok->op == ':' && tok->next->op == ':') { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } else if (tok->op == '-' && tok->next->op == '>') { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } else if ((tok->op == '<' || tok->op == '>') && tok->op == tok->next->op) { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); if (tok->next && tok->next->op == '=' && tok->next->next && tok->next->next->op != '=') { tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } } else if ((tok->op == '+' || tok->op == '-') && tok->op == tok->next->op) { if (tok->location.col + 1U != tok->next->location.col) continue; if (tok->previous && tok->previous->number) continue; if (tok->next->next && tok->next->next->number) continue; tok->setstr(tok->str() + tok->next->str()); deleteToken(tok->next); } } } static const std::string COMPL("compl"); static const std::string NOT("not"); void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { // "not" might be ! if (isAlternativeUnaryOp(tok, NOT)) tok->op = '!'; // "compl" might be ~ else if (isAlternativeUnaryOp(tok, COMPL)) tok->op = '~'; if (tok->op == '!' && tok->next && tok->next->number) { tok->setstr(tok->next->str() == "0" ? "1" : "0"); deleteToken(tok->next); } else if (tok->op == '~' && tok->next && tok->next->number) { tok->setstr(toString(~stringToLL(tok->next->str()))); deleteToken(tok->next); } else { if (tok->previous && (tok->previous->number || tok->previous->name)) continue; if (!tok->next || !tok->next->number) continue; switch (tok->op) { case '+': tok->setstr(tok->next->str()); deleteToken(tok->next); break; case '-': tok->setstr(tok->op + tok->next->str()); deleteToken(tok->next); break; } } } } void simplecpp::TokenList::constFoldMulDivRem(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; long long result; if (tok->op == '*') result = (stringToLL(tok->previous->str()) * stringToLL(tok->next->str())); else if (tok->op == '/' || tok->op == '%') { long long rhs = stringToLL(tok->next->str()); if (rhs == 0) throw std::overflow_error("division/modulo by zero"); long long lhs = stringToLL(tok->previous->str()); if (rhs == -1 && lhs == std::numeric_limits::min()) throw std::overflow_error("division overflow"); if (tok->op == '/') result = (lhs / rhs); else result = (lhs % rhs); } else continue; tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } void simplecpp::TokenList::constFoldAddSub(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; long long result; if (tok->op == '+') result = stringToLL(tok->previous->str()) + stringToLL(tok->next->str()); else if (tok->op == '-') result = stringToLL(tok->previous->str()) - stringToLL(tok->next->str()); else continue; tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } void simplecpp::TokenList::constFoldShift(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; long long result; if (tok->str() == "<<") result = stringToLL(tok->previous->str()) << stringToLL(tok->next->str()); else if (tok->str() == ">>") result = stringToLL(tok->previous->str()) >> stringToLL(tok->next->str()); else continue; tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } static const std::string NOTEQ("not_eq"); void simplecpp::TokenList::constFoldComparison(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (isAlternativeBinaryOp(tok,NOTEQ)) tok->setstr("!="); if (!tok->startsWithOneOf("<>=!")) continue; if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; int result; if (tok->str() == "==") result = (stringToLL(tok->previous->str()) == stringToLL(tok->next->str())); else if (tok->str() == "!=") result = (stringToLL(tok->previous->str()) != stringToLL(tok->next->str())); else if (tok->str() == ">") result = (stringToLL(tok->previous->str()) > stringToLL(tok->next->str())); else if (tok->str() == ">=") result = (stringToLL(tok->previous->str()) >= stringToLL(tok->next->str())); else if (tok->str() == "<") result = (stringToLL(tok->previous->str()) < stringToLL(tok->next->str())); else if (tok->str() == "<=") result = (stringToLL(tok->previous->str()) <= stringToLL(tok->next->str())); else continue; tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } static const std::string BITAND("bitand"); static const std::string BITOR("bitor"); static const std::string XOR("xor"); void simplecpp::TokenList::constFoldBitwise(Token *tok) { Token * const tok1 = tok; for (const char *op = "&^|"; *op; op++) { const std::string* altop; if (*op == '&') altop = &BITAND; else if (*op == '|') altop = &BITOR; else altop = &XOR; for (tok = tok1; tok && tok->op != ')'; tok = tok->next) { if (tok->op != *op && !isAlternativeBinaryOp(tok, *altop)) continue; if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; long long result; if (*op == '&') result = (stringToLL(tok->previous->str()) & stringToLL(tok->next->str())); else if (*op == '^') result = (stringToLL(tok->previous->str()) ^ stringToLL(tok->next->str())); else /*if (*op == '|')*/ result = (stringToLL(tok->previous->str()) | stringToLL(tok->next->str())); tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } } static const std::string AND("and"); static const std::string OR("or"); void simplecpp::TokenList::constFoldLogicalOp(Token *tok) { for (; tok && tok->op != ')'; tok = tok->next) { if (tok->name) { if (isAlternativeBinaryOp(tok,AND)) tok->setstr("&&"); else if (isAlternativeBinaryOp(tok,OR)) tok->setstr("||"); } if (tok->str() != "&&" && tok->str() != "||") continue; if (!tok->previous || !tok->previous->number) continue; if (!tok->next || !tok->next->number) continue; int result; if (tok->str() == "||") result = (stringToLL(tok->previous->str()) || stringToLL(tok->next->str())); else /*if (tok->str() == "&&")*/ result = (stringToLL(tok->previous->str()) && stringToLL(tok->next->str())); tok = tok->previous; tok->setstr(toString(result)); deleteToken(tok->next); deleteToken(tok->next); } } void simplecpp::TokenList::constFoldQuestionOp(Token **tok1) { bool gotoTok1 = false; for (Token *tok = *tok1; tok && tok->op != ')'; tok = gotoTok1 ? *tok1 : tok->next) { gotoTok1 = false; if (tok->str() != "?") continue; if (!tok->previous || !tok->next || !tok->next->next) throw std::runtime_error("invalid expression"); if (!tok->previous->number) continue; if (tok->next->next->op != ':') continue; Token * const condTok = tok->previous; Token * const trueTok = tok->next; Token * const falseTok = trueTok->next->next; if (!falseTok) throw std::runtime_error("invalid expression"); if (condTok == *tok1) *tok1 = (condTok->str() != "0" ? trueTok : falseTok); deleteToken(condTok->next); // ? deleteToken(trueTok->next); // : deleteToken(condTok->str() == "0" ? trueTok : falseTok); deleteToken(condTok); gotoTok1 = true; } } void simplecpp::TokenList::removeComments() { Token *tok = frontToken; while (tok) { Token *tok1 = tok; tok = tok->next; if (tok1->comment) deleteToken(tok1); } } std::string simplecpp::TokenList::readUntil(std::istream &istr, const Location &location, const char start, const char end, OutputList *outputList, unsigned int bom) { std::string ret; ret += start; bool backslash = false; char ch = 0; while (ch != end && ch != '\r' && ch != '\n' && istr.good()) { ch = readChar(istr, bom); if (backslash && ch == '\n') { ch = 0; backslash = false; continue; } backslash = false; ret += ch; if (ch == '\\') { bool update_ch = false; char next = 0; do { next = readChar(istr, bom); if (next == '\r' || next == '\n') { ret.erase(ret.size()-1U); backslash = (next == '\r'); update_ch = false; } else if (next == '\\') update_ch = !update_ch; ret += next; } while (next == '\\'); if (update_ch) ch = next; } } if (!istr.good() || ch != end) { clear(); if (outputList) { Output err(files); err.type = Output::SYNTAX_ERROR; err.location = location; err.msg = std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported."; outputList->push_back(err); } return ""; } return ret; } std::string simplecpp::TokenList::lastLine(int maxsize) const { std::string ret; int count = 0; for (const Token *tok = cback(); sameline(tok,cback()); tok = tok->previous) { if (tok->comment) continue; if (!ret.empty()) ret.insert(0, 1, ' '); ret.insert(0, tok->str()[0] == '\"' ? std::string("%str%") : tok->number ? std::string("%num%") : tok->str()); if (++count > maxsize) return ""; } return ret; } unsigned int simplecpp::TokenList::fileIndex(const std::string &filename) { for (unsigned int i = 0; i < files.size(); ++i) { if (files[i] == filename) return i; } files.push_back(filename); return files.size() - 1U; } namespace simplecpp { class Macro { public: explicit Macro(std::vector &f) : nameTokDef(NULL), variadic(false), valueToken(NULL), endToken(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(false) {} Macro(const Token *tok, std::vector &f) : nameTokDef(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(true) { if (sameline(tok->previous, tok)) throw std::runtime_error("bad macro syntax"); if (tok->op != '#') throw std::runtime_error("bad macro syntax"); const Token * const hashtok = tok; tok = tok->next; if (!tok || tok->str() != DEFINE) throw std::runtime_error("bad macro syntax"); tok = tok->next; if (!tok || !tok->name || !sameline(hashtok,tok)) throw std::runtime_error("bad macro syntax"); if (!parseDefine(tok)) throw std::runtime_error("bad macro syntax"); } Macro(const std::string &name, const std::string &value, std::vector &f) : nameTokDef(NULL), files(f), tokenListDefine(f), valueDefinedInCode_(false) { const std::string def(name + ' ' + value); std::istringstream istr(def); tokenListDefine.readfile(istr); if (!parseDefine(tokenListDefine.cfront())) throw std::runtime_error("bad macro syntax. macroname=" + name + " value=" + value); } Macro(const Macro ¯o) : nameTokDef(NULL), files(macro.files), tokenListDefine(macro.files), valueDefinedInCode_(macro.valueDefinedInCode_) { *this = macro; } void operator=(const Macro ¯o) { if (this != ¯o) { valueDefinedInCode_ = macro.valueDefinedInCode_; if (macro.tokenListDefine.empty()) parseDefine(macro.nameTokDef); else { tokenListDefine = macro.tokenListDefine; parseDefine(tokenListDefine.cfront()); } } } bool valueDefinedInCode() const { return valueDefinedInCode_; } /** * Expand macro. This will recursively expand inner macros. * @param output destination tokenlist * @param rawtok macro token * @param macros list of macros * @param inputFiles the input files * @return token after macro * @throw Can throw wrongNumberOfParameters or invalidHashHash */ const Token * expand(TokenList * const output, const Token * rawtok, const std::map ¯os, std::vector &inputFiles) const { std::set expandedmacros; TokenList output2(inputFiles); if (functionLike() && rawtok->next && rawtok->next->op == '(') { // Copy macro call to a new tokenlist with no linebreaks const Token * const rawtok1 = rawtok; TokenList rawtokens2(inputFiles); rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); rawtok = rawtok->next; rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); rawtok = rawtok->next; int par = 1; while (rawtok && par > 0) { if (rawtok->op == '(') ++par; else if (rawtok->op == ')') --par; else if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok)) throw Error(rawtok->location, "it is invalid to use a preprocessor directive as macro parameter"); rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); rawtok = rawtok->next; } if (expand(&output2, rawtok1->location, rawtokens2.cfront(), macros, expandedmacros)) rawtok = rawtok1->next; } else { rawtok = expand(&output2, rawtok->location, rawtok, macros, expandedmacros); } while (output2.cback() && rawtok) { unsigned int par = 0; Token* macro2tok = output2.back(); while (macro2tok) { if (macro2tok->op == '(') { if (par==0) break; --par; } else if (macro2tok->op == ')') ++par; macro2tok = macro2tok->previous; } if (macro2tok) { // macro2tok->op == '(' macro2tok = macro2tok->previous; expandedmacros.insert(name()); } else if (rawtok->op == '(') macro2tok = output2.back(); if (!macro2tok || !macro2tok->name) break; if (output2.cfront() != output2.cback() && macro2tok->str() == this->name()) break; const std::map::const_iterator macro = macros.find(macro2tok->str()); if (macro == macros.end() || !macro->second.functionLike()) break; TokenList rawtokens2(inputFiles); const Location loc(macro2tok->location); while (macro2tok) { Token *next = macro2tok->next; rawtokens2.push_back(new Token(macro2tok->str(), loc)); output2.deleteToken(macro2tok); macro2tok = next; } par = (rawtokens2.cfront() != rawtokens2.cback()) ? 1U : 0U; const Token *rawtok2 = rawtok; for (; rawtok2; rawtok2 = rawtok2->next) { rawtokens2.push_back(new Token(rawtok2->str(), loc)); if (rawtok2->op == '(') ++par; else if (rawtok2->op == ')') { if (par <= 1U) break; --par; } } if (!rawtok2 || par != 1U) break; if (macro->second.expand(&output2, rawtok->location, rawtokens2.cfront(), macros, expandedmacros) != NULL) break; rawtok = rawtok2->next; } output->takeTokens(output2); return rawtok; } /** macro name */ const TokenString &name() const { return nameTokDef->str(); } /** location for macro definition */ const Location &defineLocation() const { return nameTokDef->location; } /** how has this macro been used so far */ const std::list &usage() const { return usageList; } /** is this a function like macro */ bool functionLike() const { return nameTokDef->next && nameTokDef->next->op == '(' && sameline(nameTokDef, nameTokDef->next) && nameTokDef->next->location.col == nameTokDef->location.col + nameTokDef->str().size(); } /** base class for errors */ struct Error { Error(const Location &loc, const std::string &s) : location(loc), what(s) {} Location location; std::string what; }; /** Struct that is thrown when macro is expanded with wrong number of parameters */ struct wrongNumberOfParameters : public Error { wrongNumberOfParameters(const Location &loc, const std::string ¯oName) : Error(loc, "Wrong number of parameters for macro \'" + macroName + "\'.") {} }; /** Struct that is thrown when there is invalid ## usage */ struct invalidHashHash : public Error { invalidHashHash(const Location &loc, const std::string ¯oName) : Error(loc, "Invalid ## usage when expanding \'" + macroName + "\'.") {} }; private: /** Create new token where Token::macro is set for replaced tokens */ Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced) const { Token *tok = new Token(str,loc); if (replaced) tok->macro = nameTokDef->str(); return tok; } bool parseDefine(const Token *nametoken) { nameTokDef = nametoken; variadic = false; if (!nameTokDef) { valueToken = endToken = NULL; args.clear(); return false; } // function like macro.. if (functionLike()) { args.clear(); const Token *argtok = nameTokDef->next->next; while (sameline(nametoken, argtok) && argtok->op != ')') { if (argtok->str() == "..." && argtok->next && argtok->next->op == ')') { variadic = true; if (!argtok->previous->name) args.push_back("__VA_ARGS__"); argtok = argtok->next; // goto ')' break; } if (argtok->op != ',') args.push_back(argtok->str()); argtok = argtok->next; } if (!sameline(nametoken, argtok)) { endToken = argtok ? argtok->previous : argtok; valueToken = NULL; return false; } valueToken = argtok ? argtok->next : NULL; } else { args.clear(); valueToken = nameTokDef->next; } if (!sameline(valueToken, nameTokDef)) valueToken = NULL; endToken = valueToken; while (sameline(endToken, nameTokDef)) endToken = endToken->next; return true; } unsigned int getArgNum(const TokenString &str) const { unsigned int par = 0; while (par < args.size()) { if (str == args[par]) return par; par++; } return ~0U; } std::vector getMacroParameters(const Token *nameTokInst, bool calledInDefine) const { if (!nameTokInst->next || nameTokInst->next->op != '(' || !functionLike()) return std::vector(); std::vector parametertokens; parametertokens.push_back(nameTokInst->next); unsigned int par = 0U; for (const Token *tok = nameTokInst->next->next; calledInDefine ? sameline(tok, nameTokInst) : (tok != NULL); tok = tok->next) { if (tok->op == '(') ++par; else if (tok->op == ')') { if (par == 0U) { parametertokens.push_back(tok); break; } --par; } else if (par == 0U && tok->op == ',' && (!variadic || parametertokens.size() < args.size())) parametertokens.push_back(tok); } return parametertokens; } const Token *appendTokens(TokenList *tokens, const Token *lpar, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { if (!lpar || lpar->op != '(') return NULL; unsigned int par = 0; const Token *tok = lpar; while (sameline(lpar, tok)) { if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) { // A##B => AB tok = expandHashHash(tokens, tok->location, tok, macros, expandedmacros, parametertokens); } else if (tok->op == '#' && sameline(tok, tok->next) && tok->next->op != '#') { tok = expandHash(tokens, tok->location, tok, macros, expandedmacros, parametertokens); } else { if (!expandArg(tokens, tok, tok->location, macros, expandedmacros, parametertokens)) { bool expanded = false; const std::map::const_iterator it = macros.find(tok->str()); if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { const Macro &m = it->second; if (!m.functionLike()) { m.expand(tokens, tok->location, tok, macros, expandedmacros); expanded = true; } } if (!expanded) tokens->push_back(new Token(*tok)); } if (tok->op == '(') ++par; else if (tok->op == ')') { --par; if (par == 0U) break; } tok = tok->next; } } return sameline(lpar,tok) ? tok : NULL; } const Token * expand(TokenList * const output, const Location &loc, const Token * const nameTokInst, const std::map ¯os, std::set expandedmacros) const { expandedmacros.insert(nameTokInst->str()); usageList.push_back(loc); if (nameTokInst->str() == "__FILE__") { output->push_back(new Token('\"'+loc.file()+'\"', loc)); return nameTokInst->next; } if (nameTokInst->str() == "__LINE__") { output->push_back(new Token(toString(loc.line), loc)); return nameTokInst->next; } if (nameTokInst->str() == "__COUNTER__") { output->push_back(new Token(toString(usageList.size()-1U), loc)); return nameTokInst->next; } const bool calledInDefine = (loc.fileIndex != nameTokInst->location.fileIndex || loc.line < nameTokInst->location.line); std::vector parametertokens1(getMacroParameters(nameTokInst, calledInDefine)); if (functionLike()) { // No arguments => not macro expansion if (nameTokInst->next && nameTokInst->next->op != '(') { output->push_back(new Token(nameTokInst->str(), loc)); return nameTokInst->next; } // Parse macro-call if (variadic) { if (parametertokens1.size() < args.size()) { throw wrongNumberOfParameters(nameTokInst->location, name()); } } else { if (parametertokens1.size() != args.size() + (args.empty() ? 2U : 1U)) throw wrongNumberOfParameters(nameTokInst->location, name()); } } // If macro call uses __COUNTER__ then expand that first TokenList tokensparams(files); std::vector parametertokens2; if (!parametertokens1.empty()) { bool counter = false; for (const Token *tok = parametertokens1[0]; tok != parametertokens1.back(); tok = tok->next) { if (tok->str() == "__COUNTER__") { counter = true; break; } } const std::map::const_iterator m = macros.find("__COUNTER__"); if (!counter || m == macros.end()) parametertokens2.swap(parametertokens1); else { const Macro &counterMacro = m->second; unsigned int par = 0; for (const Token *tok = parametertokens1[0]; tok && par < parametertokens1.size(); tok = tok->next) { if (tok->str() == "__COUNTER__") { tokensparams.push_back(new Token(toString(counterMacro.usageList.size()), tok->location)); counterMacro.usageList.push_back(tok->location); } else { tokensparams.push_back(new Token(*tok)); if (tok == parametertokens1[par]) { parametertokens2.push_back(tokensparams.cback()); par++; } } } } } Token * const output_end_1 = output->back(); // expand for (const Token *tok = valueToken; tok != endToken;) { if (tok->op != '#') { // A##B => AB if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') { if (!sameline(tok, tok->next->next->next)) throw invalidHashHash(tok->location, name()); output->push_back(newMacroToken(expandArgStr(tok, parametertokens2), loc, isReplaced(expandedmacros))); tok = tok->next; } else { tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens2); } continue; } int numberOfHash = 1; const Token *hashToken = tok->next; while (sameline(tok,hashToken) && hashToken->op == '#') { hashToken = hashToken->next; ++numberOfHash; } if (numberOfHash == 4) { // # ## # => ## output->push_back(newMacroToken("##", loc, isReplaced(expandedmacros))); tok = hashToken; continue; } tok = tok->next; if (tok == endToken) { output->push_back(new Token(*tok->previous)); break; } if (tok->op == '#') { // A##B => AB tok = expandHashHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); } else { // #123 => "123" tok = expandHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); } } if (!functionLike()) { for (Token *tok = output_end_1 ? output_end_1->next : output->front(); tok; tok = tok->next) { tok->macro = nameTokInst->str(); } } if (!parametertokens1.empty()) parametertokens1.swap(parametertokens2); return functionLike() ? parametertokens2.back()->next : nameTokInst->next; } const Token *expandToken(TokenList *output, const Location &loc, const Token *tok, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { // Not name.. if (!tok->name) { output->push_back(newMacroToken(tok->str(), loc, true)); return tok->next; } // Macro parameter.. { TokenList temp(files); if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) { if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) { output->takeTokens(temp); return tok->next; } if (!sameline(tok, tok->next)) { output->takeTokens(temp); return tok->next; } const std::map::const_iterator it = macros.find(temp.cback()->str()); if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) { output->takeTokens(temp); return tok->next; } const Macro &calledMacro = it->second; if (!calledMacro.functionLike()) { output->takeTokens(temp); return tok->next; } TokenList temp2(files); temp2.push_back(new Token(temp.cback()->str(), tok->location)); const Token *tok2 = appendTokens(&temp2, tok->next, macros, expandedmacros, parametertokens); if (!tok2) return tok->next; output->takeTokens(temp); output->deleteToken(output->back()); calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros); return tok2->next; } } // Macro.. const std::map::const_iterator it = macros.find(tok->str()); if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { const Macro &calledMacro = it->second; if (!calledMacro.functionLike()) return calledMacro.expand(output, loc, tok, macros, expandedmacros); if (!sameline(tok, tok->next) || tok->next->op != '(') { output->push_back(newMacroToken(tok->str(), loc, true)); return tok->next; } TokenList tokens(files); tokens.push_back(new Token(*tok)); const Token *tok2 = appendTokens(&tokens, tok->next, macros, expandedmacros, parametertokens); if (!tok2) { output->push_back(newMacroToken(tok->str(), loc, true)); return tok->next; } calledMacro.expand(output, loc, tokens.cfront(), macros, expandedmacros); return tok2->next; } else if (tok->str() == DEFINED) { const Token *tok2 = tok->next; const Token *tok3 = tok2 ? tok2->next : NULL; const Token *tok4 = tok3 ? tok3->next : NULL; const Token *defToken = NULL; const Token *lastToken = NULL; if (sameline(tok, tok4) && tok2->op == '(' && tok3->name && tok4->op == ')') { defToken = tok3; lastToken = tok4; } else if (sameline(tok,tok2) && tok2->name) { defToken = lastToken = tok2; } if (defToken) { const bool def = (macros.find(defToken->str()) != macros.end()); output->push_back(newMacroToken(def ? "1" : "0", loc, true)); return lastToken->next; } } output->push_back(newMacroToken(tok->str(), loc, true)); return tok->next; } bool expandArg(TokenList *output, const Token *tok, const std::vector ¶metertokens) const { if (!tok->name) return false; const unsigned int argnr = getArgNum(tok->str()); if (argnr >= args.size()) return false; // empty variadic parameter if (variadic && argnr + 1U >= parametertokens.size()) return true; for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U]; partok = partok->next) output->push_back(new Token(*partok)); return true; } bool expandArg(TokenList *output, const Token *tok, const Location &loc, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { if (!tok->name) return false; const unsigned int argnr = getArgNum(tok->str()); if (argnr >= args.size()) return false; if (variadic && argnr + 1U >= parametertokens.size()) // empty variadic parameter return true; for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U];) { const std::map::const_iterator it = macros.find(partok->str()); if (it != macros.end() && (partok->str() == name() || expandedmacros.find(partok->str()) == expandedmacros.end())) partok = it->second.expand(output, loc, partok, macros, expandedmacros); else { output->push_back(newMacroToken(partok->str(), loc, isReplaced(expandedmacros))); partok = partok->next; } } return true; } /** * Get string for token. If token is argument, the expanded string is returned. * @param tok The token * @param parametertokens parameters given when expanding this macro * @return string */ std::string expandArgStr(const Token *tok, const std::vector ¶metertokens) const { TokenList tokens(files); if (expandArg(&tokens, tok, parametertokens)) { std::string s; for (const Token *tok2 = tokens.cfront(); tok2; tok2 = tok2->next) s += tok2->str(); return s; } return tok->str(); } /** * Expand #X => "X" * @param output destination tokenlist * @param loc location for expanded token * @param tok The # token * @param macros all macros * @param expandedmacros set with expanded macros, with this macro * @param parametertokens parameters given when expanding this macro * @return token after the X */ const Token *expandHash(TokenList *output, const Location &loc, const Token *tok, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { TokenList tokenListHash(files); tok = expandToken(&tokenListHash, loc, tok->next, macros, expandedmacros, parametertokens); std::ostringstream ostr; ostr << '\"'; for (const Token *hashtok = tokenListHash.cfront(); hashtok; hashtok = hashtok->next) ostr << hashtok->str(); ostr << '\"'; output->push_back(newMacroToken(escapeString(ostr.str()), loc, isReplaced(expandedmacros))); return tok; } /** * Expand A##B => AB * The A should already be expanded. Call this when you reach the first # token * @param output destination tokenlist * @param loc location for expanded token * @param tok first # token * @param macros all macros * @param expandedmacros set with expanded macros, with this macro * @param parametertokens parameters given when expanding this macro * @return token after B */ const Token *expandHashHash(TokenList *output, const Location &loc, const Token *tok, const std::map ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { Token *A = output->back(); if (!A) throw invalidHashHash(tok->location, name()); if (!sameline(tok, tok->next) || !sameline(tok, tok->next->next)) throw invalidHashHash(tok->location, name()); bool canBeConcatenatedWithEqual = A->isOneOf("+-*/%&|^") || A->str() == "<<" || A->str() == ">>"; if (!A->name && !A->number && A->op != ',' && !A->str().empty() && !canBeConcatenatedWithEqual) throw invalidHashHash(tok->location, name()); Token *B = tok->next->next; if (!B->name && !B->number && B->op && !B->isOneOf("#=")) throw invalidHashHash(tok->location, name()); if ((canBeConcatenatedWithEqual && B->op != '=') || (!canBeConcatenatedWithEqual && B->op == '=')) throw invalidHashHash(tok->location, name()); std::string strAB; const bool varargs = variadic && args.size() >= 1U && B->str() == args[args.size()-1U]; TokenList tokensB(files); if (expandArg(&tokensB, B, parametertokens)) { if (tokensB.empty()) strAB = A->str(); else if (varargs && A->op == ',') { strAB = ","; } else { strAB = A->str() + tokensB.cfront()->str(); tokensB.deleteToken(tokensB.front()); } } else { strAB = A->str() + B->str(); } const Token *nextTok = B->next; if (varargs && tokensB.empty() && tok->previous->str() == ",") output->deleteToken(A); else if (strAB != "," && macros.find(strAB) == macros.end()) { A->setstr(strAB); for (Token *b = tokensB.front(); b; b = b->next) b->location = loc; output->takeTokens(tokensB); } else { output->deleteToken(A); TokenList tokens(files); tokens.push_back(new Token(strAB, tok->location)); // for function like macros, push the (...) if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') { const std::map::const_iterator it = macros.find(strAB); if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) { const Token *tok2 = appendTokens(&tokens, B->next, macros, expandedmacros, parametertokens); if (tok2) nextTok = tok2->next; } } expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens); for (Token *b = tokensB.front(); b; b = b->next) b->location = loc; output->takeTokens(tokensB); } return nextTok; } static bool isReplaced(const std::set &expandedmacros) { // return true if size > 1 std::set::const_iterator it = expandedmacros.begin(); if (it == expandedmacros.end()) return false; ++it; return (it != expandedmacros.end()); } /** name token in definition */ const Token *nameTokDef; /** arguments for macro */ std::vector args; /** is macro variadic? */ bool variadic; /** first token in replacement string */ const Token *valueToken; /** token after replacement string */ const Token *endToken; /** files */ std::vector &files; /** this is used for -D where the definition is not seen anywhere in code */ TokenList tokenListDefine; /** usage of this macro */ mutable std::list usageList; /** was the value of this macro actually defined in the code? */ bool valueDefinedInCode_; }; } namespace simplecpp { std::string convertCygwinToWindowsPath(const std::string &cygwinPath) { std::string windowsPath; std::string::size_type pos = 0; if (cygwinPath.size() >= 11 && startsWith(cygwinPath, "/cygdrive/")) { unsigned char driveLetter = cygwinPath[10]; if (std::isalpha(driveLetter)) { if (cygwinPath.size() == 11) { windowsPath = toupper(driveLetter); windowsPath += ":\\"; // volume root directory pos = 11; } else if (cygwinPath[11] == '/') { windowsPath = toupper(driveLetter); windowsPath += ":"; pos = 11; } } } for (; pos < cygwinPath.size(); ++pos) { unsigned char c = cygwinPath[pos]; if (c == '/') c = '\\'; windowsPath += c; } return windowsPath; } } #ifdef SIMPLECPP_WINDOWS class ScopedLock { public: explicit ScopedLock(CRITICAL_SECTION& criticalSection) : m_criticalSection(criticalSection) { EnterCriticalSection(&m_criticalSection); } ~ScopedLock() { LeaveCriticalSection(&m_criticalSection); } private: ScopedLock& operator=(const ScopedLock&); ScopedLock(const ScopedLock&); CRITICAL_SECTION& m_criticalSection; }; class RealFileNameMap { public: RealFileNameMap() { InitializeCriticalSection(&m_criticalSection); } ~RealFileNameMap() { DeleteCriticalSection(&m_criticalSection); } bool getCacheEntry(const std::string& path, std::string* returnPath) { ScopedLock lock(m_criticalSection); std::map::iterator it = m_fileMap.find(path); if (it != m_fileMap.end()) { *returnPath = it->second; return true; } return false; } void addToCache(const std::string& path, const std::string& actualPath) { ScopedLock lock(m_criticalSection); m_fileMap[path] = actualPath; } private: std::map m_fileMap; CRITICAL_SECTION m_criticalSection; }; static RealFileNameMap realFileNameMap; static bool realFileName(const std::string &f, std::string *result) { // are there alpha characters in last subpath? bool alpha = false; for (std::string::size_type pos = 1; pos <= f.size(); ++pos) { unsigned char c = f[f.size() - pos]; if (c == '/' || c == '\\') break; if (std::isalpha(c)) { alpha = true; break; } } // do not convert this path if there are no alpha characters (either pointless or cause wrong results for . and ..) if (!alpha) return false; // Lookup filename or foldername on file system if (!realFileNameMap.getCacheEntry(f, result)) { WIN32_FIND_DATAA FindFileData; #ifdef __CYGWIN__ std::string fConverted = simplecpp::convertCygwinToWindowsPath(f); HANDLE hFind = FindFirstFileExA(fConverted.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); #else HANDLE hFind = FindFirstFileExA(f.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); #endif if (INVALID_HANDLE_VALUE == hFind) return false; *result = FindFileData.cFileName; realFileNameMap.addToCache(f, *result); FindClose(hFind); } return true; } static RealFileNameMap realFilePathMap; /** Change case in given path to match filesystem */ static std::string realFilename(const std::string &f) { std::string ret; ret.reserve(f.size()); // this will be the final size if (realFilePathMap.getCacheEntry(f, &ret)) return ret; // Current subpath std::string subpath; for (std::string::size_type pos = 0; pos < f.size(); ++pos) { unsigned char c = f[pos]; // Separator.. add subpath and separator if (c == '/' || c == '\\') { // if subpath is empty just add separator if (subpath.empty()) { ret += c; continue; } bool isDriveSpecification = (pos == 2 && subpath.size() == 2 && std::isalpha(subpath[0]) && subpath[1] == ':'); // Append real filename (proper case) std::string f2; if (!isDriveSpecification && realFileName(f.substr(0, pos), &f2)) ret += f2; else ret += subpath; subpath.clear(); // Append separator ret += c; } else { subpath += c; } } if (!subpath.empty()) { std::string f2; if (realFileName(f,&f2)) ret += f2; else ret += subpath; } realFilePathMap.addToCache(f, ret); return ret; } static bool isAbsolutePath(const std::string &path) { if (path.length() >= 3 && path[0] > 0 && std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) return true; return path.length() > 1U && (path[0] == '/' || path[0] == '\\'); } #else #define realFilename(f) f static bool isAbsolutePath(const std::string &path) { return path.length() > 1U && path[0] == '/'; } #endif namespace simplecpp { /** * perform path simplifications for . and .. */ std::string simplifyPath(std::string path) { if (path.empty()) return path; std::string::size_type pos; // replace backslash separators std::replace(path.begin(), path.end(), '\\', '/'); const bool unc(path.compare(0,2,"//") == 0); // replace "//" with "/" pos = 0; while ((pos = path.find("//",pos)) != std::string::npos) { path.erase(pos,1); } // remove "./" pos = 0; while ((pos = path.find("./",pos)) != std::string::npos) { if (pos == 0 || path[pos - 1U] == '/') path.erase(pos,2); else pos += 2; } // remove trailing dot if path ends with "/." if (endsWith(path,"/.")) path.erase(path.size()-1); // simplify ".." pos = 1; // don't simplify ".." if path starts with that while ((pos = path.find("/..", pos)) != std::string::npos) { // not end of path, then string must be "/../" if (pos + 3 < path.size() && path[pos + 3] != '/') { ++pos; continue; } // get previous subpath const std::string::size_type pos1 = path.rfind('/', pos - 1U) + 1U; const std::string previousSubPath = path.substr(pos1, pos-pos1); if (previousSubPath == "..") { // don't simplify ++pos; } else { // remove previous subpath and ".." path.erase(pos1,pos-pos1+4); if (path.empty()) path = "."; // update pos pos = (pos1 == 0) ? 1 : (pos1 - 1); } } // Remove trailing '/'? //if (path.size() > 1 && endsWith(path, "/")) // path.erase(path.size()-1); if (unc) path = '/' + path; return path.find_first_of("*?") == std::string::npos ? realFilename(path) : path; } } /** Evaluate sizeof(type) */ static void simplifySizeof(simplecpp::TokenList &expr, const std::map &sizeOfType) { for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { if (tok->str() != "sizeof") continue; simplecpp::Token *tok1 = tok->next; if (!tok1) { throw std::runtime_error("missing sizeof argument"); } simplecpp::Token *tok2 = tok1->next; if (!tok2) { throw std::runtime_error("missing sizeof argument"); } if (tok1->op == '(') { tok1 = tok1->next; while (tok2->op != ')') { tok2 = tok2->next; if (!tok2) { throw std::runtime_error("invalid sizeof expression"); } } } std::string type; for (simplecpp::Token *typeToken = tok1; typeToken != tok2; typeToken = typeToken->next) { if ((typeToken->str() == "unsigned" || typeToken->str() == "signed") && typeToken->next->name) continue; if (typeToken->str() == "*" && type.find('*') != std::string::npos) continue; if (!type.empty()) type += ' '; type += typeToken->str(); } const std::map::const_iterator it = sizeOfType.find(type); if (it != sizeOfType.end()) tok->setstr(toString(it->second)); else continue; tok2 = tok2->next; while (tok->next != tok2) expr.deleteToken(tok->next); } } static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"}; static const std::set altop(&altopData[0], &altopData[8]); static void simplifyName(simplecpp::TokenList &expr) { for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { if (tok->name) { if (altop.find(tok->str()) != altop.end()) { bool alt; if (tok->str() == "not" || tok->str() == "compl") { alt = isAlternativeUnaryOp(tok,tok->str()); } else { alt = isAlternativeBinaryOp(tok,tok->str()); } if (alt) continue; } tok->setstr("0"); } } } static void simplifyNumbers(simplecpp::TokenList &expr) { for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { if (tok->str().size() == 1U) continue; if (tok->str().compare(0,2,"0x") == 0) tok->setstr(toString(stringToULL(tok->str()))); else if (tok->str()[0] == '\'') tok->setstr(toString(tok->str()[1] & 0xffU)); } } static long long evaluate(simplecpp::TokenList &expr, const std::map &sizeOfType) { simplifySizeof(expr, sizeOfType); simplifyName(expr); simplifyNumbers(expr); expr.constFold(); // TODO: handle invalid expressions return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL; } static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok) { const unsigned int line = tok->location.line; const unsigned int file = tok->location.fileIndex; while (tok && tok->location.line == line && tok->location.fileIndex == file) tok = tok->next; return tok; } #ifdef SIMPLECPP_WINDOWS class NonExistingFilesCache { public: NonExistingFilesCache() { InitializeCriticalSection(&m_criticalSection); } ~NonExistingFilesCache() { DeleteCriticalSection(&m_criticalSection); } bool contains(const std::string& path) { ScopedLock lock(m_criticalSection); return (m_pathSet.find(path) != m_pathSet.end()); } void add(const std::string& path) { ScopedLock lock(m_criticalSection); m_pathSet.insert(path); } private: std::set m_pathSet; CRITICAL_SECTION m_criticalSection; }; static NonExistingFilesCache nonExistingFilesCache; #endif static std::string _openHeader(std::ifstream &f, const std::string &path) { #ifdef SIMPLECPP_WINDOWS std::string simplePath = simplecpp::simplifyPath(path); if (nonExistingFilesCache.contains(simplePath)) return ""; // file is known not to exist, skip expensive file open call f.open(simplePath.c_str()); if (f.is_open()) return simplePath; else { nonExistingFilesCache.add(simplePath); return ""; } #else f.open(path.c_str()); return f.is_open() ? simplecpp::simplifyPath(path) : ""; #endif } static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader) { if (isAbsolutePath(header)) { return _openHeader(f, header); } if (!systemheader) { if (sourcefile.find_first_of("\\/") != std::string::npos) { const std::string s = sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header; std::string simplePath = _openHeader(f, s); if (!simplePath.empty()) return simplePath; } else { std::string simplePath = _openHeader(f, header); if (!simplePath.empty()) return simplePath; } } for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { std::string s = *it; if (!s.empty() && s[s.size()-1U]!='/' && s[s.size()-1U]!='\\') s += '/'; s += header; std::string simplePath = _openHeader(f, s); if (!simplePath.empty()) return simplePath; } return ""; } static std::string getFileName(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) { if (filedata.empty()) { return ""; } if (isAbsolutePath(header)) { return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : ""; } if (!systemheader) { if (sourcefile.find_first_of("\\/") != std::string::npos) { const std::string s(simplecpp::simplifyPath(sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header)); if (filedata.find(s) != filedata.end()) return s; } else { std::string s = simplecpp::simplifyPath(header); if (filedata.find(s) != filedata.end()) return s; } } for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { std::string s = *it; if (!s.empty() && s[s.size()-1U]!='/' && s[s.size()-1U]!='\\') s += '/'; s += header; s = simplecpp::simplifyPath(s); if (filedata.find(s) != filedata.end()) return s; } return ""; } static bool hasFile(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) { return !getFileName(filedata, sourcefile, header, dui, systemheader).empty(); } std::map simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector &fileNumbers, const simplecpp::DUI &dui, simplecpp::OutputList *outputList) { std::map ret; std::list filelist; // -include files for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { const std::string &filename = realFilename(*it); if (ret.find(filename) != ret.end()) continue; std::ifstream fin(filename.c_str()); if (!fin.is_open()) { if (outputList) { simplecpp::Output err(fileNumbers); err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND; err.location = Location(fileNumbers); err.msg = "Can not open include file '" + filename + "' that is explicitly included."; outputList->push_back(err); } continue; } TokenList *tokenlist = new TokenList(fin, fileNumbers, filename, outputList); if (!tokenlist->front()) { delete tokenlist; continue; } ret[filename] = tokenlist; filelist.push_back(tokenlist->front()); } for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : NULL) { if (rawtok == NULL) { rawtok = filelist.back(); filelist.pop_back(); } if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok)) continue; rawtok = rawtok->nextSkipComments(); if (!rawtok || rawtok->str() != INCLUDE) continue; const std::string &sourcefile = rawtok->location.file(); const Token *htok = rawtok->nextSkipComments(); if (!sameline(rawtok, htok)) continue; bool systemheader = (htok->str()[0] == '<'); const std::string header(realFilename(htok->str().substr(1U, htok->str().size() - 2U))); if (hasFile(ret, sourcefile, header, dui, systemheader)) continue; std::ifstream f; const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); if (!f.is_open()) continue; TokenList *tokens = new TokenList(f, fileNumbers, header2, outputList); ret[header2] = tokens; if (tokens->front()) filelist.push_back(tokens->front()); } return ret; } static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token **tok1, std::map ¯os, std::vector &files, simplecpp::OutputList *outputList) { const simplecpp::Token *tok = *tok1; const std::map::const_iterator it = macros.find(tok->str()); if (it != macros.end()) { simplecpp::TokenList value(files); try { *tok1 = it->second.expand(&value, tok, macros, files); } catch (simplecpp::Macro::Error &err) { if (outputList) { simplecpp::Output out(files); out.type = simplecpp::Output::SYNTAX_ERROR; out.location = err.location; out.msg = "failed to expand \'" + tok->str() + "\', " + err.what; outputList->push_back(out); } return false; } output.takeTokens(value); } else { if (!tok->comment) output.push_back(new simplecpp::Token(*tok)); *tok1 = tok->next; } return true; } void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector &files, std::map &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list *macroUsage) { std::map sizeOfType(rawtokens.sizeOfType); sizeOfType.insert(std::make_pair("char", sizeof(char))); sizeOfType.insert(std::make_pair("short", sizeof(short))); sizeOfType.insert(std::make_pair("short int", sizeOfType["short"])); sizeOfType.insert(std::make_pair("int", sizeof(int))); sizeOfType.insert(std::make_pair("long", sizeof(long))); sizeOfType.insert(std::make_pair("long int", sizeOfType["long"])); sizeOfType.insert(std::make_pair("long long", sizeof(long long))); sizeOfType.insert(std::make_pair("float", sizeof(float))); sizeOfType.insert(std::make_pair("double", sizeof(double))); sizeOfType.insert(std::make_pair("long double", sizeof(long double))); sizeOfType.insert(std::make_pair("char *", sizeof(char *))); sizeOfType.insert(std::make_pair("short *", sizeof(short *))); sizeOfType.insert(std::make_pair("short int *", sizeOfType["short *"])); sizeOfType.insert(std::make_pair("int *", sizeof(int *))); sizeOfType.insert(std::make_pair("long *", sizeof(long *))); sizeOfType.insert(std::make_pair("long int *", sizeOfType["long *"])); sizeOfType.insert(std::make_pair("long long *", sizeof(long long *))); sizeOfType.insert(std::make_pair("float *", sizeof(float *))); sizeOfType.insert(std::make_pair("double *", sizeof(double *))); sizeOfType.insert(std::make_pair("long double *", sizeof(long double *))); std::map macros; for (std::list::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) { const std::string ¯ostr = *it; const std::string::size_type eq = macrostr.find('='); const std::string::size_type par = macrostr.find('('); const std::string macroname = macrostr.substr(0, std::min(eq,par)); if (dui.undefined.find(macroname) != dui.undefined.end()) continue; const std::string lhs(macrostr.substr(0,eq)); const std::string rhs(eq==std::string::npos ? std::string("1") : macrostr.substr(eq+1)); const Macro macro(lhs, rhs, files); macros.insert(std::pair(macro.name(), macro)); } macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", files))); macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", files))); macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", files))); // TRUE => code in current #if block should be kept // ELSE_IS_TRUE => code in current #if block should be dropped. the code in the #else should be kept. // ALWAYS_FALSE => drop all code in #if and #else enum IfState { TRUE, ELSE_IS_TRUE, ALWAYS_FALSE }; std::stack ifstates; ifstates.push(TRUE); std::stack includetokenstack; std::set pragmaOnce; includetokenstack.push(rawtokens.cfront()); for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { const std::map::const_iterator f = filedata.find(*it); if (f != filedata.end()) includetokenstack.push(f->second->cfront()); } for (const Token *rawtok = NULL; rawtok || !includetokenstack.empty();) { if (rawtok == NULL) { rawtok = includetokenstack.top(); includetokenstack.pop(); continue; } if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok)) { if (!sameline(rawtok, rawtok->next)) { rawtok = rawtok->next; continue; } rawtok = rawtok->next; if (!rawtok->name) { rawtok = gotoNextLine(rawtok); continue; } if (ifstates.size() <= 1U && (rawtok->str() == ELIF || rawtok->str() == ELSE || rawtok->str() == ENDIF)) { if (outputList) { simplecpp::Output err(files); err.type = Output::SYNTAX_ERROR; err.location = rawtok->location; err.msg = "#" + rawtok->str() + " without #if"; outputList->push_back(err); } output.clear(); return; } if (ifstates.top() == TRUE && (rawtok->str() == ERROR || rawtok->str() == WARNING)) { if (outputList) { simplecpp::Output err(rawtok->location.files); err.type = rawtok->str() == ERROR ? Output::ERROR : Output::WARNING; err.location = rawtok->location; for (const Token *tok = rawtok->next; tok && sameline(rawtok,tok); tok = tok->next) { if (!err.msg.empty() && isNameChar(tok->str()[0])) err.msg += ' '; err.msg += tok->str(); } err.msg = '#' + rawtok->str() + ' ' + err.msg; outputList->push_back(err); } if (rawtok->str() == ERROR) { output.clear(); return; } } if (rawtok->str() == DEFINE) { if (ifstates.top() != TRUE) continue; try { const Macro ¯o = Macro(rawtok->previous, files); if (dui.undefined.find(macro.name()) == dui.undefined.end()) { std::map::iterator it = macros.find(macro.name()); if (it == macros.end()) macros.insert(std::pair(macro.name(), macro)); else it->second = macro; } } catch (const std::runtime_error &) { if (outputList) { simplecpp::Output err(files); err.type = Output::SYNTAX_ERROR; err.location = rawtok->location; err.msg = "Failed to parse #define"; outputList->push_back(err); } output.clear(); return; } } else if (ifstates.top() == TRUE && rawtok->str() == INCLUDE) { TokenList inc1(files); for (const Token *inctok = rawtok->next; sameline(rawtok,inctok); inctok = inctok->next) { if (!inctok->comment) inc1.push_back(new Token(*inctok)); } TokenList inc2(files); if (!inc1.empty() && inc1.cfront()->name) { const Token *inctok = inc1.cfront(); if (!preprocessToken(inc2, &inctok, macros, files, outputList)) { output.clear(); return; } } else { inc2.takeTokens(inc1); } if (!inc2.empty() && inc2.cfront()->op == '<' && inc2.cback()->op == '>') { TokenString hdr; // TODO: Sometimes spaces must be added in the string // Somehow preprocessToken etc must be told that the location should be source location not destination location for (const Token *tok = inc2.cfront(); tok; tok = tok->next) { hdr += tok->str(); } inc2.clear(); inc2.push_back(new Token(hdr, inc1.cfront()->location)); inc2.front()->op = '<'; } if (inc2.empty() || inc2.cfront()->str().size() <= 2U) { if (outputList) { simplecpp::Output err(files); err.type = Output::SYNTAX_ERROR; err.location = rawtok->location; err.msg = "No header in #include"; outputList->push_back(err); } output.clear(); return; } const Token *inctok = inc2.cfront(); const bool systemheader = (inctok->op == '<'); const std::string header(realFilename(inctok->str().substr(1U, inctok->str().size() - 2U))); std::string header2 = getFileName(filedata, rawtok->location.file(), header, dui, systemheader); if (header2.empty()) { // try to load file.. std::ifstream f; header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader); if (f.is_open()) { TokenList *tokens = new TokenList(f, files, header2, outputList); filedata[header2] = tokens; } } if (header2.empty()) { if (outputList) { simplecpp::Output out(files); out.type = Output::MISSING_HEADER; out.location = rawtok->location; out.msg = "Header not found: " + inctok->str(); outputList->push_back(out); } } else if (includetokenstack.size() >= 400) { if (outputList) { simplecpp::Output out(files); out.type = Output::INCLUDE_NESTED_TOO_DEEPLY; out.location = rawtok->location; out.msg = "#include nested too deeply"; outputList->push_back(out); } } else if (pragmaOnce.find(header2) == pragmaOnce.end()) { includetokenstack.push(gotoNextLine(rawtok)); const TokenList *includetokens = filedata.find(header2)->second; rawtok = includetokens ? includetokens->cfront() : NULL; continue; } } else if (rawtok->str() == IF || rawtok->str() == IFDEF || rawtok->str() == IFNDEF || rawtok->str() == ELIF) { if (!sameline(rawtok,rawtok->next)) { if (outputList) { simplecpp::Output out(files); out.type = Output::SYNTAX_ERROR; out.location = rawtok->location; out.msg = "Syntax error in #" + rawtok->str(); outputList->push_back(out); } output.clear(); return; } bool conditionIsTrue; if (ifstates.top() == ALWAYS_FALSE || (ifstates.top() == ELSE_IS_TRUE && rawtok->str() != ELIF)) conditionIsTrue = false; else if (rawtok->str() == IFDEF) conditionIsTrue = (macros.find(rawtok->next->str()) != macros.end()); else if (rawtok->str() == IFNDEF) conditionIsTrue = (macros.find(rawtok->next->str()) == macros.end()); else { /*if (rawtok->str() == IF || rawtok->str() == ELIF)*/ TokenList expr(files); for (const Token *tok = rawtok->next; tok && tok->location.sameline(rawtok->location); tok = tok->next) { if (!tok->name) { expr.push_back(new Token(*tok)); continue; } if (tok->str() == DEFINED) { tok = tok->next; const bool par = (tok && tok->op == '('); if (par) tok = tok->next; if (tok) { if (macros.find(tok->str()) != macros.end()) expr.push_back(new Token("1", tok->location)); else expr.push_back(new Token("0", tok->location)); } if (par) tok = tok ? tok->next : NULL; if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) { if (outputList) { Output out(rawtok->location.files); out.type = Output::SYNTAX_ERROR; out.location = rawtok->location; out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; outputList->push_back(out); } output.clear(); return; } continue; } const Token *tmp = tok; if (!preprocessToken(expr, &tmp, macros, files, outputList)) { output.clear(); return; } if (!tmp) break; tok = tmp->previous; } try { conditionIsTrue = (evaluate(expr, sizeOfType) != 0); } catch (const std::exception &e) { if (outputList) { Output out(rawtok->location.files); out.type = Output::SYNTAX_ERROR; out.location = rawtok->location; out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; if (e.what() && *e.what()) out.msg += std::string(", ") + e.what(); outputList->push_back(out); } output.clear(); return; } } if (rawtok->str() != ELIF) { // push a new ifstate.. if (ifstates.top() != TRUE) ifstates.push(ALWAYS_FALSE); else ifstates.push(conditionIsTrue ? TRUE : ELSE_IS_TRUE); } else if (ifstates.top() == TRUE) { ifstates.top() = ALWAYS_FALSE; } else if (ifstates.top() == ELSE_IS_TRUE && conditionIsTrue) { ifstates.top() = TRUE; } } else if (rawtok->str() == ELSE) { ifstates.top() = (ifstates.top() == ELSE_IS_TRUE) ? TRUE : ALWAYS_FALSE; } else if (rawtok->str() == ENDIF) { ifstates.pop(); } else if (rawtok->str() == UNDEF) { if (ifstates.top() == TRUE) { const Token *tok = rawtok->next; while (sameline(rawtok,tok) && tok->comment) tok = tok->next; if (sameline(rawtok, tok)) macros.erase(tok->str()); } } else if (ifstates.top() == TRUE && rawtok->str() == PRAGMA && rawtok->next && rawtok->next->str() == ONCE && sameline(rawtok,rawtok->next)) { pragmaOnce.insert(rawtok->location.file()); } rawtok = gotoNextLine(rawtok); continue; } if (ifstates.top() != TRUE) { // drop code rawtok = gotoNextLine(rawtok); continue; } bool hash=false, hashhash=false; if (rawtok->op == '#' && sameline(rawtok,rawtok->next)) { if (rawtok->next->op != '#') { hash = true; rawtok = rawtok->next; // skip '#' } else if (sameline(rawtok,rawtok->next->next)) { hashhash = true; rawtok = rawtok->next->next; // skip '#' '#' } } const Location loc(rawtok->location); TokenList tokens(files); if (!preprocessToken(tokens, &rawtok, macros, files, outputList)) { output.clear(); return; } if (hash || hashhash) { std::string s; for (const Token *hashtok = tokens.cfront(); hashtok; hashtok = hashtok->next) s += hashtok->str(); if (hash) output.push_back(new Token('\"' + s + '\"', loc)); else if (output.back()) output.back()->setstr(output.cback()->str() + s); else output.push_back(new Token(s, loc)); } else { output.takeTokens(tokens); } } if (macroUsage) { for (std::map::const_iterator macroIt = macros.begin(); macroIt != macros.end(); ++macroIt) { const Macro ¯o = macroIt->second; const std::list &usage = macro.usage(); for (std::list::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) { MacroUsage mu(usageIt->files, macro.valueDefinedInCode()); mu.macroName = macro.name(); mu.macroLocation = macro.defineLocation(); mu.useLocation = *usageIt; macroUsage->push_back(mu); } } } } void simplecpp::cleanup(std::map &filedata) { for (std::map::iterator it = filedata.begin(); it != filedata.end(); ++it) delete it->second; filedata.clear(); } cppcheck-1.90/externals/simplecpp/simplecpp.h000066400000000000000000000241531357737443600214210ustar00rootroot00000000000000/* * simplecpp - A simple and high-fidelity C/C++ preprocessor library * Copyright (C) 2016 Daniel Marjamäki. * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef simplecppH #define simplecppH #include #include #include #include #include #include #include #include #ifdef _WIN32 # ifdef SIMPLECPP_EXPORT # define SIMPLECPP_LIB __declspec(dllexport) # elif defined(SIMPLECPP_IMPORT) # define SIMPLECPP_LIB __declspec(dllimport) # else # define SIMPLECPP_LIB # endif #else # define SIMPLECPP_LIB #endif namespace simplecpp { typedef std::string TokenString; /** * Location in source code */ class SIMPLECPP_LIB Location { public: explicit Location(const std::vector &f) : files(f), fileIndex(0), line(1U), col(0U) {} Location(const Location &loc) : files(loc.files), fileIndex(loc.fileIndex), line(loc.line), col(loc.col) {} Location &operator=(const Location &other) { if (this != &other) { fileIndex = other.fileIndex; line = other.line; col = other.col; } return *this; } /** increment this location by string */ void adjust(const std::string &str); bool operator<(const Location &rhs) const { if (fileIndex != rhs.fileIndex) return fileIndex < rhs.fileIndex; if (line != rhs.line) return line < rhs.line; return col < rhs.col; } bool sameline(const Location &other) const { return fileIndex == other.fileIndex && line == other.line; } const std::string& file() const { return fileIndex < files.size() ? files[fileIndex] : emptyFileName; } const std::vector &files; unsigned int fileIndex; unsigned int line; unsigned int col; private: static const std::string emptyFileName; }; /** * token class. * @todo don't use std::string representation - for both memory and performance reasons */ class SIMPLECPP_LIB Token { public: Token(const TokenString &s, const Location &loc) : location(loc), previous(NULL), next(NULL), string(s) { flags(); } Token(const Token &tok) : macro(tok.macro), location(tok.location), previous(NULL), next(NULL), string(tok.string) { flags(); } void flags() { name = (std::isalpha((unsigned char)string[0]) || string[0] == '_' || string[0] == '$'); comment = string.size() > 1U && string[0] == '/' && (string[1] == '/' || string[1] == '*'); number = std::isdigit((unsigned char)string[0]) || (string.size() > 1U && string[0] == '-' && std::isdigit((unsigned char)string[1])); op = (string.size() == 1U) ? string[0] : '\0'; } const TokenString& str() const { return string; } void setstr(const std::string &s) { string = s; flags(); } bool isOneOf(const char ops[]) const; bool startsWithOneOf(const char c[]) const; bool endsWithOneOf(const char c[]) const; TokenString macro; char op; bool comment; bool name; bool number; Location location; Token *previous; Token *next; const Token *previousSkipComments() const { const Token *tok = this->previous; while (tok && tok->comment) tok = tok->previous; return tok; } const Token *nextSkipComments() const { const Token *tok = this->next; while (tok && tok->comment) tok = tok->next; return tok; } void printAll() const; void printOut() const; private: TokenString string; // Not implemented - prevent assignment Token &operator=(const Token &tok); }; /** Output from preprocessor */ struct SIMPLECPP_LIB Output { explicit Output(const std::vector &files) : type(ERROR), location(files) {} enum Type { ERROR, /* #error */ WARNING, /* #warning */ MISSING_HEADER, INCLUDE_NESTED_TOO_DEEPLY, SYNTAX_ERROR, PORTABILITY_BACKSLASH, UNHANDLED_CHAR_ERROR, EXPLICIT_INCLUDE_NOT_FOUND } type; Location location; std::string msg; }; typedef std::list OutputList; /** List of tokens. */ class SIMPLECPP_LIB TokenList { public: explicit TokenList(std::vector &filenames); TokenList(std::istream &istr, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = NULL); TokenList(const TokenList &other); #if __cplusplus >= 201103L TokenList(TokenList &&other); #endif ~TokenList(); TokenList &operator=(const TokenList &other); #if __cplusplus >= 201103L TokenList &operator=(TokenList &&other); #endif void clear(); bool empty() const { return !frontToken; } void push_back(Token *tok); void dump() const; std::string stringify() const; void readfile(std::istream &istr, const std::string &filename=std::string(), OutputList *outputList = NULL); void constFold(); void removeComments(); Token *front() { return frontToken; } const Token *cfront() const { return frontToken; } Token *back() { return backToken; } const Token *cback() const { return backToken; } void deleteToken(Token *tok) { if (!tok) return; Token *prev = tok->previous; Token *next = tok->next; if (prev) prev->next = next; if (next) next->previous = prev; if (frontToken == tok) frontToken = next; if (backToken == tok) backToken = prev; delete tok; } void takeTokens(TokenList &other) { if (!other.frontToken) return; if (!frontToken) { frontToken = other.frontToken; } else { backToken->next = other.frontToken; other.frontToken->previous = backToken; } backToken = other.backToken; other.frontToken = other.backToken = NULL; } /** sizeof(T) */ std::map sizeOfType; private: void combineOperators(); void constFoldUnaryNotPosNeg(Token *tok); void constFoldMulDivRem(Token *tok); void constFoldAddSub(Token *tok); void constFoldShift(Token *tok); void constFoldComparison(Token *tok); void constFoldBitwise(Token *tok); void constFoldLogicalOp(Token *tok); void constFoldQuestionOp(Token **tok1); std::string readUntil(std::istream &istr, const Location &location, char start, char end, OutputList *outputList, unsigned int bom); void lineDirective(unsigned int fileIndex, unsigned int line, Location *location); std::string lastLine(int maxsize=100000) const; unsigned int fileIndex(const std::string &filename); Token *frontToken; Token *backToken; std::vector &files; }; /** Tracking how macros are used */ struct SIMPLECPP_LIB MacroUsage { explicit MacroUsage(const std::vector &f, bool macroValueKnown_) : macroLocation(f), useLocation(f), macroValueKnown(macroValueKnown_) {} std::string macroName; Location macroLocation; Location useLocation; bool macroValueKnown; }; /** * Command line preprocessor settings. * On the command line these are configured by -D, -U, -I, --include */ struct SIMPLECPP_LIB DUI { DUI() {} std::list defines; std::set undefined; std::list includePaths; std::list includes; }; SIMPLECPP_LIB std::map load(const TokenList &rawtokens, std::vector &filenames, const DUI &dui, OutputList *outputList = NULL); /** * Preprocess * @todo simplify interface * @param output TokenList that receives the preprocessing output * @param rawtokens Raw tokenlist for top sourcefile * @param files internal data of simplecpp * @param filedata output from simplecpp::load() * @param dui defines, undefs, and include paths * @param outputList output: list that will receive output messages * @param macroUsage output: macro usage */ SIMPLECPP_LIB void preprocess(TokenList &output, const TokenList &rawtokens, std::vector &files, std::map &filedata, const DUI &dui, OutputList *outputList = NULL, std::list *macroUsage = NULL); /** * Deallocate data */ SIMPLECPP_LIB void cleanup(std::map &filedata); /** Simplify path */ SIMPLECPP_LIB std::string simplifyPath(std::string path); /** Convert Cygwin path to Windows path */ SIMPLECPP_LIB std::string convertCygwinToWindowsPath(const std::string &cygwinPath); } #endif cppcheck-1.90/externals/tinyxml/000077500000000000000000000000001357737443600167575ustar00rootroot00000000000000cppcheck-1.90/externals/tinyxml/CMakeLists.txt000066400000000000000000000001421357737443600215140ustar00rootroot00000000000000file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") add_library(tinyxml_objs OBJECT ${srcs} ${hdrs}) cppcheck-1.90/externals/tinyxml/tinyxml2.cpp000066400000000000000000002164231357737443600212610ustar00rootroot00000000000000/* Original code by Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "tinyxml2.h" #include // yes, this one new style header, is in the Android SDK. #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) # include # include #else # include # include #endif #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) // Microsoft Visual Studio, version 2005 and higher. Not WinCE. /*int _snprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format [, argument] ... );*/ static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) { va_list va; va_start( va, format ); int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); va_end( va ); return result; } static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) { int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); return result; } #define TIXML_VSCPRINTF _vscprintf #define TIXML_SSCANF sscanf_s #elif defined _MSC_VER // Microsoft Visual Studio 2003 and earlier or WinCE #define TIXML_SNPRINTF _snprintf #define TIXML_VSNPRINTF _vsnprintf #define TIXML_SSCANF sscanf #if (_MSC_VER < 1400 ) && (!defined WINCE) // Microsoft Visual Studio 2003 and not WinCE. #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. #else // Microsoft Visual Studio 2003 and earlier or WinCE. static inline int TIXML_VSCPRINTF( const char* format, va_list va ) { int len = 512; for (;;) { len = len*2; char* str = new char[len](); const int required = _vsnprintf(str, len, format, va); delete[] str; if ( required != -1 ) { TIXMLASSERT( required >= 0 ); len = required; break; } } TIXMLASSERT( len >= 0 ); return len; } #endif #else // GCC version 3 and higher //#warning( "Using sn* functions." ) #define TIXML_SNPRINTF snprintf #define TIXML_VSNPRINTF vsnprintf static inline int TIXML_VSCPRINTF( const char* format, va_list va ) { int len = vsnprintf( 0, 0, format, va ); TIXMLASSERT( len >= 0 ); return len; } #define TIXML_SSCANF sscanf #endif static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF static const char LF = LINE_FEED; static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out static const char CR = CARRIAGE_RETURN; static const char SINGLE_QUOTE = '\''; static const char DOUBLE_QUOTE = '\"'; // Bunch of unicode info at: // http://www.unicode.org/faq/utf_bom.html // ef bb bf (Microsoft "lead bytes") - designates UTF-8 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; namespace tinyxml2 { struct Entity { const char* pattern; int length; char value; }; static const int NUM_ENTITIES = 5; static const Entity entities[NUM_ENTITIES] = { { "quot", 4, DOUBLE_QUOTE }, { "amp", 3, '&' }, { "apos", 4, SINGLE_QUOTE }, { "lt", 2, '<' }, { "gt", 2, '>' } }; StrPair::~StrPair() { Reset(); } void StrPair::TransferTo( StrPair* other ) { if ( this == other ) { return; } // This in effect implements the assignment operator by "moving" // ownership (as in auto_ptr). TIXMLASSERT( other != 0 ); TIXMLASSERT( other->_flags == 0 ); TIXMLASSERT( other->_start == 0 ); TIXMLASSERT( other->_end == 0 ); other->Reset(); other->_flags = _flags; other->_start = _start; other->_end = _end; _flags = 0; _start = 0; _end = 0; } void StrPair::Reset() { if ( _flags & NEEDS_DELETE ) { delete [] _start; } _flags = 0; _start = 0; _end = 0; } void StrPair::SetStr( const char* str, int flags ) { TIXMLASSERT( str ); Reset(); size_t len = strlen( str ); TIXMLASSERT( _start == 0 ); _start = new char[ len+1 ]; memcpy( _start, str, len+1 ); _end = _start + len; _flags = flags | NEEDS_DELETE; } char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) { TIXMLASSERT( p ); TIXMLASSERT( endTag && *endTag ); TIXMLASSERT(curLineNumPtr); char* start = p; char endChar = *endTag; size_t length = strlen( endTag ); // Inner loop of text parsing. while ( *p ) { if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { Set( start, p, strFlags ); return p + length; } else if (*p == '\n') { ++(*curLineNumPtr); } ++p; TIXMLASSERT( p ); } return 0; } char* StrPair::ParseName( char* p ) { if ( !p || !(*p) ) { return 0; } if ( !XMLUtil::IsNameStartChar( *p ) ) { return 0; } char* const start = p; ++p; while ( *p && XMLUtil::IsNameChar( *p ) ) { ++p; } Set( start, p, 0 ); return p; } void StrPair::CollapseWhitespace() { // Adjusting _start would cause undefined behavior on delete[] TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); // Trim leading space. _start = XMLUtil::SkipWhiteSpace( _start, 0 ); if ( *_start ) { const char* p = _start; // the read pointer char* q = _start; // the write pointer while( *p ) { if ( XMLUtil::IsWhiteSpace( *p )) { p = XMLUtil::SkipWhiteSpace( p, 0 ); if ( *p == 0 ) { break; // don't write to q; this trims the trailing space. } *q = ' '; ++q; } *q = *p; ++q; ++p; } *q = 0; } } const char* StrPair::GetStr() { TIXMLASSERT( _start ); TIXMLASSERT( _end ); if ( _flags & NEEDS_FLUSH ) { *_end = 0; _flags ^= NEEDS_FLUSH; if ( _flags ) { const char* p = _start; // the read pointer char* q = _start; // the write pointer while( p < _end ) { if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { // CR-LF pair becomes LF // CR alone becomes LF // LF-CR becomes LF if ( *(p+1) == LF ) { p += 2; } else { ++p; } *q = LF; ++q; } else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { if ( *(p+1) == CR ) { p += 2; } else { ++p; } *q = LF; ++q; } else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { // Entities handled by tinyXML2: // - special entities in the entity table [in/out] // - numeric character reference [in] // 中 or 中 if ( *(p+1) == '#' ) { const int buflen = 10; char buf[buflen] = { 0 }; int len = 0; char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); if ( adjusted == 0 ) { *q = *p; ++p; ++q; } else { TIXMLASSERT( 0 <= len && len <= buflen ); TIXMLASSERT( q + len <= adjusted ); p = adjusted; memcpy( q, buf, len ); q += len; } } else { bool entityFound = false; for( int i = 0; i < NUM_ENTITIES; ++i ) { const Entity& entity = entities[i]; if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 && *( p + entity.length + 1 ) == ';' ) { // Found an entity - convert. *q = entity.value; ++q; p += entity.length + 2; entityFound = true; break; } } if ( !entityFound ) { // fixme: treat as error? ++p; ++q; } } } else { *q = *p; ++p; ++q; } } *q = 0; } // The loop below has plenty going on, and this // is a less useful mode. Break it out. if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { CollapseWhitespace(); } _flags = (_flags & NEEDS_DELETE); } TIXMLASSERT( _start ); return _start; } // --------- XMLUtil ----------- // const char* XMLUtil::writeBoolTrue = "true"; const char* XMLUtil::writeBoolFalse = "false"; void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse) { static const char* defTrue = "true"; static const char* defFalse = "false"; writeBoolTrue = (writeTrue) ? writeTrue : defTrue; writeBoolFalse = (writeFalse) ? writeFalse : defFalse; } const char* XMLUtil::ReadBOM( const char* p, bool* bom ) { TIXMLASSERT( p ); TIXMLASSERT( bom ); *bom = false; const unsigned char* pu = reinterpret_cast(p); // Check for BOM: if ( *(pu+0) == TIXML_UTF_LEAD_0 && *(pu+1) == TIXML_UTF_LEAD_1 && *(pu+2) == TIXML_UTF_LEAD_2 ) { *bom = true; p += 3; } TIXMLASSERT( p ); return p; } void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) { const unsigned long BYTE_MASK = 0xBF; const unsigned long BYTE_MARK = 0x80; const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; if (input < 0x80) { *length = 1; } else if ( input < 0x800 ) { *length = 2; } else if ( input < 0x10000 ) { *length = 3; } else if ( input < 0x200000 ) { *length = 4; } else { *length = 0; // This code won't convert this correctly anyway. return; } output += *length; // Scary scary fall throughs are annotated with carefully designed comments // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc switch (*length) { case 4: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 3: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 2: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; //fall through case 1: --output; *output = (char)(input | FIRST_BYTE_MARK[*length]); break; default: TIXMLASSERT( false ); } } const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) { // Presume an entity, and pull it out. *length = 0; if ( *(p+1) == '#' && *(p+2) ) { unsigned long ucs = 0; TIXMLASSERT( sizeof( ucs ) >= 4 ); ptrdiff_t delta = 0; unsigned mult = 1; static const char SEMICOLON = ';'; if ( *(p+2) == 'x' ) { // Hexadecimal. const char* q = p+3; if ( !(*q) ) { return 0; } q = strchr( q, SEMICOLON ); if ( !q ) { return 0; } TIXMLASSERT( *q == SEMICOLON ); delta = q-p; --q; while ( *q != 'x' ) { unsigned int digit = 0; if ( *q >= '0' && *q <= '9' ) { digit = *q - '0'; } else if ( *q >= 'a' && *q <= 'f' ) { digit = *q - 'a' + 10; } else if ( *q >= 'A' && *q <= 'F' ) { digit = *q - 'A' + 10; } else { return 0; } TIXMLASSERT( digit < 16 ); TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); const unsigned int digitScaled = mult * digit; TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); ucs += digitScaled; TIXMLASSERT( mult <= UINT_MAX / 16 ); mult *= 16; --q; } } else { // Decimal. const char* q = p+2; if ( !(*q) ) { return 0; } q = strchr( q, SEMICOLON ); if ( !q ) { return 0; } TIXMLASSERT( *q == SEMICOLON ); delta = q-p; --q; while ( *q != '#' ) { if ( *q >= '0' && *q <= '9' ) { const unsigned int digit = *q - '0'; TIXMLASSERT( digit < 10 ); TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); const unsigned int digitScaled = mult * digit; TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); ucs += digitScaled; } else { return 0; } TIXMLASSERT( mult <= UINT_MAX / 10 ); mult *= 10; --q; } } // convert the UCS to UTF-8 ConvertUTF32ToUTF8( ucs, value, length ); return p + delta + 1; } return p+1; } void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); } void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); } void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse); } /* ToStr() of a number is a very tricky topic. https://github.com/leethomason/tinyxml2/issues/106 */ void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); } void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); } void XMLUtil::ToStr(int64_t v, char* buffer, int bufferSize) { // horrible syntax trick to make the compiler happy about %lld TIXML_SNPRINTF(buffer, bufferSize, "%lld", (long long)v); } bool XMLUtil::ToInt( const char* str, int* value ) { if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) { if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToBool( const char* str, bool* value ) { int ival = 0; if ( ToInt( str, &ival )) { *value = (ival==0) ? false : true; return true; } if ( StringEqual( str, "true" ) ) { *value = true; return true; } else if ( StringEqual( str, "false" ) ) { *value = false; return true; } return false; } bool XMLUtil::ToFloat( const char* str, float* value ) { if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToDouble( const char* str, double* value ) { if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToInt64(const char* str, int64_t* value) { long long v = 0; // horrible syntax trick to make the compiler happy about %lld if (TIXML_SSCANF(str, "%lld", &v) == 1) { *value = (int64_t)v; return true; } return false; } char* XMLDocument::Identify( char* p, XMLNode** node ) { TIXMLASSERT( node ); TIXMLASSERT( p ); char* const start = p; int const startLine = _parseCurLineNum; p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); if( !*p ) { *node = 0; TIXMLASSERT( p ); return p; } // These strings define the matching patterns: static const char* xmlHeader = { "( _commentPool ); returnNode->_parseLineNum = _parseCurLineNum; p += xmlHeaderLen; } else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { returnNode = CreateUnlinkedNode( _commentPool ); returnNode->_parseLineNum = _parseCurLineNum; p += commentHeaderLen; } else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { XMLText* text = CreateUnlinkedNode( _textPool ); returnNode = text; returnNode->_parseLineNum = _parseCurLineNum; p += cdataHeaderLen; text->SetCData( true ); } else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { returnNode = CreateUnlinkedNode( _commentPool ); returnNode->_parseLineNum = _parseCurLineNum; p += dtdHeaderLen; } else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { returnNode = CreateUnlinkedNode( _elementPool ); returnNode->_parseLineNum = _parseCurLineNum; p += elementHeaderLen; } else { returnNode = CreateUnlinkedNode( _textPool ); returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character p = start; // Back it up, all the text counts. _parseCurLineNum = startLine; } TIXMLASSERT( returnNode ); TIXMLASSERT( p ); *node = returnNode; return p; } bool XMLDocument::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); if ( visitor->VisitEnter( *this ) ) { for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) { break; } } } return visitor->VisitExit( *this ); } // --------- XMLNode ----------- // XMLNode::XMLNode( XMLDocument* doc ) : _document( doc ), _parent( 0 ), _value(), _parseLineNum( 0 ), _firstChild( 0 ), _lastChild( 0 ), _prev( 0 ), _next( 0 ), _userData( 0 ), _memPool( 0 ) { } XMLNode::~XMLNode() { DeleteChildren(); if ( _parent ) { _parent->Unlink( this ); } } const char* XMLNode::Value() const { // Edge case: XMLDocuments don't have a Value. Return null. if ( this->ToDocument() ) return 0; return _value.GetStr(); } void XMLNode::SetValue( const char* str, bool staticMem ) { if ( staticMem ) { _value.SetInternedStr( str ); } else { _value.SetStr( str ); } } XMLNode* XMLNode::DeepClone(XMLDocument* target) const { XMLNode* clone = this->ShallowClone(target); if (!clone) return 0; for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) { XMLNode* childClone = child->DeepClone(target); TIXMLASSERT(childClone); clone->InsertEndChild(childClone); } return clone; } void XMLNode::DeleteChildren() { while( _firstChild ) { TIXMLASSERT( _lastChild ); DeleteChild( _firstChild ); } _firstChild = _lastChild = 0; } void XMLNode::Unlink( XMLNode* child ) { TIXMLASSERT( child ); TIXMLASSERT( child->_document == _document ); TIXMLASSERT( child->_parent == this ); if ( child == _firstChild ) { _firstChild = _firstChild->_next; } if ( child == _lastChild ) { _lastChild = _lastChild->_prev; } if ( child->_prev ) { child->_prev->_next = child->_next; } if ( child->_next ) { child->_next->_prev = child->_prev; } child->_next = 0; child->_prev = 0; child->_parent = 0; } void XMLNode::DeleteChild( XMLNode* node ) { TIXMLASSERT( node ); TIXMLASSERT( node->_document == _document ); TIXMLASSERT( node->_parent == this ); Unlink( node ); TIXMLASSERT(node->_prev == 0); TIXMLASSERT(node->_next == 0); TIXMLASSERT(node->_parent == 0); DeleteNode( node ); } XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) { TIXMLASSERT( addThis ); if ( addThis->_document != _document ) { TIXMLASSERT( false ); return 0; } InsertChildPreamble( addThis ); if ( _lastChild ) { TIXMLASSERT( _firstChild ); TIXMLASSERT( _lastChild->_next == 0 ); _lastChild->_next = addThis; addThis->_prev = _lastChild; _lastChild = addThis; addThis->_next = 0; } else { TIXMLASSERT( _firstChild == 0 ); _firstChild = _lastChild = addThis; addThis->_prev = 0; addThis->_next = 0; } addThis->_parent = this; return addThis; } XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) { TIXMLASSERT( addThis ); if ( addThis->_document != _document ) { TIXMLASSERT( false ); return 0; } InsertChildPreamble( addThis ); if ( _firstChild ) { TIXMLASSERT( _lastChild ); TIXMLASSERT( _firstChild->_prev == 0 ); _firstChild->_prev = addThis; addThis->_next = _firstChild; _firstChild = addThis; addThis->_prev = 0; } else { TIXMLASSERT( _lastChild == 0 ); _firstChild = _lastChild = addThis; addThis->_prev = 0; addThis->_next = 0; } addThis->_parent = this; return addThis; } XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) { TIXMLASSERT( addThis ); if ( addThis->_document != _document ) { TIXMLASSERT( false ); return 0; } TIXMLASSERT( afterThis ); if ( afterThis->_parent != this ) { TIXMLASSERT( false ); return 0; } if ( afterThis == addThis ) { // Current state: BeforeThis -> AddThis -> OneAfterAddThis // Now AddThis must disappear from it's location and then // reappear between BeforeThis and OneAfterAddThis. // So just leave it where it is. return addThis; } if ( afterThis->_next == 0 ) { // The last node or the only node. return InsertEndChild( addThis ); } InsertChildPreamble( addThis ); addThis->_prev = afterThis; addThis->_next = afterThis->_next; afterThis->_next->_prev = addThis; afterThis->_next = addThis; addThis->_parent = this; return addThis; } const XMLElement* XMLNode::FirstChildElement( const char* name ) const { for( const XMLNode* node = _firstChild; node; node = node->_next ) { const XMLElement* element = node->ToElementWithName( name ); if ( element ) { return element; } } return 0; } const XMLElement* XMLNode::LastChildElement( const char* name ) const { for( const XMLNode* node = _lastChild; node; node = node->_prev ) { const XMLElement* element = node->ToElementWithName( name ); if ( element ) { return element; } } return 0; } const XMLElement* XMLNode::NextSiblingElement( const char* name ) const { for( const XMLNode* node = _next; node; node = node->_next ) { const XMLElement* element = node->ToElementWithName( name ); if ( element ) { return element; } } return 0; } const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const { for( const XMLNode* node = _prev; node; node = node->_prev ) { const XMLElement* element = node->ToElementWithName( name ); if ( element ) { return element; } } return 0; } char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) { // This is a recursive method, but thinking about it "at the current level" // it is a pretty simple flat list: // // // // With a special case: // // // // // Where the closing element (/foo) *must* be the next thing after the opening // element, and the names must match. BUT the tricky bit is that the closing // element will be read by the child. // // 'endTag' is the end tag for this node, it is returned by a call to a child. // 'parentEnd' is the end tag for the parent, which is filled in and returned. while( p && *p ) { XMLNode* node = 0; p = _document->Identify( p, &node ); TIXMLASSERT( p ); if ( node == 0 ) { break; } int initialLineNum = node->_parseLineNum; StrPair endTag; p = node->ParseDeep( p, &endTag, curLineNumPtr ); if ( !p ) { DeleteNode( node ); if ( !_document->Error() ) { _document->SetError( XML_ERROR_PARSING, initialLineNum, 0); } break; } XMLDeclaration* decl = node->ToDeclaration(); if ( decl ) { // Declarations are only allowed at document level bool wellLocated = ( ToDocument() != 0 ); if ( wellLocated ) { // Multiple declarations are allowed but all declarations // must occur before anything else for ( const XMLNode* existingNode = _document->FirstChild(); existingNode; existingNode = existingNode->NextSibling() ) { if ( !existingNode->ToDeclaration() ) { wellLocated = false; break; } } } if ( !wellLocated ) { _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value()); DeleteNode( node ); break; } } XMLElement* ele = node->ToElement(); if ( ele ) { // We read the end tag. Return it to the parent. if ( ele->ClosingType() == XMLElement::CLOSING ) { if ( parentEndTag ) { ele->_value.TransferTo( parentEndTag ); } node->_memPool->SetTracked(); // created and then immediately deleted. DeleteNode( node ); return p; } // Handle an end tag returned to this level. // And handle a bunch of annoying errors. bool mismatch = false; if ( endTag.Empty() ) { if ( ele->ClosingType() == XMLElement::OPEN ) { mismatch = true; } } else { if ( ele->ClosingType() != XMLElement::OPEN ) { mismatch = true; } else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { mismatch = true; } } if ( mismatch ) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name()); DeleteNode( node ); break; } } InsertEndChild( node ); } return 0; } /*static*/ void XMLNode::DeleteNode( XMLNode* node ) { if ( node == 0 ) { return; } TIXMLASSERT(node->_document); if (!node->ToDocument()) { node->_document->MarkInUse(node); } MemPool* pool = node->_memPool; node->~XMLNode(); pool->Free( node ); } void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const { TIXMLASSERT( insertThis ); TIXMLASSERT( insertThis->_document == _document ); if (insertThis->_parent) { insertThis->_parent->Unlink( insertThis ); } else { insertThis->_document->MarkInUse(insertThis); insertThis->_memPool->SetTracked(); } } const XMLElement* XMLNode::ToElementWithName( const char* name ) const { const XMLElement* element = this->ToElement(); if ( element == 0 ) { return 0; } if ( name == 0 ) { return element; } if ( XMLUtil::StringEqual( element->Name(), name ) ) { return element; } return 0; } // --------- XMLText ---------- // char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) { if ( this->CData() ) { p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 ); } return p; } else { int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; } p = _value.ParseText( p, "<", flags, curLineNumPtr ); if ( p && *p ) { return p-1; } if ( !p ) { _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 ); } } return 0; } XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? text->SetCData( this->CData() ); return text; } bool XMLText::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLText* text = compare->ToText(); return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); } bool XMLText::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); return visitor->Visit( *this ); } // --------- XMLComment ---------- // XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) { } XMLComment::~XMLComment() { } char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) { // Comment parses as text. p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr ); if ( p == 0 ) { _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 ); } return p; } XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? return comment; } bool XMLComment::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLComment* comment = compare->ToComment(); return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); } bool XMLComment::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); return visitor->Visit( *this ); } // --------- XMLDeclaration ---------- // XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) { } XMLDeclaration::~XMLDeclaration() { //printf( "~XMLDeclaration\n" ); } char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) { // Declaration parses as text. p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); if ( p == 0 ) { _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 ); } return p; } XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? return dec; } bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLDeclaration* declaration = compare->ToDeclaration(); return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); } bool XMLDeclaration::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); return visitor->Visit( *this ); } // --------- XMLUnknown ---------- // XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) { } XMLUnknown::~XMLUnknown() { } char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) { // Unknown parses as text. p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 ); } return p; } XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? return text; } bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLUnknown* unknown = compare->ToUnknown(); return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); } bool XMLUnknown::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); return visitor->Visit( *this ); } // --------- XMLAttribute ---------- // const char* XMLAttribute::Name() const { return _name.GetStr(); } const char* XMLAttribute::Value() const { return _value.GetStr(); } char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr ) { // Parse using the name rules: bug fix, was using ParseText before p = _name.ParseName( p ); if ( !p || !*p ) { return 0; } // Skip white space before = p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); if ( *p != '=' ) { return 0; } ++p; // move up to opening quote p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); if ( *p != '\"' && *p != '\'' ) { return 0; } char endTag[2] = { *p, 0 }; ++p; // move past opening quote p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); return p; } void XMLAttribute::SetName( const char* n ) { _name.SetStr( n ); } XMLError XMLAttribute::QueryIntValue( int* value ) const { if ( XMLUtil::ToInt( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const { if ( XMLUtil::ToUnsigned( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryInt64Value(int64_t* value) const { if (XMLUtil::ToInt64(Value(), value)) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryBoolValue( bool* value ) const { if ( XMLUtil::ToBool( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryFloatValue( float* value ) const { if ( XMLUtil::ToFloat( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryDoubleValue( double* value ) const { if ( XMLUtil::ToDouble( Value(), value )) { return XML_SUCCESS; } return XML_WRONG_ATTRIBUTE_TYPE; } void XMLAttribute::SetAttribute( const char* v ) { _value.SetStr( v ); } void XMLAttribute::SetAttribute( int v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( unsigned v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute(int64_t v) { char buf[BUF_SIZE]; XMLUtil::ToStr(v, buf, BUF_SIZE); _value.SetStr(buf); } void XMLAttribute::SetAttribute( bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( double v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( float v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } // --------- XMLElement ---------- // XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), _closingType( OPEN ), _rootAttribute( 0 ) { } XMLElement::~XMLElement() { while( _rootAttribute ) { XMLAttribute* next = _rootAttribute->_next; DeleteAttribute( _rootAttribute ); _rootAttribute = next; } } const XMLAttribute* XMLElement::FindAttribute( const char* name ) const { for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { if ( XMLUtil::StringEqual( a->Name(), name ) ) { return a; } } return 0; } const char* XMLElement::Attribute( const char* name, const char* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return 0; } if ( !value || XMLUtil::StringEqual( a->Value(), value )) { return a->Value(); } return 0; } int XMLElement::IntAttribute(const char* name, int defaultValue) const { int i = defaultValue; QueryIntAttribute(name, &i); return i; } unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const { unsigned i = defaultValue; QueryUnsignedAttribute(name, &i); return i; } int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const { int64_t i = defaultValue; QueryInt64Attribute(name, &i); return i; } bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const { bool b = defaultValue; QueryBoolAttribute(name, &b); return b; } double XMLElement::DoubleAttribute(const char* name, double defaultValue) const { double d = defaultValue; QueryDoubleAttribute(name, &d); return d; } float XMLElement::FloatAttribute(const char* name, float defaultValue) const { float f = defaultValue; QueryFloatAttribute(name, &f); return f; } const char* XMLElement::GetText() const { if ( FirstChild() && FirstChild()->ToText() ) { return FirstChild()->Value(); } return 0; } void XMLElement::SetText( const char* inText ) { if ( FirstChild() && FirstChild()->ToText() ) FirstChild()->SetValue( inText ); else { XMLText* theText = GetDocument()->NewText( inText ); InsertFirstChild( theText ); } } void XMLElement::SetText( int v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } void XMLElement::SetText( unsigned v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } void XMLElement::SetText(int64_t v) { char buf[BUF_SIZE]; XMLUtil::ToStr(v, buf, BUF_SIZE); SetText(buf); } void XMLElement::SetText( bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } void XMLElement::SetText( float v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } void XMLElement::SetText( double v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); SetText( buf ); } XMLError XMLElement::QueryIntText( int* ival ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToInt( t, ival ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToUnsigned( t, uval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryInt64Text(int64_t* ival) const { if (FirstChild() && FirstChild()->ToText()) { const char* t = FirstChild()->Value(); if (XMLUtil::ToInt64(t, ival)) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryBoolText( bool* bval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToBool( t, bval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryDoubleText( double* dval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToDouble( t, dval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryFloatText( float* fval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->Value(); if ( XMLUtil::ToFloat( t, fval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } int XMLElement::IntText(int defaultValue) const { int i = defaultValue; QueryIntText(&i); return i; } unsigned XMLElement::UnsignedText(unsigned defaultValue) const { unsigned i = defaultValue; QueryUnsignedText(&i); return i; } int64_t XMLElement::Int64Text(int64_t defaultValue) const { int64_t i = defaultValue; QueryInt64Text(&i); return i; } bool XMLElement::BoolText(bool defaultValue) const { bool b = defaultValue; QueryBoolText(&b); return b; } double XMLElement::DoubleText(double defaultValue) const { double d = defaultValue; QueryDoubleText(&d); return d; } float XMLElement::FloatText(float defaultValue) const { float f = defaultValue; QueryFloatText(&f); return f; } XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) { XMLAttribute* last = 0; XMLAttribute* attrib = 0; for( attrib = _rootAttribute; attrib; last = attrib, attrib = attrib->_next ) { if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { break; } } if ( !attrib ) { attrib = CreateAttribute(); TIXMLASSERT( attrib ); if ( last ) { TIXMLASSERT( last->_next == 0 ); last->_next = attrib; } else { TIXMLASSERT( _rootAttribute == 0 ); _rootAttribute = attrib; } attrib->SetName( name ); } return attrib; } void XMLElement::DeleteAttribute( const char* name ) { XMLAttribute* prev = 0; for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { if ( XMLUtil::StringEqual( name, a->Name() ) ) { if ( prev ) { prev->_next = a->_next; } else { _rootAttribute = a->_next; } DeleteAttribute( a ); break; } prev = a; } } char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) { XMLAttribute* prevAttribute = 0; // Read the attributes. while( p ) { p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); if ( !(*p) ) { _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() ); return 0; } // attribute. if (XMLUtil::IsNameStartChar( *p ) ) { XMLAttribute* attrib = CreateAttribute(); TIXMLASSERT( attrib ); attrib->_parseLineNum = _document->_parseCurLineNum; int attrLineNum = attrib->_parseLineNum; p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); if ( !p || Attribute( attrib->Name() ) ) { DeleteAttribute( attrib ); _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() ); return 0; } // There is a minor bug here: if the attribute in the source xml // document is duplicated, it will not be detected and the // attribute will be doubly added. However, tracking the 'prevAttribute' // avoids re-scanning the attribute list. Preferring performance for // now, may reconsider in the future. if ( prevAttribute ) { TIXMLASSERT( prevAttribute->_next == 0 ); prevAttribute->_next = attrib; } else { TIXMLASSERT( _rootAttribute == 0 ); _rootAttribute = attrib; } prevAttribute = attrib; } // end of the tag else if ( *p == '>' ) { ++p; break; } // end of the tag else if ( *p == '/' && *(p+1) == '>' ) { _closingType = CLOSED; return p+2; // done; sealed element. } else { _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 ); return 0; } } return p; } void XMLElement::DeleteAttribute( XMLAttribute* attribute ) { if ( attribute == 0 ) { return; } MemPool* pool = attribute->_memPool; attribute->~XMLAttribute(); pool->Free( attribute ); } XMLAttribute* XMLElement::CreateAttribute() { TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); TIXMLASSERT( attrib ); attrib->_memPool = &_document->_attributePool; attrib->_memPool->SetTracked(); return attrib; } // // // foobar // char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) { // Read the element name. p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); // The closing element is the form. It is // parsed just like a regular element then deleted from // the DOM. if ( *p == '/' ) { _closingType = CLOSING; ++p; } p = _value.ParseName( p ); if ( _value.Empty() ) { return 0; } p = ParseAttributes( p, curLineNumPtr ); if ( !p || !*p || _closingType != OPEN ) { return p; } p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr ); return p; } XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? } return element; } bool XMLElement::ShallowEqual( const XMLNode* compare ) const { TIXMLASSERT( compare ); const XMLElement* other = compare->ToElement(); if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { const XMLAttribute* a=FirstAttribute(); const XMLAttribute* b=other->FirstAttribute(); while ( a && b ) { if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { return false; } a = a->Next(); b = b->Next(); } if ( a || b ) { // different count return false; } return true; } return false; } bool XMLElement::Accept( XMLVisitor* visitor ) const { TIXMLASSERT( visitor ); if ( visitor->VisitEnter( *this, _rootAttribute ) ) { for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) { break; } } } return visitor->VisitExit( *this ); } // --------- XMLDocument ----------- // // Warning: List must match 'enum XMLError' const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { "XML_SUCCESS", "XML_NO_ATTRIBUTE", "XML_WRONG_ATTRIBUTE_TYPE", "XML_ERROR_FILE_NOT_FOUND", "XML_ERROR_FILE_COULD_NOT_BE_OPENED", "XML_ERROR_FILE_READ_ERROR", "UNUSED_XML_ERROR_ELEMENT_MISMATCH", "XML_ERROR_PARSING_ELEMENT", "XML_ERROR_PARSING_ATTRIBUTE", "UNUSED_XML_ERROR_IDENTIFYING_TAG", "XML_ERROR_PARSING_TEXT", "XML_ERROR_PARSING_CDATA", "XML_ERROR_PARSING_COMMENT", "XML_ERROR_PARSING_DECLARATION", "XML_ERROR_PARSING_UNKNOWN", "XML_ERROR_EMPTY_DOCUMENT", "XML_ERROR_MISMATCHED_ELEMENT", "XML_ERROR_PARSING", "XML_CAN_NOT_CONVERT_TEXT", "XML_NO_TEXT_NODE" }; XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : XMLNode( 0 ), _writeBOM( false ), _processEntities( processEntities ), _errorID(XML_SUCCESS), _whitespaceMode( whitespaceMode ), _errorStr(), _errorLineNum( 0 ), _charBuffer( 0 ), _parseCurLineNum( 0 ), _unlinked(), _elementPool(), _attributePool(), _textPool(), _commentPool() { // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) _document = this; } XMLDocument::~XMLDocument() { Clear(); } void XMLDocument::MarkInUse(XMLNode* node) { TIXMLASSERT(node); TIXMLASSERT(node->_parent == 0); for (int i = 0; i < _unlinked.Size(); ++i) { if (node == _unlinked[i]) { _unlinked.SwapRemove(i); break; } } } void XMLDocument::Clear() { DeleteChildren(); while( _unlinked.Size()) { DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. } #ifdef TINYXML2_DEBUG const bool hadError = Error(); #endif ClearError(); delete [] _charBuffer; _charBuffer = 0; #if 0 _textPool.Trace( "text" ); _elementPool.Trace( "element" ); _commentPool.Trace( "comment" ); _attributePool.Trace( "attribute" ); #endif #ifdef TINYXML2_DEBUG if ( !hadError ) { TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); } #endif } void XMLDocument::DeepCopy(XMLDocument* target) const { TIXMLASSERT(target); if (target == this) { return; // technically success - a no-op. } target->Clear(); for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) { target->InsertEndChild(node->DeepClone(target)); } } XMLElement* XMLDocument::NewElement( const char* name ) { XMLElement* ele = CreateUnlinkedNode( _elementPool ); ele->SetName( name ); return ele; } XMLComment* XMLDocument::NewComment( const char* str ) { XMLComment* comment = CreateUnlinkedNode( _commentPool ); comment->SetValue( str ); return comment; } XMLText* XMLDocument::NewText( const char* str ) { XMLText* text = CreateUnlinkedNode( _textPool ); text->SetValue( str ); return text; } XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) { XMLDeclaration* dec = CreateUnlinkedNode( _commentPool ); dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); return dec; } XMLUnknown* XMLDocument::NewUnknown( const char* str ) { XMLUnknown* unk = CreateUnlinkedNode( _commentPool ); unk->SetValue( str ); return unk; } static FILE* callfopen( const char* filepath, const char* mode ) { TIXMLASSERT( filepath ); TIXMLASSERT( mode ); #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) FILE* fp = 0; errno_t err = fopen_s( &fp, filepath, mode ); if ( err ) { return 0; } #else FILE* fp = fopen( filepath, mode ); #endif return fp; } void XMLDocument::DeleteNode( XMLNode* node ) { TIXMLASSERT( node ); TIXMLASSERT(node->_document == this ); if (node->_parent) { node->_parent->DeleteChild( node ); } else { // Isn't in the tree. // Use the parent delete. // Also, we need to mark it tracked: we 'know' // it was never used. node->_memPool->SetTracked(); // Call the static XMLNode version: XMLNode::DeleteNode(node); } } XMLError XMLDocument::LoadFile( const char* filename ) { if ( !filename ) { TIXMLASSERT( false ); SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); return _errorID; } Clear(); FILE* fp = callfopen( filename, "rb" ); if ( !fp ) { SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ); return _errorID; } LoadFile( fp ); fclose( fp ); return _errorID; } // This is likely overengineered template art to have a check that unsigned long value incremented // by one still fits into size_t. If size_t type is larger than unsigned long type // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit // -Wtype-limits warning. This piece makes the compiler select code with a check when a check // is useful and code with no check when a check is redundant depending on how size_t and unsigned long // types sizes relate to each other. template = sizeof(size_t))> struct LongFitsIntoSizeTMinusOne { static bool Fits( unsigned long value ) { return value < (size_t)-1; } }; template <> struct LongFitsIntoSizeTMinusOne { static bool Fits( unsigned long ) { return true; } }; XMLError XMLDocument::LoadFile( FILE* fp ) { Clear(); fseek( fp, 0, SEEK_SET ); if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } fseek( fp, 0, SEEK_END ); const long filelength = ftell( fp ); fseek( fp, 0, SEEK_SET ); if ( filelength == -1L ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } TIXMLASSERT( filelength >= 0 ); if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) { // Cannot handle files which won't fit in buffer together with null terminator SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } if ( filelength == 0 ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } const size_t size = filelength; TIXMLASSERT( _charBuffer == 0 ); _charBuffer = new char[size+1]; size_t read = fread( _charBuffer, 1, size, fp ); if ( read != size ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } _charBuffer[size] = 0; Parse(); return _errorID; } XMLError XMLDocument::SaveFile( const char* filename, bool compact ) { if ( !filename ) { TIXMLASSERT( false ); SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); return _errorID; } FILE* fp = callfopen( filename, "w" ); if ( !fp ) { SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ); return _errorID; } SaveFile(fp, compact); fclose( fp ); return _errorID; } XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) { // Clear any error from the last save, otherwise it will get reported // for *this* call. ClearError(); XMLPrinter stream( fp, compact ); Print( &stream ); return _errorID; } XMLError XMLDocument::Parse( const char* p, size_t len ) { Clear(); if ( len == 0 || !p || !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } if ( len == (size_t)(-1) ) { len = strlen( p ); } TIXMLASSERT( _charBuffer == 0 ); _charBuffer = new char[ len+1 ]; memcpy( _charBuffer, p, len ); _charBuffer[len] = 0; Parse(); if ( Error() ) { // clean up now essentially dangling memory. // and the parse fail can put objects in the // pools that are dead and inaccessible. DeleteChildren(); _elementPool.Clear(); _attributePool.Clear(); _textPool.Clear(); _commentPool.Clear(); } return _errorID; } void XMLDocument::Print( XMLPrinter* streamer ) const { if ( streamer ) { Accept( streamer ); } else { XMLPrinter stdoutStreamer( stdout ); Accept( &stdoutStreamer ); } } void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... ) { TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); _errorID = error; _errorLineNum = lineNum; _errorStr.Reset(); size_t BUFFER_SIZE = 1000; char* buffer = new char[BUFFER_SIZE]; TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum); if (format) { size_t len = strlen(buffer); TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": "); len = strlen(buffer); va_list va; va_start(va, format); TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va); va_end(va); } _errorStr.SetStr(buffer); delete[] buffer; } /*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID) { TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); const char* errorName = _errorNames[errorID]; TIXMLASSERT( errorName && errorName[0] ); return errorName; } const char* XMLDocument::ErrorStr() const { return _errorStr.Empty() ? "" : _errorStr.GetStr(); } void XMLDocument::PrintError() const { printf("%s\n", ErrorStr()); } const char* XMLDocument::ErrorName() const { return ErrorIDToName(_errorID); } void XMLDocument::Parse() { TIXMLASSERT( NoChildren() ); // Clear() must have been called previously TIXMLASSERT( _charBuffer ); _parseCurLineNum = 1; _parseLineNum = 1; char* p = _charBuffer; p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); if ( !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return; } ParseDeep(p, 0, &_parseCurLineNum ); } XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : _elementJustOpened( false ), _stack(), _firstElement( true ), _fp( file ), _depth( depth ), _textDepth( -1 ), _processEntities( true ), _compactMode( compact ), _buffer() { for( int i=0; i'] = true; // not required, but consistency is nice _buffer.Push( 0 ); } void XMLPrinter::Print( const char* format, ... ) { va_list va; va_start( va, format ); if ( _fp ) { vfprintf( _fp, format, va ); } else { const int len = TIXML_VSCPRINTF( format, va ); // Close out and re-start the va-args va_end( va ); TIXMLASSERT( len >= 0 ); va_start( va, format ); TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. TIXML_VSNPRINTF( p, len+1, format, va ); } va_end( va ); } void XMLPrinter::Write( const char* data, size_t size ) { if ( _fp ) { fwrite ( data , sizeof(char), size, _fp); } else { char* p = _buffer.PushArr( static_cast(size) ) - 1; // back up over the null terminator. memcpy( p, data, size ); p[size] = 0; } } void XMLPrinter::Putc( char ch ) { if ( _fp ) { fputc ( ch, _fp); } else { char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator. p[0] = ch; p[1] = 0; } } void XMLPrinter::PrintSpace( int depth ) { for( int i=0; i 0 && *q < ENTITY_RANGE ) { // Check for entities. If one is found, flush // the stream up until the entity, write the // entity, and keep looking. if ( flag[(unsigned char)(*q)] ) { while ( p < q ) { const size_t delta = q - p; const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; Write( p, toPrint ); p += toPrint; } bool entityPatternPrinted = false; for( int i=0; i( bom ) ); } if ( writeDec ) { PushDeclaration( "xml version=\"1.0\"" ); } } void XMLPrinter::OpenElement( const char* name, bool compactMode ) { SealElementIfJustOpened(); _stack.Push( name ); if ( _textDepth < 0 && !_firstElement && !compactMode ) { Putc( '\n' ); } if ( !compactMode ) { PrintSpace( _depth ); } Write ( "<" ); Write ( name ); _elementJustOpened = true; _firstElement = false; ++_depth; } void XMLPrinter::PushAttribute( const char* name, const char* value ) { TIXMLASSERT( _elementJustOpened ); Putc ( ' ' ); Write( name ); Write( "=\"" ); PrintString( value, false ); Putc ( '\"' ); } void XMLPrinter::PushAttribute( const char* name, int v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute( const char* name, unsigned v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute(const char* name, int64_t v) { char buf[BUF_SIZE]; XMLUtil::ToStr(v, buf, BUF_SIZE); PushAttribute(name, buf); } void XMLPrinter::PushAttribute( const char* name, bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute( const char* name, double v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::CloseElement( bool compactMode ) { --_depth; const char* name = _stack.Pop(); if ( _elementJustOpened ) { Write( "/>" ); } else { if ( _textDepth < 0 && !compactMode) { Putc( '\n' ); PrintSpace( _depth ); } Write ( "" ); } if ( _textDepth == _depth ) { _textDepth = -1; } if ( _depth == 0 && !compactMode) { Putc( '\n' ); } _elementJustOpened = false; } void XMLPrinter::SealElementIfJustOpened() { if ( !_elementJustOpened ) { return; } _elementJustOpened = false; Putc( '>' ); } void XMLPrinter::PushText( const char* text, bool cdata ) { _textDepth = _depth-1; SealElementIfJustOpened(); if ( cdata ) { Write( "" ); } else { PrintString( text, true ); } } void XMLPrinter::PushText( int64_t value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( int value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( unsigned value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( bool value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( float value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( double value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushComment( const char* comment ) { SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Putc( '\n' ); PrintSpace( _depth ); } _firstElement = false; Write( "" ); } void XMLPrinter::PushDeclaration( const char* value ) { SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Putc( '\n' ); PrintSpace( _depth ); } _firstElement = false; Write( "" ); } void XMLPrinter::PushUnknown( const char* value ) { SealElementIfJustOpened(); if ( _textDepth < 0 && !_firstElement && !_compactMode) { Putc( '\n' ); PrintSpace( _depth ); } _firstElement = false; Write( "' ); } bool XMLPrinter::VisitEnter( const XMLDocument& doc ) { _processEntities = doc.ProcessEntities(); if ( doc.HasBOM() ) { PushHeader( true, false ); } return true; } bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) { const XMLElement* parentElem = 0; if ( element.Parent() ) { parentElem = element.Parent()->ToElement(); } const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; OpenElement( element.Name(), compactMode ); while ( attribute ) { PushAttribute( attribute->Name(), attribute->Value() ); attribute = attribute->Next(); } return true; } bool XMLPrinter::VisitExit( const XMLElement& element ) { CloseElement( CompactMode(element) ); return true; } bool XMLPrinter::Visit( const XMLText& text ) { PushText( text.Value(), text.CData() ); return true; } bool XMLPrinter::Visit( const XMLComment& comment ) { PushComment( comment.Value() ); return true; } bool XMLPrinter::Visit( const XMLDeclaration& declaration ) { PushDeclaration( declaration.Value() ); return true; } bool XMLPrinter::Visit( const XMLUnknown& unknown ) { PushUnknown( unknown.Value() ); return true; } } // namespace tinyxml2 cppcheck-1.90/externals/tinyxml/tinyxml2.h000066400000000000000000002107541357737443600207270ustar00rootroot00000000000000/* Original code by Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TINYXML2_INCLUDED #define TINYXML2_INCLUDED #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) # include # include # include # include # include # if defined(__PS3__) # include # endif #else # include # include # include # include # include #endif #include /* TODO: intern strings instead of allocation. */ /* gcc: g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe Formatting, Artistic Style: AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h */ #if defined( _DEBUG ) || defined (__DEBUG__) # ifndef TINYXML2_DEBUG # define TINYXML2_DEBUG # endif #endif #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4251) #endif #ifdef _WIN32 # ifdef TINYXML2_EXPORT # define TINYXML2_LIB __declspec(dllexport) # elif defined(TINYXML2_IMPORT) # define TINYXML2_LIB __declspec(dllimport) # else # define TINYXML2_LIB # endif #elif __GNUC__ >= 4 # define TINYXML2_LIB __attribute__((visibility("default"))) #else # define TINYXML2_LIB #endif #if defined(TINYXML2_DEBUG) # if defined(_MSC_VER) # // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like # define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } # elif defined (ANDROID_NDK) # include # define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } # else # include # define TIXMLASSERT assert # endif #else # define TIXMLASSERT( x ) {} #endif /* Versioning, past 1.0.14: http://semver.org/ */ static const int TIXML2_MAJOR_VERSION = 6; static const int TIXML2_MINOR_VERSION = 1; static const int TIXML2_PATCH_VERSION = 0; #define TINYXML2_MAJOR_VERSION 6 #define TINYXML2_MINOR_VERSION 1 #define TINYXML2_PATCH_VERSION 0 namespace tinyxml2 { class XMLDocument; class XMLElement; class XMLAttribute; class XMLComment; class XMLText; class XMLDeclaration; class XMLUnknown; class XMLPrinter; /* A class that wraps strings. Normally stores the start and end pointers into the XML file itself, and will apply normalization and entity translation if actually read. Can also store (and memory manage) a traditional char[] */ class StrPair { public: enum { NEEDS_ENTITY_PROCESSING = 0x01, NEEDS_NEWLINE_NORMALIZATION = 0x02, NEEDS_WHITESPACE_COLLAPSING = 0x04, TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, ATTRIBUTE_NAME = 0, ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, COMMENT = NEEDS_NEWLINE_NORMALIZATION }; StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} ~StrPair(); void Set( char* start, char* end, int flags ) { TIXMLASSERT( start ); TIXMLASSERT( end ); Reset(); _start = start; _end = end; _flags = flags | NEEDS_FLUSH; } const char* GetStr(); bool Empty() const { return _start == _end; } void SetInternedStr( const char* str ) { Reset(); _start = const_cast(str); } void SetStr( const char* str, int flags=0 ); char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr ); char* ParseName( char* in ); void TransferTo( StrPair* other ); void Reset(); private: void CollapseWhitespace(); enum { NEEDS_FLUSH = 0x100, NEEDS_DELETE = 0x200 }; int _flags; char* _start; char* _end; StrPair( const StrPair& other ); // not supported void operator=( StrPair& other ); // not supported, use TransferTo() }; /* A dynamic array of Plain Old Data. Doesn't support constructors, etc. Has a small initial memory pool, so that low or no usage will not cause a call to new/delete */ template class DynArray { public: DynArray() : _mem( _pool ), _allocated( INITIAL_SIZE ), _size( 0 ) { } ~DynArray() { if ( _mem != _pool ) { delete [] _mem; } } void Clear() { _size = 0; } void Push( T t ) { TIXMLASSERT( _size < INT_MAX ); EnsureCapacity( _size+1 ); _mem[_size] = t; ++_size; } T* PushArr( int count ) { TIXMLASSERT( count >= 0 ); TIXMLASSERT( _size <= INT_MAX - count ); EnsureCapacity( _size+count ); T* ret = &_mem[_size]; _size += count; return ret; } T Pop() { TIXMLASSERT( _size > 0 ); --_size; return _mem[_size]; } void PopArr( int count ) { TIXMLASSERT( _size >= count ); _size -= count; } bool Empty() const { return _size == 0; } T& operator[](int i) { TIXMLASSERT( i>= 0 && i < _size ); return _mem[i]; } const T& operator[](int i) const { TIXMLASSERT( i>= 0 && i < _size ); return _mem[i]; } const T& PeekTop() const { TIXMLASSERT( _size > 0 ); return _mem[ _size - 1]; } int Size() const { TIXMLASSERT( _size >= 0 ); return _size; } int Capacity() const { TIXMLASSERT( _allocated >= INITIAL_SIZE ); return _allocated; } void SwapRemove(int i) { TIXMLASSERT(i >= 0 && i < _size); TIXMLASSERT(_size > 0); _mem[i] = _mem[_size - 1]; --_size; } const T* Mem() const { TIXMLASSERT( _mem ); return _mem; } T* Mem() { TIXMLASSERT( _mem ); return _mem; } private: DynArray( const DynArray& ); // not supported void operator=( const DynArray& ); // not supported void EnsureCapacity( int cap ) { TIXMLASSERT( cap > 0 ); if ( cap > _allocated ) { TIXMLASSERT( cap <= INT_MAX / 2 ); int newAllocated = cap * 2; T* newMem = new T[newAllocated]; TIXMLASSERT( newAllocated >= _size ); memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs if ( _mem != _pool ) { delete [] _mem; } _mem = newMem; _allocated = newAllocated; } } T* _mem; T _pool[INITIAL_SIZE]; int _allocated; // objects allocated int _size; // number objects in use }; /* Parent virtual class of a pool for fast allocation and deallocation of objects. */ class MemPool { public: MemPool() {} virtual ~MemPool() {} virtual int ItemSize() const = 0; virtual void* Alloc() = 0; virtual void Free( void* ) = 0; virtual void SetTracked() = 0; virtual void Clear() = 0; }; /* Template child class to create pools of the correct type. */ template< int ITEM_SIZE > class MemPoolT : public MemPool { public: MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} ~MemPoolT() { Clear(); } void Clear() { // Delete the blocks. while( !_blockPtrs.Empty()) { Block* lastBlock = _blockPtrs.Pop(); delete lastBlock; } _root = 0; _currentAllocs = 0; _nAllocs = 0; _maxAllocs = 0; _nUntracked = 0; } virtual int ItemSize() const { return ITEM_SIZE; } int CurrentAllocs() const { return _currentAllocs; } virtual void* Alloc() { if ( !_root ) { // Need a new block. Block* block = new Block(); _blockPtrs.Push( block ); Item* blockItems = block->items; for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { blockItems[i].next = &(blockItems[i + 1]); } blockItems[ITEMS_PER_BLOCK - 1].next = 0; _root = blockItems; } Item* const result = _root; TIXMLASSERT( result != 0 ); _root = _root->next; ++_currentAllocs; if ( _currentAllocs > _maxAllocs ) { _maxAllocs = _currentAllocs; } ++_nAllocs; ++_nUntracked; return result; } virtual void Free( void* mem ) { if ( !mem ) { return; } --_currentAllocs; Item* item = static_cast( mem ); #ifdef TINYXML2_DEBUG memset( item, 0xfe, sizeof( *item ) ); #endif item->next = _root; _root = item; } void Trace( const char* name ) { printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); } void SetTracked() { --_nUntracked; } int Untracked() const { return _nUntracked; } // This number is perf sensitive. 4k seems like a good tradeoff on my machine. // The test file is large, 170k. // Release: VS2010 gcc(no opt) // 1k: 4000 // 2k: 4000 // 4k: 3900 21000 // 16k: 5200 // 32k: 4300 // 64k: 4000 21000 // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK // in private part if ITEMS_PER_BLOCK is private enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; private: MemPoolT( const MemPoolT& ); // not supported void operator=( const MemPoolT& ); // not supported union Item { Item* next; char itemData[ITEM_SIZE]; }; struct Block { Item items[ITEMS_PER_BLOCK]; }; DynArray< Block*, 10 > _blockPtrs; Item* _root; int _currentAllocs; int _nAllocs; int _maxAllocs; int _nUntracked; }; /** Implements the interface to the "Visitor pattern" (see the Accept() method.) If you call the Accept() method, it requires being passed a XMLVisitor class to handle callbacks. For nodes that contain other nodes (Document, Element) you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs are simply called with Visit(). If you return 'true' from a Visit method, recursive parsing will continue. If you return false, no children of this node or its siblings will be visited. All flavors of Visit methods have a default implementation that returns 'true' (continue visiting). You need to only override methods that are interesting to you. Generally Accept() is called on the XMLDocument, although all nodes support visiting. You should never change the document from a callback. @sa XMLNode::Accept() */ class TINYXML2_LIB XMLVisitor { public: virtual ~XMLVisitor() {} /// Visit a document. virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { return true; } /// Visit a document. virtual bool VisitExit( const XMLDocument& /*doc*/ ) { return true; } /// Visit an element. virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { return true; } /// Visit an element. virtual bool VisitExit( const XMLElement& /*element*/ ) { return true; } /// Visit a declaration. virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { return true; } /// Visit a text node. virtual bool Visit( const XMLText& /*text*/ ) { return true; } /// Visit a comment node. virtual bool Visit( const XMLComment& /*comment*/ ) { return true; } /// Visit an unknown node. virtual bool Visit( const XMLUnknown& /*unknown*/ ) { return true; } }; // WARNING: must match XMLDocument::_errorNames[] enum XMLError { XML_SUCCESS = 0, XML_NO_ATTRIBUTE, XML_WRONG_ATTRIBUTE_TYPE, XML_ERROR_FILE_NOT_FOUND, XML_ERROR_FILE_COULD_NOT_BE_OPENED, XML_ERROR_FILE_READ_ERROR, UNUSED_XML_ERROR_ELEMENT_MISMATCH, // remove at next major version XML_ERROR_PARSING_ELEMENT, XML_ERROR_PARSING_ATTRIBUTE, UNUSED_XML_ERROR_IDENTIFYING_TAG, // remove at next major version XML_ERROR_PARSING_TEXT, XML_ERROR_PARSING_CDATA, XML_ERROR_PARSING_COMMENT, XML_ERROR_PARSING_DECLARATION, XML_ERROR_PARSING_UNKNOWN, XML_ERROR_EMPTY_DOCUMENT, XML_ERROR_MISMATCHED_ELEMENT, XML_ERROR_PARSING, XML_CAN_NOT_CONVERT_TEXT, XML_NO_TEXT_NODE, XML_ERROR_COUNT }; /* Utility functionality. */ class TINYXML2_LIB XMLUtil { public: static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) { TIXMLASSERT( p ); while( IsWhiteSpace(*p) ) { if (curLineNumPtr && *p == '\n') { ++(*curLineNumPtr); } ++p; } TIXMLASSERT( p ); return p; } static char* SkipWhiteSpace( char* p, int* curLineNumPtr ) { return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); } // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't // correct, but simple, and usually works. static bool IsWhiteSpace( char p ) { return !IsUTF8Continuation(p) && isspace( static_cast(p) ); } inline static bool IsNameStartChar( unsigned char ch ) { if ( ch >= 128 ) { // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() return true; } if ( isalpha( ch ) ) { return true; } return ch == ':' || ch == '_'; } inline static bool IsNameChar( unsigned char ch ) { return IsNameStartChar( ch ) || isdigit( ch ) || ch == '.' || ch == '-'; } inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { if ( p == q ) { return true; } TIXMLASSERT( p ); TIXMLASSERT( q ); TIXMLASSERT( nChar >= 0 ); return strncmp( p, q, nChar ) == 0; } inline static bool IsUTF8Continuation( char p ) { return ( p & 0x80 ) != 0; } static const char* ReadBOM( const char* p, bool* hasBOM ); // p is the starting location, // the UTF-8 value of the entity will be placed in value, and length filled in. static const char* GetCharacterRef( const char* p, char* value, int* length ); static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); // converts primitive types to strings static void ToStr( int v, char* buffer, int bufferSize ); static void ToStr( unsigned v, char* buffer, int bufferSize ); static void ToStr( bool v, char* buffer, int bufferSize ); static void ToStr( float v, char* buffer, int bufferSize ); static void ToStr( double v, char* buffer, int bufferSize ); static void ToStr(int64_t v, char* buffer, int bufferSize); // converts strings to primitive types static bool ToInt( const char* str, int* value ); static bool ToUnsigned( const char* str, unsigned* value ); static bool ToBool( const char* str, bool* value ); static bool ToFloat( const char* str, float* value ); static bool ToDouble( const char* str, double* value ); static bool ToInt64(const char* str, int64_t* value); // Changes what is serialized for a boolean value. // Default to "true" and "false". Shouldn't be changed // unless you have a special testing or compatibility need. // Be careful: static, global, & not thread safe. // Be sure to set static const memory as parameters. static void SetBoolSerialization(const char* writeTrue, const char* writeFalse); private: static const char* writeBoolTrue; static const char* writeBoolFalse; }; /** XMLNode is a base class for every object that is in the XML Document Object Model (DOM), except XMLAttributes. Nodes have siblings, a parent, and children which can be navigated. A node is always in a XMLDocument. The type of a XMLNode can be queried, and it can be cast to its more defined type. A XMLDocument allocates memory for all its Nodes. When the XMLDocument gets deleted, all its Nodes will also be deleted. @verbatim A Document can contain: Element (container or leaf) Comment (leaf) Unknown (leaf) Declaration( leaf ) An Element can contain: Element (container or leaf) Text (leaf) Attributes (not on tree) Comment (leaf) Unknown (leaf) @endverbatim */ class TINYXML2_LIB XMLNode { friend class XMLDocument; friend class XMLElement; public: /// Get the XMLDocument that owns this XMLNode. const XMLDocument* GetDocument() const { TIXMLASSERT( _document ); return _document; } /// Get the XMLDocument that owns this XMLNode. XMLDocument* GetDocument() { TIXMLASSERT( _document ); return _document; } /// Safely cast to an Element, or null. virtual XMLElement* ToElement() { return 0; } /// Safely cast to Text, or null. virtual XMLText* ToText() { return 0; } /// Safely cast to a Comment, or null. virtual XMLComment* ToComment() { return 0; } /// Safely cast to a Document, or null. virtual XMLDocument* ToDocument() { return 0; } /// Safely cast to a Declaration, or null. virtual XMLDeclaration* ToDeclaration() { return 0; } /// Safely cast to an Unknown, or null. virtual XMLUnknown* ToUnknown() { return 0; } virtual const XMLElement* ToElement() const { return 0; } virtual const XMLText* ToText() const { return 0; } virtual const XMLComment* ToComment() const { return 0; } virtual const XMLDocument* ToDocument() const { return 0; } virtual const XMLDeclaration* ToDeclaration() const { return 0; } virtual const XMLUnknown* ToUnknown() const { return 0; } /** The meaning of 'value' changes for the specific type. @verbatim Document: empty (NULL is returned, not an empty string) Element: name of the element Comment: the comment text Unknown: the tag contents Text: the text string @endverbatim */ const char* Value() const; /** Set the Value of an XML node. @sa Value() */ void SetValue( const char* val, bool staticMem=false ); /// Gets the line number the node is in, if the document was parsed from a file. int GetLineNum() const { return _parseLineNum; } /// Get the parent of this node on the DOM. const XMLNode* Parent() const { return _parent; } XMLNode* Parent() { return _parent; } /// Returns true if this node has no children. bool NoChildren() const { return !_firstChild; } /// Get the first child node, or null if none exists. const XMLNode* FirstChild() const { return _firstChild; } XMLNode* FirstChild() { return _firstChild; } /** Get the first child element, or optionally the first child element with the specified name. */ const XMLElement* FirstChildElement( const char* name = 0 ) const; XMLElement* FirstChildElement( const char* name = 0 ) { return const_cast(const_cast(this)->FirstChildElement( name )); } /// Get the last child node, or null if none exists. const XMLNode* LastChild() const { return _lastChild; } XMLNode* LastChild() { return _lastChild; } /** Get the last child element or optionally the last child element with the specified name. */ const XMLElement* LastChildElement( const char* name = 0 ) const; XMLElement* LastChildElement( const char* name = 0 ) { return const_cast(const_cast(this)->LastChildElement(name) ); } /// Get the previous (left) sibling node of this node. const XMLNode* PreviousSibling() const { return _prev; } XMLNode* PreviousSibling() { return _prev; } /// Get the previous (left) sibling element of this node, with an optionally supplied name. const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; XMLElement* PreviousSiblingElement( const char* name = 0 ) { return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); } /// Get the next (right) sibling node of this node. const XMLNode* NextSibling() const { return _next; } XMLNode* NextSibling() { return _next; } /// Get the next (right) sibling element of this node, with an optionally supplied name. const XMLElement* NextSiblingElement( const char* name = 0 ) const; XMLElement* NextSiblingElement( const char* name = 0 ) { return const_cast(const_cast(this)->NextSiblingElement( name ) ); } /** Add a child node as the last (right) child. If the child node is already part of the document, it is moved from its old location to the new location. Returns the addThis argument or 0 if the node does not belong to the same document. */ XMLNode* InsertEndChild( XMLNode* addThis ); XMLNode* LinkEndChild( XMLNode* addThis ) { return InsertEndChild( addThis ); } /** Add a child node as the first (left) child. If the child node is already part of the document, it is moved from its old location to the new location. Returns the addThis argument or 0 if the node does not belong to the same document. */ XMLNode* InsertFirstChild( XMLNode* addThis ); /** Add a node after the specified child node. If the child node is already part of the document, it is moved from its old location to the new location. Returns the addThis argument or 0 if the afterThis node is not a child of this node, or if the node does not belong to the same document. */ XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); /** Delete all the children of this node. */ void DeleteChildren(); /** Delete a child of this node. */ void DeleteChild( XMLNode* node ); /** Make a copy of this node, but not its children. You may pass in a Document pointer that will be the owner of the new Node. If the 'document' is null, then the node returned will be allocated from the current Document. (this->GetDocument()) Note: if called on a XMLDocument, this will return null. */ virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; /** Make a copy of this node and all its children. If the 'target' is null, then the nodes will be allocated in the current document. If 'target' is specified, the memory will be allocated is the specified XMLDocument. NOTE: This is probably not the correct tool to copy a document, since XMLDocuments can have multiple top level XMLNodes. You probably want to use XMLDocument::DeepCopy() */ XMLNode* DeepClone( XMLDocument* target ) const; /** Test if 2 nodes are the same, but don't test children. The 2 nodes do not need to be in the same Document. Note: if called on a XMLDocument, this will return false. */ virtual bool ShallowEqual( const XMLNode* compare ) const = 0; /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the XML tree will be conditionally visited and the host will be called back via the XMLVisitor interface. This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this interface versus any other.) The interface has been based on ideas from: - http://www.saxproject.org/ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern Which are both good references for "visiting". An example of using Accept(): @verbatim XMLPrinter printer; tinyxmlDoc.Accept( &printer ); const char* xmlcstr = printer.CStr(); @endverbatim */ virtual bool Accept( XMLVisitor* visitor ) const = 0; /** Set user data into the XMLNode. TinyXML-2 in no way processes or interprets user data. It is initially 0. */ void SetUserData(void* userData) { _userData = userData; } /** Get user data set into the XMLNode. TinyXML-2 in no way processes or interprets user data. It is initially 0. */ void* GetUserData() const { return _userData; } protected: XMLNode( XMLDocument* ); virtual ~XMLNode(); virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); XMLDocument* _document; XMLNode* _parent; mutable StrPair _value; int _parseLineNum; XMLNode* _firstChild; XMLNode* _lastChild; XMLNode* _prev; XMLNode* _next; void* _userData; private: MemPool* _memPool; void Unlink( XMLNode* child ); static void DeleteNode( XMLNode* node ); void InsertChildPreamble( XMLNode* insertThis ) const; const XMLElement* ToElementWithName( const char* name ) const; XMLNode( const XMLNode& ); // not supported XMLNode& operator=( const XMLNode& ); // not supported }; /** XML text. Note that a text node can have child element nodes, for example: @verbatim This is bold @endverbatim A text node can have 2 ways to output the next. "normal" output and CDATA. It will default to the mode it was parsed from the XML file and you generally want to leave it alone, but you can change the output mode with SetCData() and query it with CData(). */ class TINYXML2_LIB XMLText : public XMLNode { friend class XMLDocument; public: virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLText* ToText() { return this; } virtual const XMLText* ToText() const { return this; } /// Declare whether this should be CDATA or standard text. void SetCData( bool isCData ) { _isCData = isCData; } /// Returns true if this is a CDATA text element. bool CData() const { return _isCData; } virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} virtual ~XMLText() {} char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); private: bool _isCData; XMLText( const XMLText& ); // not supported XMLText& operator=( const XMLText& ); // not supported }; /** An XML Comment. */ class TINYXML2_LIB XMLComment : public XMLNode { friend class XMLDocument; public: virtual XMLComment* ToComment() { return this; } virtual const XMLComment* ToComment() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLComment( XMLDocument* doc ); virtual ~XMLComment(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); private: XMLComment( const XMLComment& ); // not supported XMLComment& operator=( const XMLComment& ); // not supported }; /** In correct XML the declaration is the first entry in the file. @verbatim @endverbatim TinyXML-2 will happily read or write files without a declaration, however. The text of the declaration isn't interpreted. It is parsed and written as a string. */ class TINYXML2_LIB XMLDeclaration : public XMLNode { friend class XMLDocument; public: virtual XMLDeclaration* ToDeclaration() { return this; } virtual const XMLDeclaration* ToDeclaration() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLDeclaration( XMLDocument* doc ); virtual ~XMLDeclaration(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); private: XMLDeclaration( const XMLDeclaration& ); // not supported XMLDeclaration& operator=( const XMLDeclaration& ); // not supported }; /** Any tag that TinyXML-2 doesn't recognize is saved as an unknown. It is a tag of text, but should not be modified. It will be written back to the XML, unchanged, when the file is saved. DTD tags get thrown into XMLUnknowns. */ class TINYXML2_LIB XMLUnknown : public XMLNode { friend class XMLDocument; public: virtual XMLUnknown* ToUnknown() { return this; } virtual const XMLUnknown* ToUnknown() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLUnknown( XMLDocument* doc ); virtual ~XMLUnknown(); char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); private: XMLUnknown( const XMLUnknown& ); // not supported XMLUnknown& operator=( const XMLUnknown& ); // not supported }; /** An attribute is a name-value pair. Elements have an arbitrary number of attributes, each with a unique name. @note The attributes are not XMLNodes. You may only query the Next() attribute in a list. */ class TINYXML2_LIB XMLAttribute { friend class XMLElement; public: /// The name of the attribute. const char* Name() const; /// The value of the attribute. const char* Value() const; /// Gets the line number the attribute is in, if the document was parsed from a file. int GetLineNum() const { return _parseLineNum; } /// The next attribute in the list. const XMLAttribute* Next() const { return _next; } /** IntValue interprets the attribute as an integer, and returns the value. If the value isn't an integer, 0 will be returned. There is no error checking; use QueryIntValue() if you need error checking. */ int IntValue() const { int i = 0; QueryIntValue(&i); return i; } int64_t Int64Value() const { int64_t i = 0; QueryInt64Value(&i); return i; } /// Query as an unsigned integer. See IntValue() unsigned UnsignedValue() const { unsigned i=0; QueryUnsignedValue( &i ); return i; } /// Query as a boolean. See IntValue() bool BoolValue() const { bool b=false; QueryBoolValue( &b ); return b; } /// Query as a double. See IntValue() double DoubleValue() const { double d=0; QueryDoubleValue( &d ); return d; } /// Query as a float. See IntValue() float FloatValue() const { float f=0; QueryFloatValue( &f ); return f; } /** QueryIntValue interprets the attribute as an integer, and returns the value in the provided parameter. The function will return XML_SUCCESS on success, and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. */ XMLError QueryIntValue( int* value ) const; /// See QueryIntValue XMLError QueryUnsignedValue( unsigned int* value ) const; /// See QueryIntValue XMLError QueryInt64Value(int64_t* value) const; /// See QueryIntValue XMLError QueryBoolValue( bool* value ) const; /// See QueryIntValue XMLError QueryDoubleValue( double* value ) const; /// See QueryIntValue XMLError QueryFloatValue( float* value ) const; /// Set the attribute to a string value. void SetAttribute( const char* value ); /// Set the attribute to value. void SetAttribute( int value ); /// Set the attribute to value. void SetAttribute( unsigned value ); /// Set the attribute to value. void SetAttribute(int64_t value); /// Set the attribute to value. void SetAttribute( bool value ); /// Set the attribute to value. void SetAttribute( double value ); /// Set the attribute to value. void SetAttribute( float value ); private: enum { BUF_SIZE = 200 }; XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {} virtual ~XMLAttribute() {} XMLAttribute( const XMLAttribute& ); // not supported void operator=( const XMLAttribute& ); // not supported void SetName( const char* name ); char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr ); mutable StrPair _name; mutable StrPair _value; int _parseLineNum; XMLAttribute* _next; MemPool* _memPool; }; /** The element is a container class. It has a value, the element name, and can contain other elements, text, comments, and unknowns. Elements also contain an arbitrary number of attributes. */ class TINYXML2_LIB XMLElement : public XMLNode { friend class XMLDocument; public: /// Get the name of an element (which is the Value() of the node.) const char* Name() const { return Value(); } /// Set the name of the element. void SetName( const char* str, bool staticMem=false ) { SetValue( str, staticMem ); } virtual XMLElement* ToElement() { return this; } virtual const XMLElement* ToElement() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. For example: @verbatim const char* value = ele->Attribute( "foo" ); @endverbatim The 'value' parameter is normally null. However, if specified, the attribute will only be returned if the 'name' and 'value' match. This allow you to write code: @verbatim if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); @endverbatim rather than: @verbatim if ( ele->Attribute( "foo" ) ) { if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); } @endverbatim */ const char* Attribute( const char* name, const char* value=0 ) const; /** Given an attribute name, IntAttribute() returns the value of the attribute interpreted as an integer. The default value will be returned if the attribute isn't present, or if there is an error. (For a method with error checking, see QueryIntAttribute()). */ int IntAttribute(const char* name, int defaultValue = 0) const; /// See IntAttribute() unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; /// See IntAttribute() int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; /// See IntAttribute() bool BoolAttribute(const char* name, bool defaultValue = false) const; /// See IntAttribute() double DoubleAttribute(const char* name, double defaultValue = 0) const; /// See IntAttribute() float FloatAttribute(const char* name, float defaultValue = 0) const; /** Given an attribute name, QueryIntAttribute() returns XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion can't be performed, or XML_NO_ATTRIBUTE if the attribute doesn't exist. If successful, the result of the conversion will be written to 'value'. If not successful, nothing will be written to 'value'. This allows you to provide default value: @verbatim int value = 10; QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 @endverbatim */ XMLError QueryIntAttribute( const char* name, int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryIntValue( value ); } /// See QueryIntAttribute() XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryUnsignedValue( value ); } /// See QueryIntAttribute() XMLError QueryInt64Attribute(const char* name, int64_t* value) const { const XMLAttribute* a = FindAttribute(name); if (!a) { return XML_NO_ATTRIBUTE; } return a->QueryInt64Value(value); } /// See QueryIntAttribute() XMLError QueryBoolAttribute( const char* name, bool* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryBoolValue( value ); } /// See QueryIntAttribute() XMLError QueryDoubleAttribute( const char* name, double* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryDoubleValue( value ); } /// See QueryIntAttribute() XMLError QueryFloatAttribute( const char* name, float* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryFloatValue( value ); } /// See QueryIntAttribute() XMLError QueryStringAttribute(const char* name, const char** value) const { const XMLAttribute* a = FindAttribute(name); if (!a) { return XML_NO_ATTRIBUTE; } *value = a->Value(); return XML_SUCCESS; } /** Given an attribute name, QueryAttribute() returns XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion can't be performed, or XML_NO_ATTRIBUTE if the attribute doesn't exist. It is overloaded for the primitive types, and is a generally more convenient replacement of QueryIntAttribute() and related functions. If successful, the result of the conversion will be written to 'value'. If not successful, nothing will be written to 'value'. This allows you to provide default value: @verbatim int value = 10; QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 @endverbatim */ int QueryAttribute( const char* name, int* value ) const { return QueryIntAttribute( name, value ); } int QueryAttribute( const char* name, unsigned int* value ) const { return QueryUnsignedAttribute( name, value ); } int QueryAttribute(const char* name, int64_t* value) const { return QueryInt64Attribute(name, value); } int QueryAttribute( const char* name, bool* value ) const { return QueryBoolAttribute( name, value ); } int QueryAttribute( const char* name, double* value ) const { return QueryDoubleAttribute( name, value ); } int QueryAttribute( const char* name, float* value ) const { return QueryFloatAttribute( name, value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, const char* value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, int value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, unsigned value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute(const char* name, int64_t value) { XMLAttribute* a = FindOrCreateAttribute(name); a->SetAttribute(value); } /// Sets the named attribute to value. void SetAttribute( const char* name, bool value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, double value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, float value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /** Delete an attribute. */ void DeleteAttribute( const char* name ); /// Return the first attribute in the list. const XMLAttribute* FirstAttribute() const { return _rootAttribute; } /// Query a specific attribute in the list. const XMLAttribute* FindAttribute( const char* name ) const; /** Convenience function for easy access to the text inside an element. Although easy and concise, GetText() is limited compared to getting the XMLText child and accessing it directly. If the first child of 'this' is a XMLText, the GetText() returns the character string of the Text node, else null is returned. This is a convenient method for getting the text of simple contained text: @verbatim This is text const char* str = fooElement->GetText(); @endverbatim 'str' will be a pointer to "This is text". Note that this function can be misleading. If the element foo was created from this XML: @verbatim This is text @endverbatim then the value of str would be null. The first child node isn't a text node, it is another element. From this XML: @verbatim This is text @endverbatim GetText() will return "This is ". */ const char* GetText() const; /** Convenience function for easy access to the text inside an element. Although easy and concise, SetText() is limited compared to creating an XMLText child and mutating it directly. If the first child of 'this' is a XMLText, SetText() sets its value to the given string, otherwise it will create a first child that is an XMLText. This is a convenient method for setting the text of simple contained text: @verbatim This is text fooElement->SetText( "Hullaballoo!" ); Hullaballoo! @endverbatim Note that this function can be misleading. If the element foo was created from this XML: @verbatim This is text @endverbatim then it will not change "This is text", but rather prefix it with a text element: @verbatim Hullaballoo!This is text @endverbatim For this XML: @verbatim @endverbatim SetText() will generate @verbatim Hullaballoo! @endverbatim */ void SetText( const char* inText ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( int value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( unsigned value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText(int64_t value); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( bool value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( double value ); /// Convenience method for setting text inside an element. See SetText() for important limitations. void SetText( float value ); /** Convenience method to query the value of a child text node. This is probably best shown by example. Given you have a document is this form: @verbatim 1 1.4 @endverbatim The QueryIntText() and similar functions provide a safe and easier way to get to the "value" of x and y. @verbatim int x = 0; float y = 0; // types of x and y are contrived for example const XMLElement* xElement = pointElement->FirstChildElement( "x" ); const XMLElement* yElement = pointElement->FirstChildElement( "y" ); xElement->QueryIntText( &x ); yElement->QueryFloatText( &y ); @endverbatim @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. */ XMLError QueryIntText( int* ival ) const; /// See QueryIntText() XMLError QueryUnsignedText( unsigned* uval ) const; /// See QueryIntText() XMLError QueryInt64Text(int64_t* uval) const; /// See QueryIntText() XMLError QueryBoolText( bool* bval ) const; /// See QueryIntText() XMLError QueryDoubleText( double* dval ) const; /// See QueryIntText() XMLError QueryFloatText( float* fval ) const; int IntText(int defaultValue = 0) const; /// See QueryIntText() unsigned UnsignedText(unsigned defaultValue = 0) const; /// See QueryIntText() int64_t Int64Text(int64_t defaultValue = 0) const; /// See QueryIntText() bool BoolText(bool defaultValue = false) const; /// See QueryIntText() double DoubleText(double defaultValue = 0) const; /// See QueryIntText() float FloatText(float defaultValue = 0) const; // internal: enum ElementClosingType { OPEN, // CLOSED, // CLOSING // }; ElementClosingType ClosingType() const { return _closingType; } virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ); private: XMLElement( XMLDocument* doc ); virtual ~XMLElement(); XMLElement( const XMLElement& ); // not supported void operator=( const XMLElement& ); // not supported XMLAttribute* FindAttribute( const char* name ) { return const_cast(const_cast(this)->FindAttribute( name )); } XMLAttribute* FindOrCreateAttribute( const char* name ); //void LinkAttribute( XMLAttribute* attrib ); char* ParseAttributes( char* p, int* curLineNumPtr ); static void DeleteAttribute( XMLAttribute* attribute ); XMLAttribute* CreateAttribute(); enum { BUF_SIZE = 200 }; ElementClosingType _closingType; // The attribute list is ordered; there is no 'lastAttribute' // because the list needs to be scanned for dupes before adding // a new attribute. XMLAttribute* _rootAttribute; }; enum Whitespace { PRESERVE_WHITESPACE, COLLAPSE_WHITESPACE }; /** A Document binds together all the functionality. It can be saved, loaded, and printed to the screen. All Nodes are connected and allocated to a Document. If the Document is deleted, all its Nodes are also deleted. */ class TINYXML2_LIB XMLDocument : public XMLNode { friend class XMLElement; // Gives access to SetError, but over-access for everything else. // Wishing C++ had "internal" scope. friend class XMLNode; friend class XMLText; friend class XMLComment; friend class XMLDeclaration; friend class XMLUnknown; public: /// constructor XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); ~XMLDocument(); virtual XMLDocument* ToDocument() { TIXMLASSERT( this == _document ); return this; } virtual const XMLDocument* ToDocument() const { TIXMLASSERT( this == _document ); return this; } /** Parse an XML file from a character string. Returns XML_SUCCESS (0) on success, or an errorID. You may optionally pass in the 'nBytes', which is the number of bytes which will be parsed. If not specified, TinyXML-2 will assume 'xml' points to a null terminated string. */ XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); /** Load an XML file from disk. Returns XML_SUCCESS (0) on success, or an errorID. */ XMLError LoadFile( const char* filename ); /** Load an XML file from disk. You are responsible for providing and closing the FILE*. NOTE: The file should be opened as binary ("rb") not text in order for TinyXML-2 to correctly do newline normalization. Returns XML_SUCCESS (0) on success, or an errorID. */ XMLError LoadFile( FILE* ); /** Save the XML file to disk. Returns XML_SUCCESS (0) on success, or an errorID. */ XMLError SaveFile( const char* filename, bool compact = false ); /** Save the XML file to disk. You are responsible for providing and closing the FILE*. Returns XML_SUCCESS (0) on success, or an errorID. */ XMLError SaveFile( FILE* fp, bool compact = false ); bool ProcessEntities() const { return _processEntities; } Whitespace WhitespaceMode() const { return _whitespaceMode; } /** Returns true if this document has a leading Byte Order Mark of UTF8. */ bool HasBOM() const { return _writeBOM; } /** Sets whether to write the BOM when writing the file. */ void SetBOM( bool useBOM ) { _writeBOM = useBOM; } /** Return the root element of DOM. Equivalent to FirstChildElement(). To get the first node, use FirstChild(). */ XMLElement* RootElement() { return FirstChildElement(); } const XMLElement* RootElement() const { return FirstChildElement(); } /** Print the Document. If the Printer is not provided, it will print to stdout. If you provide Printer, this can print to a file: @verbatim XMLPrinter printer( fp ); doc.Print( &printer ); @endverbatim Or you can use a printer to print to memory: @verbatim XMLPrinter printer; doc.Print( &printer ); // printer.CStr() has a const char* to the XML @endverbatim */ void Print( XMLPrinter* streamer=0 ) const; virtual bool Accept( XMLVisitor* visitor ) const; /** Create a new Element associated with this Document. The memory for the Element is managed by the Document. */ XMLElement* NewElement( const char* name ); /** Create a new Comment associated with this Document. The memory for the Comment is managed by the Document. */ XMLComment* NewComment( const char* comment ); /** Create a new Text associated with this Document. The memory for the Text is managed by the Document. */ XMLText* NewText( const char* text ); /** Create a new Declaration associated with this Document. The memory for the object is managed by the Document. If the 'text' param is null, the standard declaration is used.: @verbatim @endverbatim */ XMLDeclaration* NewDeclaration( const char* text=0 ); /** Create a new Unknown associated with this Document. The memory for the object is managed by the Document. */ XMLUnknown* NewUnknown( const char* text ); /** Delete a node associated with this document. It will be unlinked from the DOM. */ void DeleteNode( XMLNode* node ); void ClearError() { SetError(XML_SUCCESS, 0, 0); } /// Return true if there was an error parsing the document. bool Error() const { return _errorID != XML_SUCCESS; } /// Return the errorID. XMLError ErrorID() const { return _errorID; } const char* ErrorName() const; static const char* ErrorIDToName(XMLError errorID); /** Returns a "long form" error description. A hopefully helpful diagnostic with location, line number, and/or additional info. */ const char* ErrorStr() const; /// A (trivial) utility function that prints the ErrorStr() to stdout. void PrintError() const; /// Return the line where the error occurred, or zero if unknown. int ErrorLineNum() const { return _errorLineNum; } /// Clear the document, resetting it to the initial state. void Clear(); /** Copies this document to a target document. The target will be completely cleared before the copy. If you want to copy a sub-tree, see XMLNode::DeepClone(). NOTE: that the 'target' must be non-null. */ void DeepCopy(XMLDocument* target) const; // internal char* Identify( char* p, XMLNode** node ); // internal void MarkInUse(XMLNode*); virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { return 0; } virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { return false; } private: XMLDocument( const XMLDocument& ); // not supported void operator=( const XMLDocument& ); // not supported bool _writeBOM; bool _processEntities; XMLError _errorID; Whitespace _whitespaceMode; mutable StrPair _errorStr; int _errorLineNum; char* _charBuffer; int _parseCurLineNum; // Memory tracking does add some overhead. // However, the code assumes that you don't // have a bunch of unlinked nodes around. // Therefore it takes less memory to track // in the document vs. a linked list in the XMLNode, // and the performance is the same. DynArray _unlinked; MemPoolT< sizeof(XMLElement) > _elementPool; MemPoolT< sizeof(XMLAttribute) > _attributePool; MemPoolT< sizeof(XMLText) > _textPool; MemPoolT< sizeof(XMLComment) > _commentPool; static const char* _errorNames[XML_ERROR_COUNT]; void Parse(); void SetError( XMLError error, int lineNum, const char* format, ... ); template NodeType* CreateUnlinkedNode( MemPoolT& pool ); }; template inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& pool ) { TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() ); NodeType* returnNode = new (pool.Alloc()) NodeType( this ); TIXMLASSERT( returnNode ); returnNode->_memPool = &pool; _unlinked.Push(returnNode); return returnNode; } /** A XMLHandle is a class that wraps a node pointer with null checks; this is an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 DOM structure. It is a separate utility class. Take an example: @verbatim @endverbatim Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very easy to write a *lot* of code that looks like: @verbatim XMLElement* root = document.FirstChildElement( "Document" ); if ( root ) { XMLElement* element = root->FirstChildElement( "Element" ); if ( element ) { XMLElement* child = element->FirstChildElement( "Child" ); if ( child ) { XMLElement* child2 = child->NextSiblingElement( "Child" ); if ( child2 ) { // Finally do something useful. @endverbatim And that doesn't even cover "else" cases. XMLHandle addresses the verbosity of such code. A XMLHandle checks for null pointers so it is perfectly safe and correct to use: @verbatim XMLHandle docHandle( &document ); XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); if ( child2 ) { // do something useful @endverbatim Which is MUCH more concise and useful. It is also safe to copy handles - internally they are nothing more than node pointers. @verbatim XMLHandle handleCopy = handle; @endverbatim See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. */ class TINYXML2_LIB XMLHandle { public: /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. XMLHandle( XMLNode* node ) : _node( node ) { } /// Create a handle from a node. XMLHandle( XMLNode& node ) : _node( &node ) { } /// Copy constructor XMLHandle( const XMLHandle& ref ) : _node( ref._node ) { } /// Assignment XMLHandle& operator=( const XMLHandle& ref ) { _node = ref._node; return *this; } /// Get the first child of this handle. XMLHandle FirstChild() { return XMLHandle( _node ? _node->FirstChild() : 0 ); } /// Get the first child element of this handle. XMLHandle FirstChildElement( const char* name = 0 ) { return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); } /// Get the last child of this handle. XMLHandle LastChild() { return XMLHandle( _node ? _node->LastChild() : 0 ); } /// Get the last child element of this handle. XMLHandle LastChildElement( const char* name = 0 ) { return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); } /// Get the previous sibling of this handle. XMLHandle PreviousSibling() { return XMLHandle( _node ? _node->PreviousSibling() : 0 ); } /// Get the previous sibling element of this handle. XMLHandle PreviousSiblingElement( const char* name = 0 ) { return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); } /// Get the next sibling of this handle. XMLHandle NextSibling() { return XMLHandle( _node ? _node->NextSibling() : 0 ); } /// Get the next sibling element of this handle. XMLHandle NextSiblingElement( const char* name = 0 ) { return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); } /// Safe cast to XMLNode. This can return null. XMLNode* ToNode() { return _node; } /// Safe cast to XMLElement. This can return null. XMLElement* ToElement() { return ( _node ? _node->ToElement() : 0 ); } /// Safe cast to XMLText. This can return null. XMLText* ToText() { return ( _node ? _node->ToText() : 0 ); } /// Safe cast to XMLUnknown. This can return null. XMLUnknown* ToUnknown() { return ( _node ? _node->ToUnknown() : 0 ); } /// Safe cast to XMLDeclaration. This can return null. XMLDeclaration* ToDeclaration() { return ( _node ? _node->ToDeclaration() : 0 ); } private: XMLNode* _node; }; /** A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the same in all regards, except for the 'const' qualifiers. See XMLHandle for API. */ class TINYXML2_LIB XMLConstHandle { public: XMLConstHandle( const XMLNode* node ) : _node( node ) { } XMLConstHandle( const XMLNode& node ) : _node( &node ) { } XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) { } XMLConstHandle& operator=( const XMLConstHandle& ref ) { _node = ref._node; return *this; } const XMLConstHandle FirstChild() const { return XMLConstHandle( _node ? _node->FirstChild() : 0 ); } const XMLConstHandle FirstChildElement( const char* name = 0 ) const { return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); } const XMLConstHandle LastChild() const { return XMLConstHandle( _node ? _node->LastChild() : 0 ); } const XMLConstHandle LastChildElement( const char* name = 0 ) const { return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); } const XMLConstHandle PreviousSibling() const { return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); } const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); } const XMLConstHandle NextSibling() const { return XMLConstHandle( _node ? _node->NextSibling() : 0 ); } const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); } const XMLNode* ToNode() const { return _node; } const XMLElement* ToElement() const { return ( _node ? _node->ToElement() : 0 ); } const XMLText* ToText() const { return ( _node ? _node->ToText() : 0 ); } const XMLUnknown* ToUnknown() const { return ( _node ? _node->ToUnknown() : 0 ); } const XMLDeclaration* ToDeclaration() const { return ( _node ? _node->ToDeclaration() : 0 ); } private: const XMLNode* _node; }; /** Printing functionality. The XMLPrinter gives you more options than the XMLDocument::Print() method. It can: -# Print to memory. -# Print to a file you provide. -# Print XML without a XMLDocument. Print to Memory @verbatim XMLPrinter printer; doc.Print( &printer ); SomeFunction( printer.CStr() ); @endverbatim Print to a File You provide the file pointer. @verbatim XMLPrinter printer( fp ); doc.Print( &printer ); @endverbatim Print without a XMLDocument When loading, an XML parser is very useful. However, sometimes when saving, it just gets in the way. The code is often set up for streaming, and constructing the DOM is just overhead. The Printer supports the streaming case. The following code prints out a trivially simple XML file without ever creating an XML document. @verbatim XMLPrinter printer( fp ); printer.OpenElement( "foo" ); printer.PushAttribute( "foo", "bar" ); printer.CloseElement(); @endverbatim */ class TINYXML2_LIB XMLPrinter : public XMLVisitor { public: /** Construct the printer. If the FILE* is specified, this will print to the FILE. Else it will print to memory, and the result is available in CStr(). If 'compact' is set to true, then output is created with only required whitespace and newlines. */ XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); virtual ~XMLPrinter() {} /** If streaming, write the BOM and declaration. */ void PushHeader( bool writeBOM, bool writeDeclaration ); /** If streaming, start writing an element. The element must be closed with CloseElement() */ void OpenElement( const char* name, bool compactMode=false ); /// If streaming, add an attribute to an open element. void PushAttribute( const char* name, const char* value ); void PushAttribute( const char* name, int value ); void PushAttribute( const char* name, unsigned value ); void PushAttribute(const char* name, int64_t value); void PushAttribute( const char* name, bool value ); void PushAttribute( const char* name, double value ); /// If streaming, close the Element. virtual void CloseElement( bool compactMode=false ); /// Add a text node. void PushText( const char* text, bool cdata=false ); /// Add a text node from an integer. void PushText( int value ); /// Add a text node from an unsigned. void PushText( unsigned value ); /// Add a text node from an unsigned. void PushText(int64_t value); /// Add a text node from a bool. void PushText( bool value ); /// Add a text node from a float. void PushText( float value ); /// Add a text node from a double. void PushText( double value ); /// Add a comment void PushComment( const char* comment ); void PushDeclaration( const char* value ); void PushUnknown( const char* value ); virtual bool VisitEnter( const XMLDocument& /*doc*/ ); virtual bool VisitExit( const XMLDocument& /*doc*/ ) { return true; } virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); virtual bool VisitExit( const XMLElement& element ); virtual bool Visit( const XMLText& text ); virtual bool Visit( const XMLComment& comment ); virtual bool Visit( const XMLDeclaration& declaration ); virtual bool Visit( const XMLUnknown& unknown ); /** If in print to memory mode, return a pointer to the XML file in memory. */ const char* CStr() const { return _buffer.Mem(); } /** If in print to memory mode, return the size of the XML file in memory. (Note the size returned includes the terminating null.) */ int CStrSize() const { return _buffer.Size(); } /** If in print to memory mode, reset the buffer to the beginning. */ void ClearBuffer() { _buffer.Clear(); _buffer.Push(0); _firstElement = true; } protected: virtual bool CompactMode( const XMLElement& ) { return _compactMode; } /** Prints out the space before an element. You may override to change the space and tabs used. A PrintSpace() override should call Print(). */ virtual void PrintSpace( int depth ); void Print( const char* format, ... ); void Write( const char* data, size_t size ); inline void Write( const char* data ) { Write( data, strlen( data ) ); } void Putc( char ch ); void SealElementIfJustOpened(); bool _elementJustOpened; DynArray< const char*, 10 > _stack; private: void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. bool _firstElement; FILE* _fp; int _depth; int _textDepth; bool _processEntities; bool _compactMode; enum { ENTITY_RANGE = 64, BUF_SIZE = 200 }; bool _entityFlag[ENTITY_RANGE]; bool _restrictedEntityFlag[ENTITY_RANGE]; DynArray< char, 20 > _buffer; // Prohibit cloning, intentionally not implemented XMLPrinter( const XMLPrinter& ); XMLPrinter& operator=( const XMLPrinter& ); }; } // tinyxml2 #if defined(_MSC_VER) # pragma warning(pop) #endif #endif // TINYXML2_INCLUDED cppcheck-1.90/generate_coverage_report000077500000000000000000000010211357737443600202260ustar00rootroot00000000000000#!/bin/bash set -e make clean rm -rf coverage_report make test CXXFLAGS="-g -fprofile-arcs -ftest-coverage" test/cfg/runtests.sh gcov lib/*.cpp -o lib/ lcov --directory ./ --capture --output-file lcov_tmp.info -b ./ lcov --extract lcov_tmp.info "$(pwd)/*" --output-file lcov.info genhtml lcov.info -o coverage_report --frame --legend --demangle-cpp rm cli/*.gcda rm cli/*.gcno rm lib/*.gcda rm lib/*.gcno rm test/*.gcda rm test/*.gcno rm externals/tinyxml/*.gcda rm externals/tinyxml/*.gcno rm lcov.info lcov_tmp.info make clean cppcheck-1.90/gui/000077500000000000000000000000001357737443600140325ustar00rootroot00000000000000cppcheck-1.90/gui/CMakeLists.txt000066400000000000000000000032111357737443600165670ustar00rootroot00000000000000if (BUILD_GUI) set(CMAKE_AUTOMOC ON) if (${CMAKE_BUILD_TYPE} STREQUAL "Release") add_definitions(-DQT_NO_DEBUG) add_definitions(-DQT_NO_DEBUG_OUTPUT) add_definitions(-DQT_NO_WARNING_OUTPUT) else() add_definitions(-DQT_DEBUG) endif() include_directories(${PROJECT_SOURCE_DIR}/lib/) include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/externals/tinyxml/) file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") file(GLOB uis "*.ui") file(GLOB tss "*.ts") QT5_WRAP_UI(uis_hdrs ${uis}) QT5_ADD_RESOURCES(resources "gui.qrc") QT5_ADD_TRANSLATION(qms ${tss}) add_executable(cppcheck-gui ${hdrs} ${srcs} ${uis_hdrs} ${resources} ${qms} $ $ $) if (HAVE_RULES) target_link_libraries(cppcheck-gui pcre) endif() target_link_libraries(cppcheck-gui Qt5::Core Qt5::Gui Qt5::Widgets Qt5::PrintSupport) if(WITH_QCHART) target_compile_definitions (cppcheck-gui PRIVATE HAVE_QCHART ) target_link_libraries(cppcheck-gui Qt5::Charts) endif() install(TARGETS cppcheck-gui RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) install(FILES ${qms} DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) install(FILES cppcheck-gui.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) # icons install(FILES cppcheck-gui.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) install(FILES cppcheck-gui.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps) set(CMAKE_AUTOMOC OFF) endif() cppcheck-1.90/gui/about.ui000066400000000000000000000110531357737443600155030ustar00rootroot00000000000000 About 0 0 478 375 About Cppcheck :/cppcheck-gui.png Qt::Vertical 20 40 Qt::Horizontal 40 20 Version %1 Cppcheck - A tool for static C/C++ code analysis. true Copyright © 2007-2019 Cppcheck team. true This program is licensed under the terms of the GNU General Public License version 3 true Visit Cppcheck homepage at %1 true true <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> Qt::Horizontal QDialogButtonBox::Ok mButtons accepted() About accept() 248 254 157 274 mButtons rejected() About reject() 316 260 286 274 cppcheck-1.90/gui/aboutdialog.cpp000066400000000000000000000025561357737443600170400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "aboutdialog.h" AboutDialog::AboutDialog(const QString &version, const QString &extraVersion, QWidget *parent) : QDialog(parent) { mUI.setupUi(this); QString fmtVersion(version); if (!extraVersion.isEmpty()) { fmtVersion += " (" + extraVersion + ")"; } mUI.mVersion->setText(mUI.mVersion->text().arg(fmtVersion)); QString url = "http://cppcheck.net/"; mUI.mHomepage->setText(mUI.mHomepage->text().arg(url)); connect(mUI.mButtons, &QDialogButtonBox::accepted, this, &AboutDialog::accept); } cppcheck-1.90/gui/aboutdialog.h000066400000000000000000000022411357737443600164740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ABOUT_DIALOG_H #define ABOUT_DIALOG_H #include #include #include "ui_about.h" class QWidget; /// @addtogroup GUI /// @{ /** * @brief About dialog * */ class AboutDialog : public QDialog { Q_OBJECT public: AboutDialog(const QString &version, const QString &extraVersion, QWidget *parent = nullptr); private: Ui::About mUI; }; /// @} #endif // ABOUT_DIALOG_H cppcheck-1.90/gui/application.cpp000066400000000000000000000016751357737443600170520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "application.h" Application::Application(const QString &name, const QString &path, const QString ¶ms) : mName(name) , mPath(path) , mParameters(params) { } cppcheck-1.90/gui/application.h000066400000000000000000000056761357737443600165240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2016 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef APPLICATION_H #define APPLICATION_H #include /** * @brief A class containing information of the application to execute. * * Each application has a name and a path. Name is displayed to the user * and has no other meaning. It isn't used to start the application. * Path contains the full path to the application containing the executable name. * Parameters contains the command line arguments for the executable. * * User can also specify certain predefined strings to parameters. These strings * will be replaced with appropriate values concerning the error. Strings are: * (file) - Filename containing the error * (line) - Line number containing the error * (message) - Error message * (severity) - Error severity * * Example opening a file with Kate and make Kate scroll to the correct line. * Executable: kate * Parameters: -l(line) (file) */ class Application { public: Application() { } Application(const QString &name, const QString &path, const QString ¶ms); /** * @brief Get application name. * @return Application name. */ QString getName() const { return mName; } /** * @brief Get application path. * @return Application path. */ QString getPath() const { return mPath; } /** * @brief Get application command line parameters. * @return Application command line parameters. */ QString getParameters() const { return mParameters; } /** * @brief Set application name. * @param name Application name. */ void setName(const QString &name) { mName = name; } /** * @brief Set application path. * @param path Application path. */ void setPath(const QString &path) { mPath = path; } /** * @brief Set application command line parameters. * @param parameters Application command line parameters. */ void setParameters(const QString ¶meters) { mParameters = parameters; } private: /** * @brief Application's name */ QString mName; /** * @brief Application's path */ QString mPath; /** * @brief Application's parameters */ QString mParameters; }; #endif // APPLICATION_H cppcheck-1.90/gui/application.ui000066400000000000000000000125211357737443600166750ustar00rootroot00000000000000 ApplicationDialog Qt::WindowModal 0 0 569 471 0 0 Add an application 0 0 0 0 Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Qt::AutoText false Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true &Name: mName &Executable: mPath &Parameters: mParameters Qt::Horizontal 40 20 Browse Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok mName mPath mParameters mButtonBrowse mButtons mButtons accepted() ApplicationDialog accept() 248 254 157 274 mButtons rejected() ApplicationDialog reject() 316 260 286 274 cppcheck-1.90/gui/applicationdialog.cpp000066400000000000000000000057761357737443600202400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "applicationdialog.h" #include "application.h" #include "common.h" ApplicationDialog::ApplicationDialog(const QString &title, Application &app, QWidget *parent) : QDialog(parent), mApplication(app) { mUI.setupUi(this); connect(mUI.mButtonBrowse, &QPushButton::clicked, this, &ApplicationDialog::browse); connect(mUI.mButtons, &QDialogButtonBox::accepted, this, &ApplicationDialog::ok); connect(mUI.mButtons, &QDialogButtonBox::rejected, this, &ApplicationDialog::reject); mUI.mPath->setText(app.getPath()); mUI.mName->setText(app.getName()); mUI.mParameters->setText(app.getParameters()); setWindowTitle(title); adjustSize(); } ApplicationDialog::~ApplicationDialog() { //dtor } void ApplicationDialog::browse() { QString filter; #ifdef Q_OS_WIN // In Windows (almost) all executables have .exe extension // so it does not make sense to show everything. filter += tr("Executable files (*.exe);;All files(*.*)"); #endif // Q_OS_WIN QString selectedFile = QFileDialog::getOpenFileName(this, tr("Select viewer application"), getPath(SETTINGS_LAST_APP_PATH), filter); if (!selectedFile.isEmpty()) { setPath(SETTINGS_LAST_APP_PATH, selectedFile); QString path(QDir::toNativeSeparators(selectedFile)); mUI.mPath->setText(path); } } void ApplicationDialog::ok() { if (mUI.mName->text().isEmpty() || mUI.mPath->text().isEmpty()) { QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), tr("You must specify a name, a path and optionally parameters for the application!"), QMessageBox::Ok, this); msg.exec(); reject(); } else { // Convert possible native (Windows) path to internal presentation format mApplication.setName(mUI.mName->text()); mApplication.setPath(QDir::fromNativeSeparators(mUI.mPath->text())); mApplication.setParameters(mUI.mParameters->text()); accept(); } } cppcheck-1.90/gui/applicationdialog.h000066400000000000000000000035241357737443600176720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef APPLICATIONDIALOG_H #define APPLICATIONDIALOG_H #include #include #include "application.h" #include "ui_application.h" class QWidget; /// @addtogroup GUI /// @{ /** * @brief Dialog to edit a startable application. * User can open errors with user specified applications. This is a dialog * to modify/add an application to open errors with. * */ class ApplicationDialog : public QDialog { Q_OBJECT public: /** * @brief Constructor. * @param title Title for the dialog. * @param app Application definition. * @param parent Parent widget. */ ApplicationDialog(const QString &title, Application &app, QWidget *parent = nullptr); virtual ~ApplicationDialog(); protected slots: void ok(); /** * @brief Slot to browse for an application * */ void browse(); protected: /** * @brief UI from the Qt designer * */ Ui::ApplicationDialog mUI; private: /** * @brief Underlying Application */ Application& mApplication; }; /// @} #endif // APPLICATIONDIALOG_H cppcheck-1.90/gui/applicationlist.cpp000066400000000000000000000220361357737443600177400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "common.h" #include "applicationlist.h" #include "application.h" #include ApplicationList::ApplicationList(QObject *parent) : QObject(parent), mDefaultApplicationIndex(-1) { //ctor } ApplicationList::~ApplicationList() { clear(); } bool ApplicationList::loadSettings() { QSettings settings; QStringList names = settings.value(SETTINGS_APPLICATION_NAMES, QStringList()).toStringList(); QStringList paths = settings.value(SETTINGS_APPLICATION_PATHS, QStringList()).toStringList(); QStringList params = settings.value(SETTINGS_APPLICATION_PARAMS, QStringList()).toStringList(); int defapp = settings.value(SETTINGS_APPLICATION_DEFAULT, -1).toInt(); // Params will be empty first time starting with the new setting. // Return false and inform user about problem with application settings. bool succeeded = true; if (!names.empty() && !paths.empty() && params.empty()) { for (int i = 0; i < paths.length(); i++) params << QString(); succeeded = false; } if (names.empty() && paths.empty() && params.empty()) { #ifndef _WIN32 // use as default for gnome environments if (QFileInfo("/usr/bin/gedit").isExecutable()) { Application app; app.setName("gedit"); app.setPath("/usr/bin/gedit"); app.setParameters("+(line) (file)"); addApplication(app); defapp = 0; } checkAndAddApplication("/usr/bin/geany","geany","+(line) (file)"); checkAndAddApplication("/usr/bin/qtcreator","Qt Creator","-client (file):(line)"); // use as default for kde environments if (QFileInfo("/usr/bin/kate").isExecutable()) { Application app; app.setName("kate"); app.setPath("/usr/bin/kate"); app.setParameters("-l(line) (file)"); addApplication(app); defapp = 0; } #else if (findDefaultWindowsEditor()) { defapp = 0; } #endif } else if (names.size() == paths.size()) { for (int i = 0; i < names.size(); i++) { const Application app(names[i], paths[i], params[i]); addApplication(app); } } if (defapp == -1) mDefaultApplicationIndex = 0; else if (defapp < names.size()) mDefaultApplicationIndex = defapp; else mDefaultApplicationIndex = 0; return succeeded; } void ApplicationList::saveSettings() const { QSettings settings; QStringList names; QStringList paths; QStringList params; for (int i = 0; i < getApplicationCount(); i++) { const Application& app = getApplication(i); names << app.getName(); paths << app.getPath(); params << app.getParameters(); } settings.setValue(SETTINGS_APPLICATION_NAMES, names); settings.setValue(SETTINGS_APPLICATION_PATHS, paths); settings.setValue(SETTINGS_APPLICATION_PARAMS, params); settings.setValue(SETTINGS_APPLICATION_DEFAULT, mDefaultApplicationIndex); } int ApplicationList::getApplicationCount() const { return mApplications.size(); } Application& ApplicationList::getApplication(const int index) { if (index >= 0 && index < mApplications.size()) { return mApplications[index]; } static Application dummy; // TODO: Throw exception instead? return dummy; } const Application& ApplicationList::getApplication(const int index) const { if (index >= 0 && index < mApplications.size()) { return mApplications[index]; } static const Application dummy; // TODO: Throw exception instead? return dummy; } void ApplicationList::addApplication(const Application &app) { if (app.getName().isEmpty() || app.getPath().isEmpty()) { return; } mApplications << app; } void ApplicationList::removeApplication(const int index) { mApplications.removeAt(index); } void ApplicationList::setDefault(const int index) { if (index < mApplications.size() && index >= 0) { mDefaultApplicationIndex = index; } } void ApplicationList::copy(const ApplicationList *list) { if (!list) { return; } clear(); for (int i = 0; i < list->getApplicationCount(); i++) { const Application& app = list->getApplication(i); addApplication(app); } mDefaultApplicationIndex = list->getDefaultApplication(); } void ApplicationList::clear() { mApplications.clear(); mDefaultApplicationIndex = -1; } bool ApplicationList::checkAndAddApplication(QString appPath, QString name, QString parameters) { if (QFileInfo(appPath).exists() && QFileInfo(appPath).isExecutable()) { Application app; app.setName(name); app.setPath("\"" + appPath + "\""); app.setParameters(parameters); addApplication(app); return true; } return false; } bool ApplicationList::findDefaultWindowsEditor() { bool foundOne = false; #ifdef WIN64 // As long as we do support 32-bit XP, we cannot be sure that the environment variable "ProgramFiles(x86)" exists const QString appPathx86(getenv("ProgramFiles(x86)")); #else const QString appPathx86(getenv("ProgramFiles")); #endif const QString appPathx64(getenv("ProgramW6432")); const QString windowsPath(getenv("windir")); if (checkAndAddApplication(appPathx86 + "\\Notepad++\\notepad++.exe", "Notepad++", "-n(line) (file)")) foundOne = true; else if (checkAndAddApplication(appPathx64 + "\\Notepad++\\notepad++.exe", "Notepad++", "-n(line) (file)")) foundOne = true; if (checkAndAddApplication(appPathx86 + "\\Notepad2\\Notepad2.exe", "Notepad2", "/g (line) (file)")) foundOne = true; else if (checkAndAddApplication(appPathx64 + "\\Notepad2\\Notepad2.exe", "Notepad2", "/g (line) (file)")) foundOne = true; if (checkAndAddApplication(windowsPath + "\\system32\\notepad.exe", "Notepad", "(file)")) foundOne = true; QString regPath = "HKEY_CLASSES_ROOT\\Applications\\QtProject.QtCreator.pro\\shell\\Open\\command"; QSettings registry(regPath, QSettings::NativeFormat); QString qtCreatorRegistry = registry.value("Default", QString()).toString(); QString qtCreatorPath = qtCreatorRegistry.left(qtCreatorRegistry.indexOf(".exe") + 4); if (!qtCreatorRegistry.isEmpty() && checkAndAddApplication(qtCreatorPath, "Qt Creator", "-client (file):(line)")) { foundOne = true; } const QString regPathUEdit32 = "HKEY_CLASSES_ROOT\\Applications\\Uedit32.exe\\shell\\open\\Command"; const QSettings registryUEdit32(regPathUEdit32, QSettings::NativeFormat); const QString uedit32Registry = registryUEdit32.value("Default", QString()).toString(); if (!uedit32Registry.isEmpty()) { // Extract path to executable and make sure there is no single quotation mark at the beginning const QString uedit32Path = uedit32Registry.left(uedit32Registry.indexOf(".exe") + 4).replace("\"", ""); if (checkAndAddApplication(uedit32Path, "UltraEdit 32", "(file)/(line)")) { foundOne = true; } } const QString regPathUEdit64 = "HKEY_CLASSES_ROOT\\Applications\\uedit64.exe\\shell\\open\\Command"; const QSettings registryUEdit64(regPathUEdit64, QSettings::NativeFormat); const QString uedit64Registry = registryUEdit64.value("Default", QString()).toString(); if (!uedit64Registry.isEmpty()) { // Extract path to executable and make sure there is no single quotation mark at the beginning const QString uedit64Path = uedit64Registry.left(uedit64Registry.indexOf(".exe") + 4).replace("\"", ""); if (checkAndAddApplication(uedit64Path, "UltraEdit 64", "(file)/(line)")) { foundOne = true; } } const QString regPathMSVSCode = "HKEY_CLASSES_ROOT\\Applications\\Code.exe\\shell\\open\\command"; const QSettings registryMSVSCode(regPathMSVSCode, QSettings::NativeFormat); const QString msvscodeRegistry = registryMSVSCode.value("Default", QString()).toString(); if (!msvscodeRegistry.isEmpty()) { const QString msvscodePath = msvscodeRegistry.left(msvscodeRegistry.indexOf(".exe") + 4).replace("\"", ""); if (checkAndAddApplication(msvscodePath, "Microsoft VS Code", "-g (file):(line)")) { foundOne = true; } } return foundOne; } cppcheck-1.90/gui/applicationlist.h000066400000000000000000000064311357737443600174060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef APPLICATIONLIST_H #define APPLICATIONLIST_H #include #include "application.h" /// @addtogroup GUI /// @{ /** * @brief List of applications user has specified to open errors with. */ class ApplicationList : public QObject { Q_OBJECT public: explicit ApplicationList(QObject *parent = nullptr); virtual ~ApplicationList(); /** * @brief Load all applications * * @return true if loading succeeded, false if there is problem with * application list. Most probably because of older version settings need * to be upgraded. */ bool loadSettings(); /** * @brief Save all applications */ void saveSettings() const; /** * @brief Get the amount of applications in the list * @return The count of applications */ int getApplicationCount() const; /** * @brief Get specific application's name * * @param index Index of the application whose name to get * @return Name of the application */ const Application& getApplication(const int index) const; Application& getApplication(const int index); /** * @brief Return the default application. * @return Index of the default application. */ int getDefaultApplication() const { return mDefaultApplicationIndex; } /** * @brief Add a new application * * @param app Application to add. */ void addApplication(const Application &app); /** * @brief Remove an application from the list * * @param index Index of the application to remove. */ void removeApplication(const int index); /** * @brief Set application as default application. * @param index Index of the application to make the default one */ void setDefault(const int index); /** * @brief Remove all applications from this list and copy all applications from * list given as a parameter. * @param list Copying source */ void copy(const ApplicationList *list); protected: /** * @brief Clear the list * */ void clear(); /** * @brief Find editor used by default in Windows. * Check if Notepad++ is installed and use it. If not, use Notepad. */ bool findDefaultWindowsEditor(); private: bool checkAndAddApplication(QString appPath, QString name, QString parameters); /** * @brief List of applications * */ QList mApplications; /** * @brief Index of the default application. * */ int mDefaultApplicationIndex; }; /// @} #endif // APPLICATIONLIST_H cppcheck-1.90/gui/checkstatistics.cpp000066400000000000000000000061761357737443600177400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "checkstatistics.h" CheckStatistics::CheckStatistics(QObject *parent) : QObject(parent) { clear(); } static void addItem(QMap &m, const QString &key) { if (m.contains(key)) m[key]++; else m[key] = 0; } void CheckStatistics::addItem(const QString &tool, ShowTypes::ShowType type) { const QString lower = tool.toLower(); switch (type) { case ShowTypes::ShowStyle: ::addItem(mStyle, tool); break; case ShowTypes::ShowWarnings: ::addItem(mWarning, tool); break; case ShowTypes::ShowPerformance: ::addItem(mPerformance, tool); break; case ShowTypes::ShowPortability: ::addItem(mPortability, tool); break; case ShowTypes::ShowErrors: ::addItem(mError, tool); break; case ShowTypes::ShowInformation: ::addItem(mInformation, tool); break; case ShowTypes::ShowNone: default: qDebug() << "Unknown error type - not added to statistics."; break; } } void CheckStatistics::clear() { mStyle.clear(); mWarning.clear(); mPerformance.clear(); mPortability.clear(); mInformation.clear(); mError.clear(); } unsigned CheckStatistics::getCount(const QString &tool, ShowTypes::ShowType type) const { const QString lower = tool.toLower(); switch (type) { case ShowTypes::ShowStyle: return mStyle.value(lower,0); case ShowTypes::ShowWarnings: return mWarning.value(lower,0); case ShowTypes::ShowPerformance: return mPerformance.value(lower,0); case ShowTypes::ShowPortability: return mPortability.value(lower,0); case ShowTypes::ShowErrors: return mError.value(lower,0); case ShowTypes::ShowInformation: return mInformation.value(lower,0); case ShowTypes::ShowNone: default: qDebug() << "Unknown error type - returning zero statistics."; return 0; } } QStringList CheckStatistics::getTools() const { QSet ret; foreach (QString tool, mStyle.keys()) ret.insert(tool); foreach (QString tool, mWarning.keys()) ret.insert(tool); foreach (QString tool, mPerformance.keys()) ret.insert(tool); foreach (QString tool, mPortability.keys()) ret.insert(tool); foreach (QString tool, mError.keys()) ret.insert(tool); return QStringList(ret.toList()); } cppcheck-1.90/gui/checkstatistics.h000066400000000000000000000036131357737443600173760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CHECKSTATISTICS_H #define CHECKSTATISTICS_H #include #include "showtypes.h" /// @addtogroup GUI /// @{ /** * A class for check statistics. */ class CheckStatistics : public QObject { public: explicit CheckStatistics(QObject *parent = nullptr); /** * @brief Add new checked item to statistics. * * @param tool Tool. * @param type Type of the item to add. */ void addItem(const QString &tool, ShowTypes::ShowType type); /** * @brief Clear the statistics. * */ void clear(); /** * @brief Return statistics for given type. * * @param tool Tool. * @param type Type for which the statistics are returned. * @return Number of items of given type. */ unsigned getCount(const QString &tool, ShowTypes::ShowType type) const; /** Get tools with results */ QStringList getTools() const; private: QMap mStyle; QMap mWarning; QMap mPerformance; QMap mPortability; QMap mInformation; QMap mError; }; /// @} #endif // CHECKSTATISTICS_H cppcheck-1.90/gui/checkthread.cpp000066400000000000000000000511651357737443600170130ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "checkthread.h" #include "erroritem.h" #include "threadresult.h" #include "cppcheck.h" #include "common.h" CheckThread::CheckThread(ThreadResult &result) : mState(Ready), mResult(result), mCppcheck(result, true), mAnalyseWholeProgram(false) { //ctor } CheckThread::~CheckThread() { //dtor } void CheckThread::check(const Settings &settings) { mFiles.clear(); mCppcheck.settings() = settings; start(); } void CheckThread::analyseWholeProgram(const QStringList &files) { mFiles = files; mAnalyseWholeProgram = true; start(); } void CheckThread::run() { mState = Running; if (!mFiles.isEmpty() || mAnalyseWholeProgram) { mAnalyseWholeProgram = false; qDebug() << "Whole program analysis"; const std::string &buildDir = mCppcheck.settings().buildDir; if (!buildDir.empty()) { std::map files2; for (QString file : mFiles) files2[file.toStdString()] = 0; mCppcheck.analyseWholeProgram(buildDir, files2); } mFiles.clear(); emit done(); return; } QString file = mResult.getNextFile(); while (!file.isEmpty() && mState == Running) { qDebug() << "Checking file" << file; mCppcheck.check(file.toStdString()); runAddonsAndTools(nullptr, file); emit fileChecked(file); if (mState == Running) file = mResult.getNextFile(); } ImportProject::FileSettings fileSettings = mResult.getNextFileSettings(); while (!fileSettings.filename.empty() && mState == Running) { file = QString::fromStdString(fileSettings.filename); qDebug() << "Checking file" << file; mCppcheck.check(fileSettings); runAddonsAndTools(&fileSettings, QString::fromStdString(fileSettings.filename)); emit fileChecked(file); if (mState == Running) fileSettings = mResult.getNextFileSettings(); } if (mState == Running) mState = Ready; else mState = Stopped; emit done(); } void CheckThread::runAddonsAndTools(const ImportProject::FileSettings *fileSettings, const QString &fileName) { QString dumpFile; foreach (const QString addon, mAddonsAndTools) { if (addon == CLANG_ANALYZER || addon == CLANG_TIDY) { if (!fileSettings) continue; if (!fileSettings->cfg.empty() && fileSettings->cfg.compare(0,5,"Debug") != 0) continue; QStringList args; for (std::list::const_iterator incIt = fileSettings->includePaths.begin(); incIt != fileSettings->includePaths.end(); ++incIt) args << ("-I" + QString::fromStdString(*incIt)); for (std::list::const_iterator i = fileSettings->systemIncludePaths.begin(); i != fileSettings->systemIncludePaths.end(); ++i) args << "-isystem" << QString::fromStdString(*i); foreach (QString def, QString::fromStdString(fileSettings->defines).split(";")) { args << ("-D" + def); } foreach (const std::string& U, fileSettings->undefs) { args << QString::fromStdString("-U" + U); } const QString clangPath = CheckThread::clangTidyCmd(); if (!clangPath.isEmpty()) { QDir dir(clangPath + "/../lib/clang"); foreach (QString ver, dir.entryList()) { QString includePath = dir.absolutePath() + '/' + ver + "/include"; if (ver[0] != '.' && QDir(includePath).exists()) { args << "-isystem" << includePath; break; } } } #ifdef Q_OS_WIN // To create compile_commands.json in windows see: // https://bitsmaker.gitlab.io/post/clang-tidy-from-vs2015/ foreach (QString includePath, mClangIncludePaths) { if (!includePath.isEmpty()) { includePath.replace("\\", "/"); args << "-isystem" << includePath.trimmed(); } } args << "-U__STDC__" << "-fno-ms-compatibility"; #endif if (!fileSettings->standard.empty()) args << ("-std=" + QString::fromStdString(fileSettings->standard)); else { switch (mCppcheck.settings().standards.cpp) { case Standards::CPP03: args << "-std=c++03"; break; case Standards::CPP11: args << "-std=c++11"; break; case Standards::CPP14: args << "-std=c++14"; break; case Standards::CPP17: args << "-std=c++17"; break; case Standards::CPP20: args << "-std=c++20"; break; } } QString analyzerInfoFile; const std::string &buildDir = mCppcheck.settings().buildDir; if (!buildDir.empty()) { analyzerInfoFile = QString::fromStdString(AnalyzerInformation::getAnalyzerInfoFile(buildDir, fileSettings->filename, fileSettings->cfg)); QStringList args2(args); args2.insert(0,"-E"); args2 << fileName; QProcess process; process.start(clangCmd(),args2); process.waitForFinished(); const QByteArray &ba = process.readAllStandardOutput(); const quint16 chksum = qChecksum(ba.data(), ba.length()); QFile f1(analyzerInfoFile + '.' + addon + "-E"); if (f1.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in1(&f1); const quint16 oldchksum = in1.readAll().toInt(); if (oldchksum == chksum) { QFile f2(analyzerInfoFile + '.' + addon + "-results"); if (f2.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream in2(&f2); parseClangErrors(addon, fileName, in2.readAll()); continue; } } f1.close(); } f1.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out1(&f1); out1 << chksum; QFile::remove(analyzerInfoFile + '.' + addon + "-results"); } if (addon == CLANG_ANALYZER) { /* // Using clang args.insert(0,"--analyze"); args.insert(1, "-Xanalyzer"); args.insert(2, "-analyzer-output=text"); args << fileName; */ // Using clang-tidy args.insert(0,"-checks=-*,clang-analyzer-*"); args.insert(1, fileName); args.insert(2, "--"); } else { args.insert(0,"-checks=*,-clang-analyzer-*,-llvm*"); args.insert(1, fileName); args.insert(2, "--"); } { const QString cmd(clangTidyCmd()); QString debug(cmd.contains(" ") ? ('\"' + cmd + '\"') : cmd); foreach (QString arg, args) { if (arg.contains(" ")) debug += " \"" + arg + '\"'; else debug += ' ' + arg; } qDebug() << debug; if (!analyzerInfoFile.isEmpty()) { QFile f(analyzerInfoFile + '.' + addon + "-cmd"); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&f); out << debug; } } } QProcess process; process.start(clangTidyCmd(), args); process.waitForFinished(600*1000); const QString errout(process.readAllStandardOutput() + "\n\n\n" + process.readAllStandardError()); if (!analyzerInfoFile.isEmpty()) { QFile f(analyzerInfoFile + '.' + addon + "-results"); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&f); out << errout; } } parseClangErrors(addon, fileName, errout); } else { const QString python = CheckThread::pythonCmd(); if (python.isEmpty()) continue; const QString addonFilePath = CheckThread::getAddonFilePath(mDataDir, addon + ".py"); if (addonFilePath.isEmpty()) continue; if (dumpFile.isEmpty()) { const std::string buildDir = mCppcheck.settings().buildDir; mCppcheck.settings().buildDir.clear(); mCppcheck.settings().dump = true; if (!buildDir.empty()) { mCppcheck.settings().dumpFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir, fileName.toStdString(), fileSettings ? fileSettings->cfg : std::string()) + ".dump"; dumpFile = QString::fromStdString(mCppcheck.settings().dumpFile); } else { dumpFile = fileName + ".dump"; } if (fileSettings) mCppcheck.check(*fileSettings); else mCppcheck.check(fileName.toStdString()); mCppcheck.settings().dump = false; mCppcheck.settings().dumpFile.clear(); mCppcheck.settings().buildDir = buildDir; } QStringList args; args << addonFilePath << "--cli" << dumpFile; if (addon == "misra" && !mMisraFile.isEmpty() && QFileInfo(mMisraFile).exists()) { if (mMisraFile.endsWith(".pdf", Qt::CaseInsensitive)) args << "--misra-pdf=" + mMisraFile; else args << "--rule-texts=" + mMisraFile; } qDebug() << python << args; QProcess process; QProcessEnvironment env = process.processEnvironment(); if (!env.contains("PYTHONHOME") && !python.startsWith("python")) { env.insert("PYTHONHOME", QFileInfo(python).canonicalPath()); process.setProcessEnvironment(env); } process.start(python, args); if (!process.waitForFinished()) { const QString errMsg("ERROR: Process '" + python + " " + args.join(" ") + "' did not finish successfully: " + process.errorString()); qWarning() << errMsg; mResult.reportOut(errMsg.toStdString()); } const QByteArray errout = process.readAllStandardError(); if (process.exitCode() != 0 && !errout.isEmpty()) { const QString errMsg("ERROR: Process '" + python + " " + args.join(" ") + "' failed with error code " + QString::number(process.exitCode()) + ": '" + process.errorString() + "'\nError output: " + errout); qWarning() << errMsg; mResult.reportOut(errMsg.toStdString()); } const QString output(process.readAllStandardOutput()); QFile f(dumpFile + '-' + addon + "-results"); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&f); out << output; f.close(); } parseAddonErrors(output, addon); } } if (!dumpFile.isEmpty()) { QFile::remove(dumpFile); } } void CheckThread::stop() { mState = Stopping; mCppcheck.terminate(); } void CheckThread::parseAddonErrors(QString err, const QString &tool) { Q_UNUSED(tool) QTextStream in(&err, QIODevice::ReadOnly); while (!in.atEnd()) { QString line = in.readLine(); if (!line.startsWith("{")) continue; const QJsonDocument doc = QJsonDocument::fromJson(line.toLocal8Bit()); const QJsonObject obj = doc.object(); /* msg = { 'file': location.file, 'linenr': location.linenr, 'column': location.column, 'severity': severity, 'message': message, 'addon': addon, 'errorId': errorId, 'extra': extra} */ const std::string &filename = obj["file"].toString().toStdString(); const int lineNumber = obj["linenr"].toInt(); const int column = obj["column"].toInt(); const std::string severity = obj["severity"].toString().toStdString(); const std::string message = obj["message"].toString().toStdString(); const std::string id = (obj["addon"].toString() + "-" + obj["errorId"].toString()).toStdString(); std::list callstack; callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(filename, lineNumber, column)); ErrorLogger::ErrorMessage errmsg(callstack, filename, Severity::fromString(severity), message, id, false); if (isSuppressed(errmsg.toSuppressionsErrorMessage())) continue; mResult.reportErr(errmsg); } } void CheckThread::parseClangErrors(const QString &tool, const QString &file0, QString err) { QList errorItems; ErrorItem errorItem; QRegExp r1("(.+):([0-9]+):([0-9]+): (note|warning|error|fatal error): (.*)"); QRegExp r2("(.*)\\[([a-zA-Z0-9\\-_\\.]+)\\]"); QTextStream in(&err, QIODevice::ReadOnly); while (!in.atEnd()) { QString line = in.readLine(); if (line.startsWith("Assertion failed:")) { ErrorItem e; e.errorPath.append(QErrorPathItem()); e.errorPath.last().file = file0; e.errorPath.last().line = 1; e.errorPath.last().column = 1; e.errorId = tool + "-internal-error"; e.file0 = file0; e.message = line; e.severity = Severity::information; errorItems.append(e); continue; } if (!r1.exactMatch(line)) continue; if (r1.cap(4) != "note") { errorItems.append(errorItem); errorItem = ErrorItem(); errorItem.file0 = r1.cap(1); } errorItem.errorPath.append(QErrorPathItem()); errorItem.errorPath.last().file = r1.cap(1); errorItem.errorPath.last().line = r1.cap(2).toInt(); errorItem.errorPath.last().column = r1.cap(3).toInt(); if (r1.cap(4) == "warning") errorItem.severity = Severity::SeverityType::warning; else if (r1.cap(4) == "error" || r1.cap(4) == "fatal error") errorItem.severity = Severity::SeverityType::error; QString message,id; if (r2.exactMatch(r1.cap(5))) { message = r2.cap(1); const QString id1(r2.cap(2)); if (id1.startsWith("clang")) id = id1; else id = tool + '-' + r2.cap(2); if (tool == CLANG_TIDY) { if (id1.startsWith("performance")) errorItem.severity = Severity::SeverityType::performance; else if (id1.startsWith("portability")) errorItem.severity = Severity::SeverityType::portability; else if (id1.startsWith("cert") || (id1.startsWith("misc") && !id1.contains("unused"))) errorItem.severity = Severity::SeverityType::warning; else errorItem.severity = Severity::SeverityType::style; } } else { message = r1.cap(5); id = CLANG_ANALYZER; } if (errorItem.errorPath.size() == 1) { errorItem.message = message; errorItem.errorId = id; } errorItem.errorPath.last().info = message; } errorItems.append(errorItem); foreach (const ErrorItem &e, errorItems) { if (e.errorPath.isEmpty()) continue; Suppressions::ErrorMessage errorMessage; errorMessage.setFileName(e.errorPath.back().file.toStdString()); errorMessage.lineNumber = e.errorPath.back().line; errorMessage.errorId = e.errorId.toStdString(); errorMessage.symbolNames = e.symbolNames.toStdString(); if (isSuppressed(errorMessage)) continue; std::list callstack; foreach (const QErrorPathItem &path, e.errorPath) { callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(path.file.toStdString(), path.info.toStdString(), path.line, path.column)); } const std::string f0 = file0.toStdString(); const std::string msg = e.message.toStdString(); const std::string id = e.errorId.toStdString(); ErrorLogger::ErrorMessage errmsg(callstack, f0, e.severity, msg, id, false); mResult.reportErr(errmsg); } } bool CheckThread::isSuppressed(const Suppressions::ErrorMessage &errorMessage) const { foreach (const Suppressions::Suppression &suppression, mSuppressions) { if (suppression.isSuppressed(errorMessage)) return true; } return false; } QString CheckThread::clangCmd() { QString path = QSettings().value(SETTINGS_CLANG_PATH,QString()).toString(); if (!path.isEmpty()) path += '/'; path += "clang"; #ifdef Q_OS_WIN path += ".exe"; #endif QProcess process; process.start(path, QStringList() << "--version"); process.waitForFinished(); if (process.exitCode() == 0) return path; #ifdef Q_OS_WIN // Try to autodetect clang if (QFileInfo("C:/Program Files/LLVM/bin/clang.exe").exists()) return "C:/Program Files/LLVM/bin/clang.exe"; #endif return QString(); } QString CheckThread::clangTidyCmd() { QString path = QSettings().value(SETTINGS_CLANG_PATH,QString()).toString(); if (!path.isEmpty()) path += '/'; path += "clang-tidy"; #ifdef Q_OS_WIN path += ".exe"; #endif QProcess process; process.start(path, QStringList() << "--version"); process.waitForFinished(); if (process.exitCode() == 0) return path; #ifdef Q_OS_WIN // Try to autodetect clang-tidy if (QFileInfo("C:/Program Files/LLVM/bin/clang-tidy.exe").exists()) return "C:/Program Files/LLVM/bin/clang-tidy.exe"; #endif return QString(); } QString CheckThread::pythonCmd() { QString path = QSettings().value(SETTINGS_PYTHON_PATH).toString(); if (!path.isEmpty()) return path; path = "python"; #ifdef Q_OS_WIN path += ".exe"; #endif QProcess process; process.start(path, QStringList() << "--version"); process.waitForFinished(); if (process.exitCode() == 0) return path; return QString(); } QString CheckThread::getAddonFilePath(const QString &dataDir, const QString &addonFile) { const QStringList paths = QStringList() << "/" << "/addons/" << "/../addons/"; if (!dataDir.isEmpty()) { foreach (const QString p, paths) { const QString filePath(dataDir + p + addonFile); if (QFileInfo(filePath).exists()) return filePath; } } const QString appPath = QApplication::applicationDirPath(); foreach (const QString p, paths) { const QString filePath(appPath + p + addonFile); if (QFileInfo(filePath).exists()) return filePath; } return QString(); } cppcheck-1.90/gui/checkthread.h000066400000000000000000000100521357737443600164460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CHECKTHREAD_H #define CHECKTHREAD_H #include #include "cppcheck.h" #include "threadresult.h" #include "suppressions.h" class Settings; /// @addtogroup GUI /// @{ /** * @brief Thread to run cppcheck * */ class CheckThread : public QThread { Q_OBJECT public: explicit CheckThread(ThreadResult &result); virtual ~CheckThread(); /** * @brief Set settings for cppcheck * * @param settings settings for cppcheck */ void check(const Settings &settings); /** * @brief Run whole program analysis * @param files All files */ void analyseWholeProgram(const QStringList &files); void setAddonsAndTools(const QStringList &addonsAndTools) { mAddonsAndTools = addonsAndTools; } void setMisraFile(const QString &misraFile) { mMisraFile = misraFile; } void setDataDir(const QString &dataDir) { mDataDir = dataDir; } void setClangIncludePaths(const QStringList &s) { mClangIncludePaths = s; } void setSuppressions(const QList &s) { mSuppressions = s; } /** * @brief method that is run in a thread * */ void run(); void stop(); /** * Determine command to run clang * \return Command to run clang, empty if it is not found */ static QString clangCmd(); /** * Determine command to run clang-tidy * \return Command to run clang-tidy, empty if it is not found */ static QString clangTidyCmd(); /** * Determine command to run python * \return Command to run python, empty if it is not found */ static QString pythonCmd(); /** * Look for addon and return path * \return path to addon if found, empty if it is not found */ static QString getAddonFilePath(const QString &dataDir, const QString &addonFile); signals: /** * @brief cpp checking is done * */ void done(); void fileChecked(const QString &file); protected: /** * @brief States for the check thread. * Whole purpose of these states is to allow stopping of the checking. When * stopping we say for the thread (Stopping) that stop when current check * has been completed. Thread must be stopped cleanly, just terminating thread * likely causes unpredictable side-effects. */ enum State { Running, /**< The thread is checking. */ Stopping, /**< The thread will stop after current work. */ Stopped, /**< The thread has been stopped. */ Ready, /**< The thread is ready. */ }; /** * @brief Thread's current execution state. */ State mState; ThreadResult &mResult; /** * @brief Cppcheck itself */ CppCheck mCppcheck; private: void runAddonsAndTools(const ImportProject::FileSettings *fileSettings, const QString &fileName); void parseAddonErrors(QString err, const QString &tool); void parseClangErrors(const QString &tool, const QString &file0, QString err); bool isSuppressed(const Suppressions::ErrorMessage &errorMessage) const; QStringList mFiles; bool mAnalyseWholeProgram; QStringList mAddonsAndTools; QString mDataDir; QStringList mClangIncludePaths; QList mSuppressions; QString mMisraFile; }; /// @} #endif // CHECKTHREAD_H cppcheck-1.90/gui/codeeditor.cpp000066400000000000000000000266361357737443600166740ustar00rootroot00000000000000#include #include #include "codeeditor.h" Highlighter::Highlighter(QTextDocument *parent, CodeEditorStyle *widgetStyle) : QSyntaxHighlighter(parent), mWidgetStyle(widgetStyle) { HighlightingRule rule; mKeywordFormat.setForeground(mWidgetStyle->keywordColor); mKeywordFormat.setFontWeight(mWidgetStyle->keywordWeight); QStringList keywordPatterns; keywordPatterns << "bool" << "break" << "case" << "char" << "class" << "const" << "continue" << "default" << "do" << "double" << "else" << "enum" << "explicit" << "for" << "friend" << "if" << "inline" << "int" << "long" << "namespace" << "operator" << "private" << "protected" << "public" << "return" << "short" << "signed" << "static" << "struct" << "switch" << "template" << "throw" << "typedef" << "typename" << "union" << "unsigned" << "virtual" << "void" << "volatile" << "while"; foreach (const QString &pattern, keywordPatterns) { rule.pattern = QRegularExpression("\\b" + pattern + "\\b"); rule.format = mKeywordFormat; rule.ruleRole = RuleRole::Keyword; mHighlightingRules.append(rule); } mClassFormat.setForeground(mWidgetStyle->classColor); mClassFormat.setFontWeight(mWidgetStyle->classWeight); rule.pattern = QRegularExpression("\\bQ[A-Za-z]+\\b"); rule.format = mClassFormat; rule.ruleRole = RuleRole::Class; mHighlightingRules.append(rule); mQuotationFormat.setForeground(mWidgetStyle->quoteColor); mQuotationFormat.setFontWeight(mWidgetStyle->quoteWeight); rule.pattern = QRegularExpression("\".*\""); rule.format = mQuotationFormat; rule.ruleRole = RuleRole::Quote; mHighlightingRules.append(rule); mSingleLineCommentFormat.setForeground(mWidgetStyle->commentColor); mSingleLineCommentFormat.setFontWeight(mWidgetStyle->commentWeight); rule.pattern = QRegularExpression("//[^\n]*"); rule.format = mSingleLineCommentFormat; rule.ruleRole = RuleRole::Comment; mHighlightingRules.append(rule); mHighlightingRulesWithSymbols = mHighlightingRules; mMultiLineCommentFormat.setForeground(mWidgetStyle->commentColor); mMultiLineCommentFormat.setFontWeight(mWidgetStyle->commentWeight); mSymbolFormat.setForeground(mWidgetStyle->symbolFGColor); mSymbolFormat.setBackground(mWidgetStyle->symbolBGColor); mSymbolFormat.setFontWeight(mWidgetStyle->symbolWeight); mCommentStartExpression = QRegularExpression("/\\*"); mCommentEndExpression = QRegularExpression("\\*/"); } void Highlighter::setSymbols(const QStringList &symbols) { mHighlightingRulesWithSymbols = mHighlightingRules; foreach (const QString &sym, symbols) { HighlightingRule rule; rule.pattern = QRegularExpression("\\b" + sym + "\\b"); rule.format = mSymbolFormat; rule.ruleRole = RuleRole::Symbol; mHighlightingRulesWithSymbols.append(rule); } } void Highlighter::setStyle(const CodeEditorStyle &newStyle) { mKeywordFormat.setForeground(newStyle.keywordColor); mKeywordFormat.setFontWeight(newStyle.keywordWeight); mClassFormat.setForeground(newStyle.classColor); mClassFormat.setFontWeight(newStyle.classWeight); mSingleLineCommentFormat.setForeground(newStyle.commentColor); mSingleLineCommentFormat.setFontWeight(newStyle.commentWeight); mMultiLineCommentFormat.setForeground(newStyle.commentColor); mMultiLineCommentFormat.setFontWeight(newStyle.commentWeight); mQuotationFormat.setForeground(newStyle.quoteColor); mQuotationFormat.setFontWeight(newStyle.quoteWeight); mSymbolFormat.setForeground(newStyle.symbolFGColor); mSymbolFormat.setBackground(newStyle.symbolBGColor); mSymbolFormat.setFontWeight(newStyle.symbolWeight); for (HighlightingRule& rule : mHighlightingRules) { applyFormat(rule); } for (HighlightingRule& rule : mHighlightingRulesWithSymbols) { applyFormat(rule); } } void Highlighter::highlightBlock(const QString &text) { foreach (const HighlightingRule &rule, mHighlightingRulesWithSymbols) { QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text); while (matchIterator.hasNext()) { QRegularExpressionMatch match = matchIterator.next(); setFormat(match.capturedStart(), match.capturedLength(), rule.format); } } setCurrentBlockState(0); int startIndex = 0; if (previousBlockState() != 1) startIndex = text.indexOf(mCommentStartExpression); while (startIndex >= 0) { QRegularExpressionMatch match = mCommentEndExpression.match(text, startIndex); int endIndex = match.capturedStart(); int commentLength = 0; if (endIndex == -1) { setCurrentBlockState(1); commentLength = text.length() - startIndex; } else { commentLength = endIndex - startIndex + match.capturedLength(); } setFormat(startIndex, commentLength, mMultiLineCommentFormat); startIndex = text.indexOf(mCommentStartExpression, startIndex + commentLength); } } void Highlighter::applyFormat(HighlightingRule &rule) { switch (rule.ruleRole) { case RuleRole::Keyword: rule.format = mKeywordFormat; break; case RuleRole::Class: rule.format = mClassFormat; break; case RuleRole::Comment: rule.format = mSingleLineCommentFormat; break; case RuleRole::Quote: rule.format = mQuotationFormat; break; case RuleRole::Symbol: rule.format = mSymbolFormat; break; } } CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent), mWidgetStyle(new CodeEditorStyle(defaultStyleLight)) { mLineNumberArea = new LineNumberArea(this); mHighlighter = new Highlighter(document(), mWidgetStyle); mErrorPosition = -1; QFont font("Monospace"); font.setStyleHint(QFont::TypeWriter); setFont(font); mLineNumberArea->setFont(font); // set widget coloring by overriding widget style sheet setObjectName("CodeEditor"); setStyleSheet(generateStyleString()); QShortcut *copyText = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_C),this); QShortcut *allText = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_A),this); connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); connect(copyText, SIGNAL(activated()), this, SLOT(copy())); connect(allText, SIGNAL(activated()), this, SLOT(selectAll())); updateLineNumberAreaWidth(0); } CodeEditor::~CodeEditor() { // NOTE: not a Qt Object - delete manually delete mWidgetStyle; } static int getPos(const QString &fileData, int lineNumber) { if (lineNumber <= 1) return 0; for (int pos = 0, line = 1; pos < fileData.size(); ++pos) { if (fileData[pos] != '\n') continue; ++line; if (line >= lineNumber) return pos + 1; } return fileData.size(); } void CodeEditor::setStyle(const CodeEditorStyle& newStyle) { *mWidgetStyle = newStyle; // apply new styling setStyleSheet(generateStyleString()); mHighlighter->setStyle(newStyle); mHighlighter->rehighlight(); highlightErrorLine(); } void CodeEditor::setError(const QString &code, int errorLine, const QStringList &symbols) { mHighlighter->setSymbols(symbols); setPlainText(code); mErrorPosition = getPos(code, errorLine); QTextCursor tc = textCursor(); tc.setPosition(mErrorPosition); setTextCursor(tc); centerCursor(); highlightErrorLine(); } int CodeEditor::lineNumberAreaWidth() { int digits = 1; int max = qMax(1, blockCount()); while (max >= 10) { max /= 10; ++digits; } int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; return space; } void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */) { setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); } void CodeEditor::updateLineNumberArea(const QRect &rect, int dy) { if (dy) mLineNumberArea->scroll(0, dy); else mLineNumberArea->update(0, rect.y(), mLineNumberArea->width(), rect.height()); if (rect.contains(viewport()->rect())) updateLineNumberAreaWidth(0); } void CodeEditor::resizeEvent(QResizeEvent *event) { QPlainTextEdit::resizeEvent(event); QRect cr = contentsRect(); mLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); } void CodeEditor::highlightErrorLine() { QList extraSelections; QTextEdit::ExtraSelection selection; selection.format.setBackground(mWidgetStyle->highlightBGColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = QTextCursor(document()); if (mErrorPosition >= 0) { selection.cursor.setPosition(mErrorPosition); } else { selection.cursor.setPosition(0); } selection.cursor.clearSelection(); extraSelections.append(selection); setExtraSelections(extraSelections); } void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event) { QPainter painter(mLineNumberArea); painter.fillRect(event->rect(), mWidgetStyle->lineNumBGColor); QTextBlock block = firstVisibleBlock(); int blockNumber = block.blockNumber(); int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); int bottom = top + (int) blockBoundingRect(block).height(); while (block.isValid() && top <= event->rect().bottom()) { if (block.isVisible() && bottom >= event->rect().top()) { QString number = QString::number(blockNumber + 1); painter.setPen(mWidgetStyle->lineNumFGColor); painter.drawText(0, top, mLineNumberArea->width(), fontMetrics().height(), Qt::AlignRight, number); } block = block.next(); top = bottom; bottom = top + (int) blockBoundingRect(block).height(); ++blockNumber; } } QString CodeEditor::generateStyleString() { QString bgcolor = QString("background:rgb(%1,%2,%3);") .arg(mWidgetStyle->widgetBGColor.red()) .arg(mWidgetStyle->widgetBGColor.green()) .arg(mWidgetStyle->widgetBGColor.blue()); QString fgcolor = QString("color:rgb(%1,%2,%3);") .arg(mWidgetStyle->widgetFGColor.red()) .arg(mWidgetStyle->widgetFGColor.green()) .arg(mWidgetStyle->widgetFGColor.blue()); QString style = QString("%1 %2") .arg(bgcolor) .arg(fgcolor); return style; } cppcheck-1.90/gui/codeeditor.h000066400000000000000000000056031357737443600163300ustar00rootroot00000000000000#ifndef CODEEDITOR_H #define CODEEDITOR_H #include #include #include #include #include "codeeditorstyle.h" class QPaintEvent; class QResizeEvent; class QSize; class QWidget; class LineNumberArea; class Highlighter : public QSyntaxHighlighter { Q_OBJECT public: explicit Highlighter(QTextDocument *parent, CodeEditorStyle *widgetStyle); void setSymbols(const QStringList &symbols); void setStyle(const CodeEditorStyle &newStyle); protected: void highlightBlock(const QString &text) override; private: enum RuleRole { Keyword = 1, Class = 2, Comment = 3, Quote = 4, Symbol = 5 }; struct HighlightingRule { QRegularExpression pattern; QTextCharFormat format; RuleRole ruleRole; }; void applyFormat(HighlightingRule &rule); QVector mHighlightingRules; QVector mHighlightingRulesWithSymbols; QRegularExpression mCommentStartExpression; QRegularExpression mCommentEndExpression; QTextCharFormat mKeywordFormat; QTextCharFormat mClassFormat; QTextCharFormat mSingleLineCommentFormat; QTextCharFormat mMultiLineCommentFormat; QTextCharFormat mQuotationFormat; QTextCharFormat mSymbolFormat; CodeEditorStyle *mWidgetStyle; }; class CodeEditor : public QPlainTextEdit { Q_OBJECT public: explicit CodeEditor(QWidget *parent); CodeEditor(const CodeEditor &) = delete; CodeEditor &operator=(const CodeEditor &) = delete; ~CodeEditor(); void lineNumberAreaPaintEvent(QPaintEvent *event); int lineNumberAreaWidth(); void setStyle(const CodeEditorStyle& newStyle); /** * Set source code to show, goto error line and highlight that line. * \param code The source code. * \param errorLine line number * \param symbols the related symbols, these are marked */ void setError(const QString &code, int errorLine, const QStringList &symbols); protected: void resizeEvent(QResizeEvent *event) override; private slots: void updateLineNumberAreaWidth(int newBlockCount); void highlightErrorLine(); void updateLineNumberArea(const QRect &, int); private: QString generateStyleString(); private: QWidget *mLineNumberArea; Highlighter *mHighlighter; CodeEditorStyle *mWidgetStyle; int mErrorPosition; }; class LineNumberArea : public QWidget { public: explicit LineNumberArea(CodeEditor *editor) : QWidget(editor) { mCodeEditor = editor; } QSize sizeHint() const override { return QSize(mCodeEditor->lineNumberAreaWidth(), 0); } protected: void paintEvent(QPaintEvent *event) override { mCodeEditor->lineNumberAreaPaintEvent(event); } private: CodeEditor *mCodeEditor; }; #endif // CODEEDITOR_H cppcheck-1.90/gui/codeeditorstyle.cpp000066400000000000000000000242121357737443600177410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "codeeditorstyle.h" #include CodeEditorStyle::CodeEditorStyle( const QColor& CtrlFGColor, const QColor& CtrlBGColor, const QColor& HiLiBGColor, const QColor& LnNumFGColor, const QColor& LnNumBGColor, const QColor& KeyWdFGColor, const QFont::Weight& KeyWdWeight, const QColor& ClsFGColor, const QFont::Weight& ClsWeight, const QColor& QteFGColor, const QFont::Weight& QteWeight, const QColor& CmtFGColor, const QFont::Weight& CmtWeight, const QColor& SymbFGColor, const QColor& SymbBGColor, const QFont::Weight& SymbWeight) : mSystemTheme(false), widgetFGColor(CtrlFGColor), widgetBGColor(CtrlBGColor), highlightBGColor(HiLiBGColor), lineNumFGColor(LnNumFGColor), lineNumBGColor(LnNumBGColor), keywordColor(KeyWdFGColor), keywordWeight(KeyWdWeight), classColor(ClsFGColor), classWeight(ClsWeight), quoteColor(QteFGColor), quoteWeight(QteWeight), commentColor(CmtFGColor), commentWeight(CmtWeight), symbolFGColor(SymbFGColor), symbolBGColor(SymbBGColor), symbolWeight(SymbWeight) {} bool CodeEditorStyle::operator==(const CodeEditorStyle& rhs) const { if (mSystemTheme != rhs.mSystemTheme) return false; if (widgetFGColor != rhs.widgetFGColor) return false; if (widgetBGColor != rhs.widgetBGColor) return false; if (highlightBGColor != rhs.highlightBGColor) return false; if (lineNumFGColor != rhs.lineNumFGColor) return false; if (lineNumBGColor != rhs.lineNumBGColor) return false; if (keywordColor != rhs.keywordColor) return false; if (keywordWeight != rhs.keywordWeight) return false; if (classColor != rhs.classColor) return false; if (classWeight != rhs.classWeight) return false; if (quoteColor != rhs.quoteColor) return false; if (quoteWeight != rhs.quoteWeight) return false; if (commentColor != rhs.commentColor) return false; if (commentWeight != rhs.commentWeight) return false; if (symbolFGColor != rhs.symbolFGColor) return false; if (symbolBGColor != rhs.symbolBGColor) return false; if (symbolWeight != rhs.symbolWeight) return false; return true; } bool CodeEditorStyle::operator!=(const CodeEditorStyle& rhs) const { return !(*this == rhs); } CodeEditorStyle CodeEditorStyle::getSystemTheme() { CodeEditorStyle theStyle(defaultStyleLight); theStyle.mSystemTheme = true; return theStyle; } CodeEditorStyle CodeEditorStyle::loadSettings(QSettings *settings) { CodeEditorStyle theStyle(CodeEditorStyle::getSystemTheme()); if (!settings) return theStyle; if (!settings->childGroups().contains(SETTINGS_STYLE_GROUP)) return theStyle; // style section exists - load values settings->beginGroup(SETTINGS_STYLE_GROUP); QString type = settings->value( SETTINGS_STYLE_TYPE, QVariant(SETTINGS_STYLE_TYPE_LIGHT) ).toString(); if (type == SETTINGS_STYLE_TYPE_LIGHT) { settings->endGroup(); return theStyle; } if (type == SETTINGS_STYLE_TYPE_DARK) { theStyle = defaultStyleDark; settings->endGroup(); return theStyle; } if (type == SETTINGS_STYLE_TYPE_CUSTOM) { theStyle.widgetFGColor = settings->value( SETTINGS_STYLE_WIDGETFG, QVariant(defaultStyleLight.widgetFGColor)).value(); theStyle.widgetBGColor = settings->value( SETTINGS_STYLE_WIDGETBG, QVariant(defaultStyleLight.widgetBGColor)).value(); theStyle.highlightBGColor = settings->value( SETTINGS_STYLE_HILIFG, QVariant(defaultStyleLight.highlightBGColor)).value(); theStyle.lineNumFGColor = settings->value( SETTINGS_STYLE_LINENUMFG, QVariant(defaultStyleLight.lineNumFGColor)).value(); theStyle.lineNumBGColor = settings->value( SETTINGS_STYLE_LINENUMBG, QVariant(defaultStyleLight.lineNumBGColor)).value(); theStyle.keywordColor = settings->value( SETTINGS_STYLE_KEYWORDFG, QVariant(defaultStyleLight.keywordColor)).value(); QVariant defKeyWWt(static_cast(defaultStyleLight.keywordWeight)); theStyle.keywordWeight = static_cast( settings->value(SETTINGS_STYLE_KEYWORDWT, defKeyWWt).toInt()); theStyle.classColor = settings->value( SETTINGS_STYLE_CLASSFG, QVariant(defaultStyleLight.classColor)).value(); QVariant defClsWt(static_cast(defaultStyleLight.classWeight)); theStyle.classWeight = static_cast( settings->value(SETTINGS_STYLE_CLASSWT, defClsWt).toInt()); theStyle.quoteColor = settings->value( SETTINGS_STYLE_QUOTEFG, QVariant(defaultStyleLight.quoteColor)).value(); QVariant defQteWt(static_cast(defaultStyleLight.quoteWeight)); theStyle.quoteWeight = static_cast( settings->value(SETTINGS_STYLE_QUOTEWT, defQteWt).toInt()); theStyle.commentColor = settings->value( SETTINGS_STYLE_COMMENTFG, QVariant(defaultStyleLight.commentColor)).value(); QVariant defCmtWt(static_cast(defaultStyleLight.commentWeight)); theStyle.commentWeight = static_cast( settings->value(SETTINGS_STYLE_COMMENTWT, defCmtWt).toInt()); theStyle.symbolFGColor = settings->value( SETTINGS_STYLE_SYMBOLFG, QVariant(defaultStyleLight.symbolFGColor)).value(); theStyle.symbolBGColor = settings->value( SETTINGS_STYLE_SYMBOLBG, QVariant(defaultStyleLight.symbolBGColor)).value(); QVariant defSymWt(static_cast(defaultStyleLight.symbolWeight)); theStyle.symbolWeight = static_cast( settings->value(SETTINGS_STYLE_SYMBOLWT, defSymWt).toInt()); } settings->endGroup(); return theStyle; } void CodeEditorStyle::saveSettings(QSettings *settings, const CodeEditorStyle& theStyle) { if (!settings) return; if (settings->childGroups().contains(SETTINGS_STYLE_GROUP)) { settings->remove(SETTINGS_STYLE_GROUP); if (theStyle.isSystemTheme()) return; } settings->beginGroup(SETTINGS_STYLE_GROUP); bool isDefaultLight = (defaultStyleLight == theStyle); bool isDefaultDark = (defaultStyleDark == theStyle); if (isDefaultLight && !isDefaultDark) { settings->setValue(SETTINGS_STYLE_TYPE, SETTINGS_STYLE_TYPE_LIGHT); } else if (!isDefaultLight && isDefaultDark) { settings->setValue(SETTINGS_STYLE_TYPE, SETTINGS_STYLE_TYPE_DARK); } else { settings->setValue(SETTINGS_STYLE_TYPE, SETTINGS_STYLE_TYPE_CUSTOM); settings->setValue(SETTINGS_STYLE_WIDGETFG, QVariant(theStyle.widgetFGColor)); settings->setValue(SETTINGS_STYLE_WIDGETBG, QVariant(theStyle.widgetBGColor)); settings->setValue(SETTINGS_STYLE_HILIFG, QVariant(theStyle.highlightBGColor)); settings->setValue(SETTINGS_STYLE_LINENUMFG, QVariant(theStyle.lineNumFGColor)); settings->setValue(SETTINGS_STYLE_LINENUMBG, QVariant(theStyle.lineNumBGColor)); settings->setValue(SETTINGS_STYLE_KEYWORDFG, QVariant(theStyle.keywordColor)); settings->setValue(SETTINGS_STYLE_KEYWORDWT, QVariant(static_cast(theStyle.keywordWeight))); settings->setValue(SETTINGS_STYLE_CLASSFG, QVariant(theStyle.classColor)); settings->setValue(SETTINGS_STYLE_CLASSWT, QVariant(static_cast(theStyle.classWeight))); settings->setValue(SETTINGS_STYLE_QUOTEFG, QVariant(theStyle.quoteColor)); settings->setValue(SETTINGS_STYLE_QUOTEWT, QVariant(static_cast(theStyle.quoteWeight))); settings->setValue(SETTINGS_STYLE_COMMENTFG, QVariant(theStyle.commentColor)); settings->setValue(SETTINGS_STYLE_COMMENTWT, QVariant(static_cast(theStyle.commentWeight))); settings->setValue(SETTINGS_STYLE_SYMBOLFG, QVariant(theStyle.symbolFGColor)); settings->setValue(SETTINGS_STYLE_SYMBOLBG, QVariant(theStyle.symbolBGColor)); settings->setValue(SETTINGS_STYLE_SYMBOLWT, QVariant(static_cast(theStyle.symbolWeight))); } settings->endGroup(); } cppcheck-1.90/gui/codeeditorstyle.h000066400000000000000000000115441357737443600174120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CODEEDITORSTYLE_H #define CODEEDITORSTYLE_H #include #include #include const QString SETTINGS_STYLE_GROUP("EditorStyle"); const QString SETTINGS_STYLE_TYPE("StyleType"); const QString SETTINGS_STYLE_TYPE_LIGHT("DefaultLight"); const QString SETTINGS_STYLE_TYPE_DARK("DefaultDark"); const QString SETTINGS_STYLE_TYPE_CUSTOM("Custom"); const QString SETTINGS_STYLE_WIDGETFG("StyleWidgetFG"); const QString SETTINGS_STYLE_WIDGETBG("StyleWidgetBG"); const QString SETTINGS_STYLE_HILIFG("StyleHighlightFG"); const QString SETTINGS_STYLE_LINENUMFG("StyleLineNumFG"); const QString SETTINGS_STYLE_LINENUMBG("StyleLineNumBG"); const QString SETTINGS_STYLE_KEYWORDFG("StyleKeywordFG"); const QString SETTINGS_STYLE_KEYWORDWT("StyleKeywordWeight"); const QString SETTINGS_STYLE_CLASSFG("StyleClassFG"); const QString SETTINGS_STYLE_CLASSWT("StyleClassWeight"); const QString SETTINGS_STYLE_QUOTEFG("StyleQuoteFG"); const QString SETTINGS_STYLE_QUOTEWT("StyleQuoteWeight"); const QString SETTINGS_STYLE_COMMENTFG("StyleCommentFG"); const QString SETTINGS_STYLE_COMMENTWT("StyleCommentWeight"); const QString SETTINGS_STYLE_SYMBOLFG("StyleSymbolFG"); const QString SETTINGS_STYLE_SYMBOLBG("StyleSymbolBG"); const QString SETTINGS_STYLE_SYMBOLWT("StyleSymbolWeight"); class QSettings; class CodeEditorStyle { public: explicit CodeEditorStyle( const QColor& CtrlFGColor, const QColor& CtrlBGColor, const QColor& HiLiBGColor, const QColor& LnNumFGColor, const QColor& LnNumBGColor, const QColor& KeyWdFGColor, const QFont::Weight& KeyWdWeight, const QColor& ClsFGColor, const QFont::Weight& ClsWeight, const QColor& QteFGColor, const QFont::Weight& QteWeight, const QColor& CmtFGColor, const QFont::Weight& CmtWeight, const QColor& SymbFGColor, const QColor& SymbBGColor, const QFont::Weight& SymbWeight); ~CodeEditorStyle() {} bool operator==(const CodeEditorStyle& rhs) const; bool operator!=(const CodeEditorStyle& rhs) const; bool isSystemTheme() const { return mSystemTheme; } static CodeEditorStyle getSystemTheme(); static CodeEditorStyle loadSettings(QSettings *settings); static void saveSettings(QSettings *settings, const CodeEditorStyle& theStyle); public: bool mSystemTheme; QColor widgetFGColor; QColor widgetBGColor; QColor highlightBGColor; QColor lineNumFGColor; QColor lineNumBGColor; QColor keywordColor; QFont::Weight keywordWeight; QColor classColor; QFont::Weight classWeight; QColor quoteColor; QFont::Weight quoteWeight; QColor commentColor; QFont::Weight commentWeight; QColor symbolFGColor; QColor symbolBGColor; QFont::Weight symbolWeight; }; static const CodeEditorStyle defaultStyleLight( /* editor FG/BG */ Qt::black, QColor(240, 240, 240), /* highlight BG */ QColor(255, 220, 220), /* line number FG/BG */ Qt::black, QColor(240, 240, 240), /* keyword FG/Weight */ Qt::darkBlue, QFont::Bold, /* class FG/Weight */ Qt::darkMagenta, QFont::Bold, /* quote FG/Weight */ Qt::darkGreen, QFont::Normal, /* comment FG/Weight */ Qt::gray, QFont::Normal, /* Symbol FG/BG/Weight */ Qt::red, QColor(220, 220, 255), QFont::Normal ); // Styling derived from Eclipse Color Theme - 'RecognEyes' // http://www.eclipsecolorthemes.org/?view=theme&id=30 static const CodeEditorStyle defaultStyleDark( /* editor FG/BG */ QColor(218, 218, 218), QColor(16, 16, 32), /* highlight BG */ QColor(64, 64, 64), /* line number FG/BG */ QColor(43, 145, 175), QColor(16, 16, 32), /* keyword FG/Weight */ QColor(0, 204, 204), QFont::Bold, /* class FG/Weight */ QColor(218, 0, 218), QFont::Bold, /* quote FG/Weight */ QColor(0, 204, 0), QFont::Normal, /* comment FG/Weight */ QColor(180, 180, 180), QFont::Normal, /* Symbol FG/BG/Weight */ QColor(218, 32, 32), QColor(32, 32, 108), QFont::Normal ); #endif /* CODEEDITORSTYLE_H */ cppcheck-1.90/gui/codeeditstylecontrols.cpp000066400000000000000000000070371357737443600211720ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "codeeditstylecontrols.h" #include SelectColorButton::SelectColorButton(QWidget* parent) : QPushButton(parent), mColor(QColor(255, 255, 255)) { updateColor(); connect(this, SIGNAL(clicked()), this, SLOT(changeColor())); } void SelectColorButton::updateColor() { QString btnColorStyle = QString( "background-color:rgb(%1,%2,%3);" "border-style:outset;" "border-width: 1px;") .arg(mColor.red()) .arg(mColor.green()) .arg(mColor.blue()); setObjectName("SelectColorButton"); setStyleSheet(btnColorStyle); } void SelectColorButton::changeColor() { QColorDialog pDlg(mColor); pDlg.setModal(true); int nResult = pDlg.exec(); if (nResult == QDialog::Accepted) { setColor(pDlg.selectedColor()); emit colorChanged(mColor); } } void SelectColorButton::setColor(const QColor& color) { mColor = color; updateColor(); } const QColor& SelectColorButton::getColor() { return mColor; } SelectFontWeightCombo::SelectFontWeightCombo(QWidget* parent) : QComboBox(parent), mWeight(QFont::Normal) { addItem(QObject::tr("Thin"), QVariant(static_cast(QFont::Thin))); addItem(QObject::tr("ExtraLight"), QVariant(static_cast(QFont::ExtraLight))); addItem(QObject::tr("Light"), QVariant(static_cast(QFont::Light))); addItem(QObject::tr("Normal"), QVariant(static_cast(QFont::Normal))); addItem(QObject::tr("Medium"), QVariant(static_cast(QFont::Medium))); addItem(QObject::tr("DemiBold"), QVariant(static_cast(QFont::DemiBold))); addItem(QObject::tr("Bold"), QVariant(static_cast(QFont::Bold))); addItem(QObject::tr("ExtraBold"), QVariant(static_cast(QFont::ExtraBold))); addItem(QObject::tr("Black"), QVariant(static_cast(QFont::Black))); updateWeight(); connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(changeWeight(int))); } void SelectFontWeightCombo::updateWeight() { int nResult = findData(QVariant(static_cast(mWeight))); if (nResult != -1) { setCurrentIndex(nResult); } else { setCurrentIndex(findData(static_cast(QFont::Normal))); } } void SelectFontWeightCombo::changeWeight(int index) { if (index != -1) { setWeight(static_cast(itemData(index).toInt())); emit weightChanged(mWeight); } } void SelectFontWeightCombo::setWeight(const QFont::Weight& weight) { mWeight = weight; updateWeight(); } const QFont::Weight& SelectFontWeightCombo::getWeight() { return mWeight; } cppcheck-1.90/gui/codeeditstylecontrols.h000066400000000000000000000035371357737443600206400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // widget subclass methodology derived from here: // https://stackoverflow.com/questions/18257281/qt-color-picker-widget/43871405#43871405 #ifndef CODEEDITORSTYLECONTROLS_H #define CODEEDITORSTYLECONTROLS_H #include #include #include #include class SelectColorButton : public QPushButton { Q_OBJECT public: explicit SelectColorButton(QWidget* parent); virtual ~SelectColorButton() {} void setColor(const QColor& color); const QColor& getColor(); signals: void colorChanged(const QColor& newColor); public slots: void updateColor(); void changeColor(); private: QColor mColor; }; class SelectFontWeightCombo : public QComboBox { Q_OBJECT public: explicit SelectFontWeightCombo(QWidget* parent); virtual ~SelectFontWeightCombo() {} void setWeight(const QFont::Weight& weight); const QFont::Weight& getWeight(); signals: void weightChanged(const QFont::Weight& newWeight); public slots: void updateWeight(); void changeWeight(int index); private: QFont::Weight mWeight; }; #endif //CODEEDITORSTYLECONTROLS_H cppcheck-1.90/gui/codeeditstyledialog.cpp000066400000000000000000000305121357737443600205600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "codeeditstyledialog.h" #include #include #include #include const QString StyleEditDialog::mSampleDocument( "/*****\n" "* Multiline Comment\n" "*****/\n" "#include \n" "#include \n" "\n" "class fwdClass;\n" "\n" "int main(int argc, char *argv[])\n" "{\n" " QApplication a(argc, argv);\n" " int nLife = 42;\n" " w.show();\n" " // single line comment\n" " // line below is highlighted\n" " fwdClass( nLife );\n" " return a.exec();\n" "}\n" "\n" "void class fwdClass( double dValue ) {\n" " std::cout << \"Ipsum Lorem: \"\n" " << nValue\n" " << std::endl;\n" "}\n"); const QStringList StyleEditDialog::mErrSymbolsList = ( QStringList(QStringList() << "nLife" << "dValue" << "nValue")); const int StyleEditDialog::mErrLineNum = 16; StyleEditDialog::StyleEditDialog(const CodeEditorStyle& newStyle, QWidget *parent /*= nullptr*/) : QDialog(parent), mStyleIncoming(newStyle), mStyleOutgoing(newStyle) { QVBoxLayout *vboxMain = new QVBoxLayout(this); QHBoxLayout *hboxEdit = new QHBoxLayout(); // Color/Weight controls QFormLayout *flEditControls = new QFormLayout(); mBtnWidgetColorFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Editor Foreground Color"), mBtnWidgetColorFG); mBtnWidgetColorBG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Editor Background Color"), mBtnWidgetColorBG); mBtnHighlightBG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Highlight Background Color"), mBtnHighlightBG); mBtnLineNumFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Line Number Foreground Color"), mBtnLineNumFG); mBtnLineNumBG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Line Number Background Color"), mBtnLineNumBG); mBtnKeywordFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Keyword Foreground Color"), mBtnKeywordFG); mCBKeywordWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Keyword Font Weight"), mCBKeywordWeight); mBtnClassFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Class Foreground Color"), mBtnClassFG); mCBClassWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Class Font Weight"), mCBClassWeight); mBtnQuoteFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Quote Foreground Color"), mBtnQuoteFG); mCBQuoteWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Quote Font Weight"), mCBQuoteWeight); mBtnCommentFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Comment Foreground Color"), mBtnCommentFG); mCBCommentWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Comment Font Weight"), mCBCommentWeight); mBtnSymbolFG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Symbol Foreground Color"), mBtnSymbolFG); mBtnSymbolBG = new SelectColorButton(this); flEditControls->addRow(QObject::tr("Symbol Background Color"), mBtnSymbolBG); mCBSymbolWeight = new SelectFontWeightCombo(this); flEditControls->addRow(QObject::tr("Symbol Font Weight"), mCBSymbolWeight); hboxEdit->addLayout(flEditControls); // CodeEditor to display Style mSampleEditor = new CodeEditor(this); QFont sampleFont("Monospace"); QFontMetrics fm(sampleFont); mSampleEditor->setMinimumWidth(fm.width(QString(40, 'W'))); // designate highlight, errors, and symbols mSampleEditor->setError(mSampleDocument, mErrLineNum, mErrSymbolsList); // End Controls hboxEdit->addWidget(mSampleEditor); vboxMain->addLayout(hboxEdit); // Default Controls QHBoxLayout *hboxDefaultControls = new QHBoxLayout(); mBtnDefaultLight = new QPushButton(QObject::tr("Set to Default Light"), this); mBtnDefaultDark = new QPushButton(QObject::tr("Set to Default Dark"), this); hboxDefaultControls->addStretch(1); hboxDefaultControls->addWidget(mBtnDefaultLight); hboxDefaultControls->addWidget(mBtnDefaultDark); hboxDefaultControls->addStretch(1); vboxMain->addLayout(hboxDefaultControls); vboxMain->addStretch(2); // dialog controls QDialogButtonBox *dBtnBox = new QDialogButtonBox( QDialogButtonBox::Cancel | QDialogButtonBox::Ok | QDialogButtonBox::Reset); vboxMain->addStretch(1); vboxMain->addWidget(dBtnBox); // setup values for style controls updateControls(); updateStyle(); connect(dBtnBox, SIGNAL(accepted()), this, SLOT(accept())); connect(dBtnBox, SIGNAL(rejected()), this, SLOT(reject())); connect(dBtnBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(resetStyle())); connect(mBtnDefaultLight, SIGNAL(clicked()), this, SLOT(setStyleDefaultLight())); connect(mBtnDefaultDark, SIGNAL(clicked()), this, SLOT(setStyleDefaultDark())); connect(mBtnWidgetColorFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedWidgetFG(const QColor&))); connect(mBtnWidgetColorBG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedWidgetBG(const QColor&))); connect(mBtnHighlightBG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedHighlightBG(const QColor&))); connect(mBtnLineNumFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedLineNumFG(const QColor&))); connect(mBtnLineNumBG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedLineNumBG(const QColor&))); connect(mBtnKeywordFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedKeywordFG(const QColor&))); connect(mCBKeywordWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedKeyword(const QFont::Weight&))); connect(mBtnClassFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedClassFG(const QColor&))); connect(mCBClassWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedClass(const QFont::Weight&))); connect(mBtnQuoteFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedQuoteFG(const QColor&))); connect(mCBQuoteWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedQuote(const QFont::Weight&))); connect(mBtnCommentFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedCommentFG(const QColor&))); connect(mCBCommentWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedComment(const QFont::Weight&))); connect(mBtnSymbolFG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedSymbolFG(const QColor&))); connect(mBtnSymbolBG, SIGNAL(colorChanged(const QColor&)), this, SLOT(colorChangedSymbolBG(const QColor&))); connect(mCBSymbolWeight, SIGNAL(weightChanged(const QFont::Weight&)), this, SLOT(weightChangedSymbol(const QFont::Weight&))); } void StyleEditDialog::updateControls() { mBtnWidgetColorFG->setColor(mStyleOutgoing.widgetFGColor); mBtnWidgetColorBG->setColor(mStyleOutgoing.widgetBGColor); mBtnHighlightBG->setColor(mStyleOutgoing.highlightBGColor); mBtnLineNumFG->setColor(mStyleOutgoing.lineNumFGColor); mBtnLineNumBG->setColor(mStyleOutgoing.lineNumBGColor); mBtnKeywordFG->setColor(mStyleOutgoing.keywordColor); mCBKeywordWeight->setWeight(mStyleOutgoing.keywordWeight); mBtnClassFG->setColor(mStyleOutgoing.classColor); mCBClassWeight->setWeight(mStyleOutgoing.classWeight); mBtnQuoteFG->setColor(mStyleOutgoing.quoteColor); mCBQuoteWeight->setWeight(mStyleOutgoing.quoteWeight); mBtnCommentFG->setColor(mStyleOutgoing.commentColor); mCBCommentWeight->setWeight(mStyleOutgoing.commentWeight); mBtnSymbolFG->setColor(mStyleOutgoing.symbolFGColor); mBtnSymbolBG->setColor(mStyleOutgoing.symbolBGColor); mCBSymbolWeight->setWeight(mStyleOutgoing.symbolWeight); } void StyleEditDialog::updateStyle() { mBtnDefaultLight->setEnabled(mStyleOutgoing != defaultStyleLight); mBtnDefaultDark->setEnabled(mStyleOutgoing != defaultStyleDark); // set Editor Styling mSampleEditor->setStyle(mStyleOutgoing); } CodeEditorStyle StyleEditDialog::getStyle() { return mStyleOutgoing; } void StyleEditDialog::resetStyle() { mStyleOutgoing = mStyleIncoming; updateControls(); updateStyle(); } void StyleEditDialog::setStyleDefaultLight() { mStyleOutgoing = defaultStyleLight; updateControls(); updateStyle(); } void StyleEditDialog::setStyleDefaultDark() { mStyleOutgoing = defaultStyleDark; updateControls(); updateStyle(); } void StyleEditDialog::colorChangedWidgetFG(const QColor& newColor) { mStyleOutgoing.widgetFGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedWidgetBG(const QColor& newColor) { mStyleOutgoing.widgetBGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedHighlightBG(const QColor& newColor) { mStyleOutgoing.highlightBGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedLineNumFG(const QColor& newColor) { mStyleOutgoing.lineNumFGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedLineNumBG(const QColor& newColor) { mStyleOutgoing.lineNumBGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedKeywordFG(const QColor& newColor) { mStyleOutgoing.keywordColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedKeyword(const QFont::Weight& newWeight) { mStyleOutgoing.keywordWeight = newWeight; updateStyle(); } void StyleEditDialog::colorChangedClassFG(const QColor& newColor) { mStyleOutgoing.classColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedClass(const QFont::Weight& newWeight) { mStyleOutgoing.classWeight = newWeight; updateStyle(); } void StyleEditDialog::colorChangedQuoteFG(const QColor& newColor) { mStyleOutgoing.quoteColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedQuote(const QFont::Weight& newWeight) { mStyleOutgoing.quoteWeight = newWeight; updateStyle(); } void StyleEditDialog::colorChangedCommentFG(const QColor& newColor) { mStyleOutgoing.commentColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedComment(const QFont::Weight& newWeight) { mStyleOutgoing.commentWeight = newWeight; updateStyle(); } void StyleEditDialog::colorChangedSymbolFG(const QColor& newColor) { mStyleOutgoing.symbolFGColor = newColor; updateStyle(); } void StyleEditDialog::colorChangedSymbolBG(const QColor& newColor) { mStyleOutgoing.symbolBGColor = newColor; updateStyle(); } void StyleEditDialog::weightChangedSymbol(const QFont::Weight& newWeight) { mStyleOutgoing.symbolWeight = newWeight; updateStyle(); } cppcheck-1.90/gui/codeeditstyledialog.h000066400000000000000000000065131357737443600202310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CODEEDITSTYLEDIALOG_H #define CODEEDITSTYLEDIALOG_H #include #include #include "codeeditstylecontrols.h" #include "codeeditor.h" #include "codeeditorstyle.h" class StyleEditDialog : public QDialog { Q_OBJECT public: explicit StyleEditDialog(const CodeEditorStyle& newStyle, QWidget *parent = nullptr); virtual ~StyleEditDialog() {} CodeEditorStyle getStyle(); private: void updateControls(); void updateStyle(); public slots: void resetStyle(); void setStyleDefaultLight(); void setStyleDefaultDark(); void colorChangedWidgetFG(const QColor& newColor); void colorChangedWidgetBG(const QColor& newColor); void colorChangedHighlightBG(const QColor& newColor); void colorChangedLineNumFG(const QColor& newColor); void colorChangedLineNumBG(const QColor& newColor); void colorChangedKeywordFG(const QColor& newColor); void weightChangedKeyword(const QFont::Weight& newWeight); void colorChangedClassFG(const QColor& newColor); void weightChangedClass(const QFont::Weight& newWeight); void colorChangedQuoteFG(const QColor& newColor); void weightChangedQuote(const QFont::Weight& newWeight); void colorChangedCommentFG(const QColor& newColor); void weightChangedComment(const QFont::Weight& newWeight); void colorChangedSymbolFG(const QColor& newColor); void colorChangedSymbolBG(const QColor& newColor); void weightChangedSymbol(const QFont::Weight& newWeight); private: CodeEditorStyle mStyleIncoming; CodeEditorStyle mStyleOutgoing; CodeEditor *mSampleEditor; SelectColorButton *mBtnWidgetColorFG; SelectColorButton *mBtnWidgetColorBG; SelectColorButton *mBtnHighlightBG; SelectColorButton *mBtnLineNumFG; SelectColorButton *mBtnLineNumBG; SelectColorButton *mBtnKeywordFG; SelectFontWeightCombo *mCBKeywordWeight; SelectColorButton *mBtnClassFG; SelectFontWeightCombo *mCBClassWeight; SelectColorButton *mBtnQuoteFG; SelectFontWeightCombo *mCBQuoteWeight; SelectColorButton *mBtnCommentFG; SelectFontWeightCombo *mCBCommentWeight; SelectColorButton *mBtnSymbolFG; SelectColorButton *mBtnSymbolBG; SelectFontWeightCombo *mCBSymbolWeight; QPushButton *mBtnDefaultLight; QPushButton *mBtnDefaultDark; static const QString mSampleDocument; static const QStringList mErrSymbolsList; static const int mErrLineNum; }; #endif //CODEEDITSTYLEDIALOG_H cppcheck-1.90/gui/common.cpp000066400000000000000000000043301357737443600160260ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "common.h" #include #include #include #include QString getPath(const QString &type) { QSettings settings; QString path = settings.value(type, QString()).toString(); if (path.isEmpty()) { // if not set, fallback to last check path hoping that it will be close enough path = settings.value(SETTINGS_LAST_CHECK_PATH, QString()).toString(); if (path.isEmpty()) // if not set, return user's home directory as the best we can do for now return QDir::homePath(); } return path; } void setPath(const QString &type, const QString &value) { QSettings settings; settings.setValue(type, value); } QString toFilterString(const QMap& filters, bool addAllSupported, bool addAll) { QStringList entries; if (addAllSupported) { entries << QCoreApplication::translate("toFilterString", "All supported files (%1)") .arg(QStringList(filters.values()).join(" ")); } if (addAll) { entries << QCoreApplication::translate("toFilterString", "All files (%1)").arg("*.*"); } // We're using the description of the filters as the map keys, the file // name patterns are our values. The generated filter string list will // thus be sorted alphabetically over the descriptions. for (auto k: filters.keys()) { entries << QString("%1 (%2)").arg(k).arg(filters.value(k)); } return entries.join(";;"); } cppcheck-1.90/gui/common.h000066400000000000000000000137101357737443600154750ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef COMMON_H #define COMMON_H #include #include /// @addtogroup GUI /// @{ #define CLANG_ANALYZER "clang-analyzer" #define CLANG_TIDY "clang-tidy" /** * QSetting value names */ // Window/dialog sizes #define SETTINGS_WINDOW_MAXIMIZED "Window maximized" #define SETTINGS_WINDOW_WIDTH "Window width" #define SETTINGS_WINDOW_HEIGHT "Window height" #define SETTINGS_LOG_VIEW_WIDTH "Log/View width" #define SETTINGS_LOG_VIEW_HEIGHT "Log/View height" #define SETTINGS_MAINWND_SPLITTER_STATE "Mainwindow/Vertical splitter state" #define SETTINGS_CHECK_DIALOG_WIDTH "Check dialog width" #define SETTINGS_CHECK_DIALOG_HEIGHT "Check dialog height" #define SETTINGS_PROJECT_DIALOG_WIDTH "Project dialog width" #define SETTINGS_PROJECT_DIALOG_HEIGHT "Project dialog height" // Main window settings #define SETTINGS_RESULT_COLUMN_WIDTH "Result column %1 width" #define SETTINGS_TOOLBARS_MAIN_SHOW "Toolbars/ShowStandard" #define SETTINGS_TOOLBARS_VIEW_SHOW "Toolbars/ShowView" #define SETTINGS_TOOLBARS_FILTER_SHOW "Toolbars/ShowFilter" // Show * states #define SETTINGS_SHOW_STYLE "Show style" #define SETTINGS_SHOW_ERRORS "Show errors" #define SETTINGS_SHOW_WARNINGS "Show warnings" #define SETTINGS_SHOW_PERFORMANCE "Show performance" #define SETTINGS_SHOW_INFORMATION "Show information" #define SETTINGS_SHOW_PORTABILITY "Show portability" // Standards support #define SETTINGS_STD_CPP "Standard CPP" #define SETTINGS_STD_C "Standard C" // Language enforcement #define SETTINGS_ENFORCED_LANGUAGE "Enforced language" // Other settings #define SETTINGS_CHECK_FORCE "Check force" #define SETTINGS_CHECK_THREADS "Check threads" #define SETTINGS_SHOW_FULL_PATH "Show full path" #define SETTINGS_SHOW_NO_ERRORS "Show no errors message" #define SETTINGS_SHOW_DEBUG_WARNINGS "Show debug warnings" #define SETTINGS_SAVE_ALL_ERRORS "Save all errors" #define SETTINGS_SAVE_FULL_PATH "Save full path" #define SETTINGS_APPLICATION_NAMES "Application names" #define SETTINGS_APPLICATION_PATHS "Application paths" #define SETTINGS_APPLICATION_PARAMS "Application parameters" #define SETTINGS_APPLICATION_DEFAULT "Default Application" #define SETTINGS_LANGUAGE "Application language" #define SETTINGS_GLOBAL_INCLUDE_PATHS "Global include paths" #define SETTINGS_PYTHON_PATH "Python path" #define SETTINGS_MISRA_FILE "MISRA C 2012 file" #define SETTINGS_CLANG_PATH "Clang path" #define SETTINGS_VS_INCLUDE_PATHS "VS include paths" #define SETTINGS_INLINE_SUPPRESSIONS "Inline suppressions" #define SETTINGS_INCONCLUSIVE_ERRORS "Inconclusive errors" #define SETTINGS_MRU_PROJECTS "MRU Projects" #define SETTINGS_SHOW_ERROR_ID "Show error Id" #define SETTINGS_SHOW_STATISTICS "Show statistics" #define SETTINGS_OPEN_PROJECT "Open Project" // The maximum value for the progress bar #define PROGRESS_MAX 1024.0 #define SETTINGS_CHECKED_PLATFORM "Checked platform" #define SETTINGS_LAST_CHECK_PATH "Last check path" #define SETTINGS_LAST_PROJECT_PATH "Last project path" #define SETTINGS_LAST_RESULT_PATH "Last result path" #define SETTINGS_LAST_SOURCE_PATH "Last source path" #define SETTINGS_LAST_INCLUDE_PATH "Last include path" #define SETTINGS_LAST_APP_PATH "Last application path" #define SETTINGS_LAST_ANALYZE_FILES_FILTER "Last analyze files filter" /** * @brief Obtains the path of specified type * Returns the path of specified type if not empty. Otherwise returns last check * path if valid or user's home directory. * @param type Type of path to obtain * @return Best path for provided type */ QString getPath(const QString &type); /** * @brief Stores last used path of specified type * Stores provided path as last used path for specified type. * @param type Type of the path to store * @param value Path to store */ void setPath(const QString &type, const QString &value); /** * @brief Creates a string suitable for passing as the filter argument to * methods like QFileDialog::getOpenFileName. * @param filters A map of filter descriptions to the associated file name * patterns. * @param addAllSupported If set to true (the default), the function will * include a filter entry containing all the file name patterns found in * \p filters. This entry will be the first in the resulting filter string. * @param addAll If set to true (the default), the function will * include a filter entry displaying all files. This entry will be placed * after the entry for \p addAllSupported files. * * Example usage: * * @code * QMap filters; * filters[tr("Supported images")] = "*.bmp *.jpg *.png"; * filters[tr("Plain text")] = "*.txt"; * * const QString filterString = toFilterString(filters); * * // filterString contains "All supported files (*.txt *.bmp *.jpg *.png);;All files (*.*);;Plain text (*.txt);;Supported images (*.bmp *.jpg *.png)" * @endcode */ QString toFilterString(const QMap& filters, bool addAllSupported=true, bool addAll=true); /// @} #endif cppcheck-1.90/gui/cppcheck-gui.desktop000066400000000000000000000002611357737443600177660ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=Cppcheck Comment=A tool for static C/C++ code analysis Exec=cppcheck-gui Icon=cppcheck-gui Categories=Development;Debugger;Qt; cppcheck-1.90/gui/cppcheck-gui.png000066400000000000000000000047571357737443600171170ustar00rootroot00000000000000PNG  IHDRAAE IDATxt$񎝬m۶mڶm\۶m۾kS{*'әAMwuUX^V x/1ovb :n*m⃟ `.A 11V>2`vIJzFfH/S6o>#S=oW2iҫj-n[Ixx.1B<$((X/g";w^]c۷_3ޗWH2Mh3(m 'QSAX Ԭ[. ΧҲ)QYo=Z Iɒ s煲l>X?wA4C@T0;]uGj.npaUTl3W!_Leow8Wo2G"N` ZP]C'!8Fg;ZLTFQN@zZi\EsE\ͻzBȍ.]ޣ:[ȓntƍ{L(P/\n? @6j`e9!|Ѩ1m;~Y!,Xdyvb3d o,jdxVP ,tU!Gg!n^ǔH5}u مЬYԯ?4]ХPE!t 5Hmo͚`=q˒{=Hdd<3nCHJzZ7< ~n #f6ȰނzO. +QN+=^A#$6}8BFߣ<ҥ qwim:iwG4Uk*a읖~ĒY'>Gh QqUjƺ'd;\4F+wmJH,W`X~+#&ԁ/P=0NrSijrȨt_iےUnK$"*5xᶪJJ'%4'5)mVc G`ǎR׍řuXn d},ݒ]- K=ƫ EᲚ$27.ѣ\ siQ""f}wH|.K&'9,ך/#&GU_Ԫ՗0vCanݖLr~FEhOC]u)HѲrzAKj65Q.+q|YG^mUNO|mS,q1kUgsS7YRd_:DqIlԚ?RZy5DX: { ] pK:Uu' `(_~6TuBHD%gݖf݈_71K0jH|L{&3I=荀8)Uɒv2;?\ ?k!CQO]d !~D T(KPiN< /OAC O0>g,-I>z#~6d=vbFߡ;V}-tzoNC, M=_P~pjQc|x t:!jf`:^]_<>jVQsLmï{!Ӽ?X=eJN͂=U[a{%)Am˜ƬU/[V0 AY\xwTٶoP  PťjIƚzD ˪4@OЏL5^-0_BRWr[-XA1  jta3_X Pc==W,cTP7UC==YyGԓ?#u`4vaj}ʧ(lQA!Ju4\.e r{h,9VѰpc_پ \@ۍOU脀WϥIENDB`cppcheck-1.90/gui/cppcheck-gui.rc000066400000000000000000000002661357737443600167260ustar00rootroot00000000000000// Include command line program's resource file containing version info #include "../cli/version.rc" // GUI Icon IDI_ICON1 ICON DISCARDABLE "cppcheck.ico" cppcheck-1.90/gui/cppcheck-gui.svg000066400000000000000000000077731357737443600171330ustar00rootroot00000000000000 image/svg+xml c ++ cppcheck-1.90/gui/cppcheck.ico000066400000000000000000000611761357737443600163210ustar00rootroot00000000000000 00h ( 00 h^"00 %'  nM h^(0`p 32333 0#3333p`#33333#p#33333330p333333332 p333333333333333332333332333330#333330p33332333333 p3303333332p#333332p3333330p333333 3333330sp333333"wpp#333333 pp|3333333333333 |pppp#33332  3330p|p32 3 |wppwGpppp pp p p p @ p 0  @p p p pp w wpwpww@ p  @ p p| p| pw|p|Lpwtpp???~???>A3B5D6F8I:K<L=QATCWEaMeQjUlVnXpYs\v^ya|c}dAAAFFFIIINNNPPPTTTYYY^^^nAApBBuEExGGzHH}JJ```eeehhhoooqqquuuzzz}}}LLNNPPSSTTUUXX[[]]^^``bbccddiikkmmooppssuuwwyy{{||~~fhjmpstwxz|~ځ݂ބⅅ戈銊싋/ P6pLbx1Qq,/KPip1Qq/-P?pRcv1Qqϑܱ/Pp!&,>X1qQq@pp6C|)b8%|%``CJ Zj7*l7[lr6dh5*A`Wr@YdAl>kaX=|*ca55a Im]d4$nKe8~)8g9mr __KAbj< C|Xf .r I+7[ lB ib @Jd;!r~sz~r\a?K*/3ML3 C~~!X9#||@"Rt?#8^JH,uTRV}HiZ?B1y,%Kl.Nxmaf9nCEy{A3T*q[k;Ir-"~+Q0z%=k>I*u?t@#6dW?s,GsCy(znnI @znn+,oDz?/|+0~}l?xE|+1~|3?G|+3~JQ*?G|+3AVwA?G|+3~!wuC*Gq)3rVH##G ##.~"#3 %##ytHP{,P?uvB'{0HTy?&y{0yrL~/00S00{-00P00)+zznGBzz|zzm3Czzz!(U??G|+3~nF2'??G|+3~QIlN??G|+3~IM%??G|+3~!UO.%R??U0|+V-~"UOmnP@C}B!OM'LP.COF.qI,OTuywtRL-@onC+ !.JsGwGwGwGwGw?GwGwGwGwGwGwGwGwGw?GwGw?GwGwGwGwGwGw~Gw??Gw?Gw2 :1 ;2 ?4%!!)"".##)&&-$$*)),++0 4""2$$9##8$$1**5((4--:**=++9,,=,, 1-$30)42+74-64*84100:11<222;96=;F**F--I--L--P//A33D11I22M22M44@88A>>E<<P00T22R77U77\66]99`99a<<f<<h==B5B6D7H;J<M=M>RBUDXG[I^K_L8B@?FEaMhSkUmWoYpYs\ya|c~eAAAGCCEEEICCIEELFFEHGFIHIIIMHHILKIOMNNNRNNTOOKQOPQQVRRVUUUXW[]\]]]kAAnAApBBvEEyHHKK`]]^a_bbbfeennnpoorqqvuuyyy}}}MMOOPPTTVVXXZZ]]``bbcceehhkkmmqqrrttvvxxyy||~~hnpvxy}ہ݃鉉싋/ P6pLbx1Qq,/KPip1Qq/-P?pRcv1Qqϑܱ/Pp!&,>X1qQq:~aX9 ^4UVr3g {gua] hdwRfvbi0fd{rY[ 5_zTijZ9ic@=PL/o3\S9/A`ZW"CBo7HT2<lGWk8#+.KN#7yP *;Q)mF+%+>(,Ikn8ttO/I{|//O/I{|8EEO}'|8D6+BC}7J8p.$&7xx?xx|3c3qssxs( 1,,E++F..H//N00Q::W::TF XH ]LTHZL*SK+TL3RK:UO6WP:VQgSiUmWr[s\zaZEE\FFSOOTPPoBBjKKqJJwKKzII{MM|NNbPPfRRcTTnSSnUUj^^o^^qQQ{QQJa\Tb_Uca[ge_libbbgccmcciiiqbbreeujjvllktrrppuqqvttyww|wwqyw~~~VVZZ^^mmyyqy~}܂戈 =1[Qyq/"P0p=LYgx1Qq&/@PZpt1Qq/&PAp[tϩ1Qq/P"p0>M[iy1Qqұ/Pp  >1\Qzq/Pp!+6@IZ1pQq/ P6pLbx1Qq,/KPip1Qq/-P?pRcv1Qqϑܱ/Pp!&,>X1qQq/0NJ P KONOO:5ONOOI  LONJOOH*BC!-HOOLUF<)D@K1?TW+#$=A.+WW6#WV9A"WV9CSEGS(WW*<3;3>(W,,7&RS%A%R43''! '<?G]](0` [6R*!Z )cOxj/%%WF{WEM k E7g{b+" C*"~ewI: 9<( lVu^%{!WEso?30d D6tlVf S<0}dt`MB5nm8--diTaM&%fz`M V8 L=w{mW#I= nXh-$!.%h~s\1'X) TC~yTC OWeQ|c7, bC5pYum  i,#jzG9 ;lt"Uw_fQjAkVo4*!8_{oH+K<|bN^7'4C''@&&3;B5ug1' [55[[zzځ߄ᅅ݃{{kkj?? tg.%lxQA GA pBBفuuNNa::Z55MMddᅅ܂h== Ix`xF8LG.mm}JJ!J,,ww{HHq9bOoX1' [8!!zzKK )!_4tt닋`88. I;|c3) b%ppvv .Y55yy,~:.p}d<0mMPPoBB+x qqm@@' _)!lVA3u*ބ;##2KKss m-oH!4d3LLqq f7 cc^^ @) /{,B, Vsscc 5  !@zHH8!!BMM/3݃XX2333+ @߄NN=$$BTT3X44쌌LL@߄NN=$$BTT3i>>vFF@߄NN=$$BTT3xGGoBB55hNN=$$553 55jTT3855LLj??yNN=$$TT3|IIm@@ ]]bbbb}}oobbbbT11XXbbbbmmbbbbU33nAAtEELL-戈MM`99{II銊}JJ+ރ~KKA&&ㆆSSU``[),,[[_88,, ',,__V33,+ g zz__%9  rNN=$$`TT3=__kkOY SSc;;$@߄NN=$$BTT3<##||0ㆆOO$@߄NN=$$BTT3hjjW33!J,,OO$@߄NN=$$BTT3D((ہ``giccOO$@߄NN=$$BTT3?d;;牉O..c U[66싋OO$@b::-Bi>>%ud;;ᅅځO.. xV33OO$<?uM..qqㆆ]]D((  @&&UUㆆW33$<EE)=EE&LO//ii~~艉ꊊㆆۂ}}mmLL;## #w M..b99qBB~JJxGGnAA\66B''%w<%G{}K/GwGwGwGw?GwGwGwGwGw?Gw?GwGwGwGwGw?Gw`<GwGwGwGwpGw0Gw8GwGwGwGwGw7Gw?Gw?Gw?GwGwGwGwGwGwGwGw?Gw?Gw?Gw?Gw?Gw?Gw?GwGw?GwGw( @ a 84!bNJ; K8#_Lh) &f C6npF8F[ <0pqZ&"1!s\zn6+0`M~y^K3)x`kV~>@3opY1' }hS}d1'. oYxmW1'U:QAzXGc G ( i) >1z|c"!M*"fyUD _7VhfQ* w{bjT!I%E((f<1 kT@&&zzj?? 7 ``qq )/%hvM=1'ttqq A=$$߃^77v 7B@3V=*_@%%WW ӝ]]wFF&  dJA TTnAAq+)P//ei>> γjjZ55\ +,T22eoBB $vvM--Q9,T22o&oBB *||G**N=,T22oBB 'yyJ++OYL--qqyyssMM ݷkkss݃vv``!!ssP//TY`99cc艉{{* ``b::c? D((h== KK,xFFyHHF))0u5W,T225,.FoBB 75A닋cc  eeZ55c+,T22eoBB VV}}137.Z55c+,T22eoBB דWWbb e1/` ШccZ55c* )c6 Vؚ[[[[B'' =$$ZZ싋E))ct2 VY#$MMeessxxttmm``OOF)) 0K "(&  ^4??00p x!0!!!??(  9#lV@3J VDr[ r[x`'gSy2(8mP@+"HHL=cO ~0.)2(}q%("*PP[[zHHf<<v!rK<_W33yy L*]]Z zadP$ `99uEE7 f|X443V^>%%2a>%%zHH%n@&&R@&&avFF&-ZZ戈mm戈oBBK,,8!!  K,, u K,, t܂pBBoBB.@&&2a@&&1戈<##;""ᅅ( %H  {Q00vEEqCCP// 6A?AAAAAAAAAAAAAAAcppcheck-1.90/gui/cppcheck_de.ts000066400000000000000000003135231357737443600166410ustar00rootroot00000000000000 About About Cppcheck Über Cppcheck Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Ein Werkzeug zur statischen C/C++-Code-Analyse. Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2019 Cppcheck-Team. This program is licensed under the terms of the GNU General Public License version 3 Dieses Programm ist unter den Bedingungen der GNU General Public License Version 3 lizenziert Visit Cppcheck homepage at %1 Besuchen Sie die Cppcheck-Homepage unter %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> <html><head/><body> <p>Vielen Dank für die von uns genutzten Bibliotheken:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Anwendung hinzufügen Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Hier können Sie Anwendungen hinzufügen, die Codedateien öffnen können. Geben Sie den Namen der Anwendung, deren ausführbare Datei und Kommandozeilenparameter für die Ausführung an. Die folgenden Texte in Parametern werden durch die passenden Werte ersetzt, wenn die Anwendung ausgeführt wird: (file) - Name der Datei, die den Fehler enthält (line) - Zeile, die den Fehler enthält (message) - Fehlermeldung (severity) - Schweregrad des Fehlers Beispiel: Öffnen einer Datei mit Kate, automatisch zur korrekten Zeile scrollen: Ausführbare Datei: kate Parameter: -l(line) (file) &Name: &Name: &Executable: &Ausführbare Datei: &Parameters: &Parameter: Browse Suchen Executable files (*.exe);;All files(*.*) Ausführbare Dateien (*.exe);;Alle Dateien(*.*) Select viewer application Anzeigeanwendung auswählen Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Sie müssen einen Namen, einen Pfad und ggf. Parameter für die Anwendung angeben! FileViewDialog Could not find the file: %1 Konnte die Datei nicht finden: %1 Cppcheck Cppcheck Could not read the file: %1 Konnte die Datei nicht lesen: %1 LibraryAddFunctionDialog Add function Funktion hinzufügen Function name(s) Funktionsname(n) Number of arguments Anzahl Argumente LibraryDialog Library Editor Bibliothekseditor Open Öffnen Save Speichern Save as Speichern unter Functions Funktionen Sort Sortiere Add Hinzufügen Filter: Filter: Comments Kommentare noreturn Zurückkehrend False Ja True Nein Unknown Unbekannt return value must be used Rückgabewert muss genutzt werden ignore function in leaks checking Ignoriere Funktion in Speicherleck-Prüfung Arguments Argumente Edit Bearbeiten Library files (*.cfg) Bibliotheksdateien (*.cfg) Open library file Bibliothek öffnen Cppcheck Cppcheck Cannot open file %1. Datei %1 kann nicht geöffnet werden. Failed to load %1. %2. %1 kann nicht geladen werden. %2. Cannot save file %1. Datei %1 kann nicht gespeichert werden. Save the library as Speichere Bibliothek unter LibraryEditArgDialog Edit argument Argument bearbeiten <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> <html><head/><body> <p>Ist ein boolscher Wert, beispielsweise das Ergebnis eines Vergleichsoperators, oder von '!' zulässig?</p> <p>Diese Option wird typischerweise gesetzt, wenn das Argument ein Zeiger, eine Größe, etc. ist.</p> <p>Beispiel:</p> <pre> memcmp(x, y, i == 123); // Das letzte Argument sollte kein boolscher Wert sein.</pre> </body></html> Not bool Nicht boolsch <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> <html><head/><body> <p>Ist die Übergabe von Null zulässig?</p> <p>Dies wird typischerweise für Funktionen mit Zeigern als Parameter genutzt, die nicht Null sein dürfen.</p> <p>Beispiel:</p> <pre> strcpy(x,y); // Weder x noch y dürfen ein Nullzeiger sein.</pre> </body></html> Not null Nicht Null Not uninit Nicht uninitialisiert String String Format string Formatstring Min size of buffer Minimale Puffergröße Type Typ None Keine argvalue Argumentwert mul Multiplikation strlen strlen Arg Argument 1 Arg2 Argument 2 and und Valid values Zulässige Werte MainWindow Cppcheck Cppcheck Standard Standard &File &Datei &View &Ansicht &Toolbars &Symbolleisten A&nalyze A&nalysieren C++ standard C++-Standard &C standard &C-Standard &Edit &Bearbeiten &License... &Lizenz... A&uthors... &Autoren... &About... Ü&ber... &Files... &Dateien... Analyze files Analysiere Dateien Ctrl+F Strg+F &Directory... &Verzeichnis... Analyze directory Analysiere Verzeichnis Ctrl+D Strg+D Ctrl+R Strg+R &Stop &Stoppen Stop analysis Analyse abbrechen Esc Esc &Save results to file... &Ergebnisse in Datei speichern... Ctrl+S Strg+S &Quit &Beenden &Clear results Ergebnisse &löschen &Preferences &Einstellungen Show errors Zeige Fehler Show warnings Zeige Warnungen Show performance warnings Zeige Performance-Warnungen Show &hidden Zeige &versteckte Information Information Show information messages Zeige Informationsmeldungen Show portability warnings Zeige Portabilitätswarnungen Show Cppcheck results Zeige Cppcheck-Ergebnisse Clang Clang Show Clang results Zeige Clang-Ergebnisse &Filter &Filter Filter results Gefilterte Ergebnisse Windows 32-bit ANSI Windows 32-bit, ANSI Windows 32-bit Unicode Windows 32-bit, Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit &Print... Drucken... Print the Current Report Aktuellen Bericht ausdrucken Print Pre&view... Druckvorschau Open a Print Preview Dialog for the Current Results Druckvorschaudialog für aktuelle Ergebnisse öffnen Open library editor Bibliothekseditor öffnen &Check all Alle &auswählen Filter Filter &Reanalyze modified files Veränderte Dateien neu analysieren Reanal&yze all files Alle Dateien erneut anal&ysieren Style war&nings Stilwar&nungen E&rrors F&ehler &Uncheck all Alle a&bwählen Collapse &all Alle &reduzieren &Expand all Alle &erweitern &Standard &Standard Standard items Standardeinträge Toolbar Symbolleiste &Categories &Kategorien Error categories Fehler-Kategorien &Open XML... Öffne &XML... Open P&roject File... Pr&ojektdatei öffnen... Sh&ow Scratchpad... &Zeige Schmierzettel... &New Project File... &Neue Projektdatei... &Log View &Loganzeige Log View Loganzeige C&lose Project File Projektdatei &schließen &Edit Project File... Projektdatei &bearbeiten... &Statistics &Statistik &Warnings &Warnungen Per&formance warnings Per&formance-Warnungen &Information &Information &Portability &Portabilität P&latforms P&lattformen C++&11 C++&11 C&99 C&99 &Posix Posix C&11 C&11 &C89 &C89 &C++03 &C++03 &Library Editor... &Bibliothekseditor &Auto-detect language Sprache &automatisch erkennen &Enforce C++ C++ &erzwingen E&nforce C C e&rzwingen C++14 C++14 Reanalyze and check library Neu analysieren und Bibliothek prüfen Check configuration (defines, includes) Prüfe Konfiguration (Definitionen, Includes) C++17 C++17 C++20 C++20 &Contents &Inhalte Categories Kategorien Show style warnings Zeige Stilwarnungen Open the help contents Öffnet die Hilfe-Inhalte F1 F1 &Help &Hilfe Quick Filter: Schnellfilter: Select configuration Konfiguration wählen Found project file: %1 Do you want to load this project file instead? Gefundene Projektdatei: %1 Möchten Sie stattdessen diese öffnen? File not found Datei nicht gefunden Bad XML Fehlerhaftes XML Missing attribute Fehlendes Attribut Bad attribute value Falscher Attributwert Duplicate platform type Plattformtyp doppelt Platform type redefined Plattformtyp neu definiert Failed to load the selected library '%1'. %2 Laden der ausgewählten Bibliothek '%1' schlug fehl. %2 License Lizenz Authors Autoren Save the report file Speichert die Berichtdatei XML files (*.xml) XML-Dateien (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. Beim Laden der Editor-Anwendungseinstellungen trat ein Problem auf. Dies wurde vermutlich durch einen Wechsel der Cppcheck-Version hervorgerufen. Bitte prüfen (und korrigieren) Sie die Einstellungen, andernfalls könnte die Editor-Anwendung nicht korrekt starten. You must close the project file before selecting new files or directories! Sie müssen die Projektdatei schließen, bevor Sie neue Dateien oder Verzeichnisse auswählen! The library '%1' contains unknown elements: %2 Die Bibliothek '%1' enthält unbekannte Elemente: %2 Unsupported format Nicht unterstütztes Format Unknown element Unbekanntes Element Unknown issue Unbekannter Fehler Error Fehler Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Laden von %1 fehlgeschlagen. Ihre Cppcheck-Installation ist defekt. Sie können --data-dir=<Verzeichnis> als Kommandozeilenparameter verwenden, um anzugeben, wo die Datei sich befindet. Bitte beachten Sie, dass --data-dir in Installationsroutinen genutzt werden soll, und die GUI bei dessen Nutzung nicht startet, sondern die Einstellungen konfiguriert. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Aktuelle Ergebnisse werden gelöscht. Das Einlesen einer XML-Datei löscht die aktuellen Ergebnisse. Fortfahren? Open the report file Berichtdatei öffnen Text files (*.txt) Textdateien (*.txt) CSV files (*.csv) CSV-Dateien (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Projektdateien (*.cppcheck);;Alle Dateien(*.*) Select Project File Projektdatei auswählen Project: Projekt: No suitable files found to analyze! Keine passenden Dateien für Analyse gefunden! C/C++ Source C/C++-Quellcode Compile database Compilerdatenbank Visual Studio Visual Studio Borland C++ Builder 6 Borland C++-Builder 6 Select files to analyze Dateien für Analyse auswählen Select directory to analyze Verzeichnis für Analyse auswählen Select the configuration that will be analyzed Zu analysierende Konfiguration auswählen Found project files from the directory. Do you want to proceed analysis without using any of these project files? Projektdateien im Verzeichnis gefunden. Wollen sie fortfahren, ohne diese Projektdateien zu nutzen? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Aktuelle Ergebnisse werden gelöscht. Eine neue XML-Datei zu öffnen wird die aktuellen Ergebnisse löschen Möchten sie fortfahren? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? Analyse läuft. Wollen sie die Analyse abbrechen und Cppcheck beenden? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML-Dateien (*.xml);;Textdateien (*.txt);;CSV-Dateien (*.csv) Build dir '%1' does not exist, create it? Erstellungsverzeichnis '%1' existiert nicht. Erstellen? Failed to import '%1', analysis is stopped Import von '%1' fehlgeschlagen; Analyse wurde abgebrochen. Project files (*.cppcheck) Projektdateien (*.cppcheck) Select Project Filename Projektnamen auswählen No project file loaded Keine Projektdatei geladen The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Die Projektdatei %1 konnte nicht gefunden werden! Möchten Sie die Datei von der Liste der zuletzt benutzten Projekte entfernen? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONEN] [Dateien oder Pfade] Options: -h, --help Gibt diese Hilfeinformationen aus -p <file> Öffnet das angegebene Projekt und beginnt die Prüfung -l <file> Öffnet die angegebene XML-Ergebnisdatei -d <directory> Gibt das Verzeichnis an, das geprüft wurde, um das unter -l angegebene XML-Ergebnis zu erzeugen -v, --version Zeigt Programmversion an --data-dir=<directory> Gibt das Verzeichnis an, unter dem sich die Konfigurationsdateien für die GUI (Übersetzungen, Cfg) befinden. Die GUI startet bei Nutzung dieser Option nicht. Cppcheck GUI - Command line parameters Cppcheck GUI - Kommandozeilenparameter NewSuppressionDialog New suppression Neue Fehlerunterdrückung Error ID Fehler-ID File name Dateiname Line number Zeilennummer Symbol name Symbolname Edit suppression Fehlerunterdrückung bearbeiten Platforms Native Nativ Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit, ANSI Windows 32-bit Unicode Windows 32-bit, Unicode Windows 64-bit Windows 64-bit ProjectFile Project File Projektdatei Paths and Defines Pfade und Definitionen Import Project (Visual studio / compile database/ Borland C++ Builder 6) Importiere Projekt (Visual Studio / Compile-Datenbank / Borland C++-Builder 6) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Definitionen müssen mit einem Semikolon getrennt werden. Beispiel: DEF1;DEF2=5;DEF3=int Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Hinweis: Legen Sie eigene .cfg-Dateien in den Ordner der Projektdatei. Dann sollten sie oben sichtbar werden. Addons and tools Addons und Werkzeuge MISRA C 2012 MISRA C 2012 Misra rule texts Misra-Regeltexte <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Text aus Anhang A &quot;Summary of guidelines&quot; aus der MISRA-C-2012-PDF in eine Textdatei einfügen.</p></body></html> ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>Sie haben die Auswahl:</p><p> * Alle Debug- und Release-Konfigurationen analysieren</p><p> * Nur die erste passende Debug-Konfiguration analysieren</p><p><br/></p></body></html> Browse... Durchsuchen... Analyze all Visual Studio configurations Alle Visual-Studio-Konfigurationen analysieren Paths: Pfade: Add... Hinzufügen... Edit Bearbeiten Remove Entfernen Undefines: Un-Definitionen: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Un-Definitionen müssen Semikolon-getrennt sein. Beispiel: UNDEF1;UNDEF2;UNDEF3 Include Paths: Includepfade: Up Auf Down Ab Checking Prüfung Platform Plattform Analysis Analyse Check code in headers (slower analysis, more results) Prüfe Code in Headern (langsamere Analyse, mehr Ergebnisse) Check code in unused templates (slower and less accurate analysis) Prüfe Code in ungenutzten Templates (langsamere und weniger genaue Analyse) Max CTU depth Maximale CTU-Tiefe Warning options Warnoptionen Root path: Wurzelverzeichnis: Warning tags (separated by semicolon) Warnungs-Tags (Semikolon-getrennt) Exclude source files in paths Quelldateien in Pfaden ausschließen Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. Hinweis: Addons setzen voraus, dass <a href="https://www.python.org/">Python</a> installiert ist. External tools Externe Werkzeuge Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck-Arbeitsverzeichnis (Vollständige Programmanalyse, inkrementelle Analyse, Statistiken, etc.) Libraries Bibliotheken Suppressions Fehlerunterdrückungen Add Hinzufügen Addons Add-Ons Y2038 Y2038 Thread safety Threadsicherheit Coding standards Programmierstandards Cert Cert Clang analyzer Clang-Analyzer Clang-tidy Clang-Tidy Defines: Definitionen: ProjectFileDialog Project file: %1 Projektdatei: %1 Select Cppcheck build dir Wähle Cppcheck-Erstellungsverzeichnis Select include directory Wähle Include-Verzeichnisse Select a directory to check Wähle zu prüfendes Verzeichnis (no rule texts file) (keine Regeltexte) Clang-tidy (not found) Clang-tidy (nicht gefunden) Visual Studio Visual Studio Compile database Compilerdatenbank Borland C++ Builder 6 Borland C++-Builder 6 Import Project Projekt importieren Select directory to ignore Wähle zu ignorierendes Verzeichnis Select MISRA rule texts file Wähle MISRA-Regeltext-Datei Misra rule texts file (%1) MISRA-Regeltext-Datei QDialogButtonBox OK OK Cancel Abbrechen Close Schließen Save Speichern QObject Unknown language specified! Unbekannte Sprache angegeben! Language file %1 not found! Sprachdatei %1 nicht gefunden! Failed to load translation for language %1 from file %2 Die Übersetzungen der Sprache %1 konnten nicht aus der Datei %2 geladen werden line %1: Unhandled element %2 Zeile %1: Nicht behandeltes Element %2 (Not found) (nicht gefunden) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Klassen-Vordergrundfarbe Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK OK Cancel Abbrechen Close Schließen Save Speichern ResultsTree File Datei Severity Schweregrad Line Zeile Summary Zusammenfassung Undefined file Undefinierte Datei Copy Kopieren Could not find file: Kann Datei nicht finden: Please select the folder '%1' Bitte wählen Sie den Ordner '%1' Select Directory '%1' Wähle Verzeichnis '%1' Please select the directory where file is located. Bitte wählen Sie das Verzeichnis, wo sich die Datei befindet debug Debug note Anmerkung Recheck Erneut prüfen Hide Verstecken Hide all with id Verstecke alle mit gleicher ID Suppress selected id(s) Ausgewählte ID(s) unterdrücken Open containing folder Übergeordneten Ordner öffnen Tag Tag No tag Kein Tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Keine Editor-Anwendung eingestellt. Konfigurieren Sie diese unter Einstellungen/Anwendungen. No default editor application selected. Please select the default editor application in preferences/Applications. Keine Standard-Editor-Anwendung eingestellt. Bitte wählen Sie eine Standardanwendung unter Einstellungen/Anwendungen. Could not find the file! Datei konnte nicht gefunden werden! Could not start %1 Please check the application path and parameters are correct. %1 konnte nicht gestartet werden. Bitte überprüfen Sie ob der Pfad und die Parameter der Anwendung richtig eingestellt sind. Select Directory Wähle Verzeichnis Id Id Inconclusive Unklar Since date Seit Datum style Stil error Fehler warning Warnung performance Performance portability Portabilität information Information ResultsView Print Report Bericht drucken No errors found, nothing to print. Keine Funde, nichts zu drucken. %p% (%1 of %2 files checked) %p% (%1 von %2 Dateien geprüft) Cppcheck Cppcheck No errors found. Keine Fehler gefunden. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Es wurden Fehler gefunden, aber sie sind so konfiguriert, ausgeblendet zu werden. Legen Sie unter dem Menü Ansicht fest, welche Arten von Fehlern angezeigt werden sollen. Failed to read the report. Lesen des Berichts fehlgeschlagen. XML format version 1 is no longer supported. XML-Format-Version 1 wird nicht länger unterstützt. First included by Zuerst inkludiert von Id Id Clear Log Protokoll leeren Copy this Log entry Diesen Protokolleintrag kopieren Copy complete Log Gesamtes Protokoll kopieren No errors found, nothing to save. Keine Fehler gefunden, nichts zu speichern. Failed to save the report. Der Bericht konnte nicht speichern werden. Results Berichte Analysis Log Analyseprotokoll Warning Details Warnungs-Details ScratchPad Scratchpad Schmierzettel Copy or write some C/C++ code here: Kopieren oder schreiben Sie C/C++-Code hierher: Optionally enter a filename (mainly for automatic language detection) and click on "Check": Optional einen Dateinamen (hauptsächlich für automatische Spracherkennung) eingeben und auf "Check" klicken: filename Dateiname Check Prüfe Settings Preferences Einstellungen General Allgemein Add... Hinzufügen... Number of threads: Anzahl der Threads: Ideal count: Ideale Anzahl: Force checking all #ifdef configurations Erzwinge Prüfung aller #ifdef-Konfigurationen Show full path of files Vollständigen Dateipfad anzeigen Show "No errors found" message when no errors found "Keine Fehler gefunden"-Meldung anzeigen, wenn keine Fehler gefunden werden Display error Id in column "Id" Zeige Meldungs-Id in Spalte "Id" Enable inline suppressions Inline-Fehlerunterdrückung aktivieren Check for inconclusive errors also Auch nach unklaren Fehlern suchen Show statistics on check completion Zeige Statistiken nach Prüfungsabschluss Show internal warnings in log Interne Warnungen im Log anzeigen Addons Add-Ons Python binary (leave this empty to use python in the PATH) Python-Binärdatei (Python aus PATH wird genutzt, wenn leer) ... ... Misra addon MISRA-Addon Misra rule texts file MISRA-Regeltext-Datei <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Text aus Anhang A &quot;Summary of guidelines&quot; aus der MISRA-C-2012-PDF in eine Textdatei einfügen.</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clang-Verzeichnis (PATH wird genutzt, wenn leer) Visual Studio headers Visual-Studio-Header <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Pfade zu Visual-Studio-Headern, Semikolon-getrennt.</p><p>Sie können eine Visual-Studio-Kommandozeile öffnen, &quot;SET INCLUDE&quot; eingeben und dann die Pfade hier reinkopieren.</p></body></html> Code Editor Code-Editor Code Editor Style Code-Editor-Stil System Style Systemstil Default Light Style Heller Standardstil Default Dark Style Dunkler Standardstil Custom Benutzerdefiniert Remove Entfernen Applications Anwendungen Edit... Bearbeiten... Set as default Als Standard festlegen Reports Berichte Save all errors when creating report Alle Fehler beim Erstellen von Berichten speichern Save full path to files in reports Vollständigen Dateipfad in Berichten speichern Language Sprache SettingsDialog N/A kA Add a new application Neue Anwendung hinzufügen Modify an application Anwendung ändern [Default] [Standard] [Default] [Standard] Select python binary Python-Binärdatei auswählen Select MISRA File Wähle MISRA-Datei Select clang path Clang-Verzeichnis auswählen StatsDialog Statistics Statistik Project Projekt Project: Projekt: Paths: Pfade: Include paths: Include-Pfade: Defines: Definitionen: Undefines: Un-Definitionen: Previous Scan Vorherige Prüfung Path Selected: Ausgewählte Pfade: Number of Files Scanned: Anzahl der geprüften Dateien: Scan Duration: Prüfungsdauer: Errors: Fehler: Warnings: Warnungen: Stylistic warnings: Stilwarnungen: Portability warnings: Portabilitätswarnungen: Performance issues: Performance-Probleme: Information messages: Informationsmeldungen: History Verlauf File: Datei: Copy to Clipboard In die Zwischenablage kopieren Pdf Export PDF-Export 1 day 1 Tag %1 days %1 Tage 1 hour 1 Stunde %1 hours %1 Stunden 1 minute 1 Minute %1 minutes %1 Minuten 1 second 1 Sekunde %1 seconds %1 Sekunden 0.%1 seconds 0,%1 Sekunden and und Export PDF Exportiere PDF Project Settings Projekteinstellungen Paths Pfade Include paths Include-Pfade Defines Definitionen Undefines Un-Definitionen Path selected Gewählte Pfade Number of files scanned Anzahl geprüfter Dateien Scan duration Prüfungsdauer Errors Fehler File: Datei: No cppcheck build dir Kein Cppcheck-Analyseverzeichnis Warnings Warnungen Style warnings Stilwarnungen Portability warnings Portabilitätswarnungen Performance warnings Performance-Warnungen Information messages Informationsmeldungen ThreadResult %1 of %2 files checked %1 von %2 Dateien geprüft TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Wechsel der Sprache der Benutzeroberfläche fehlgeschlagen: %1 Die Sprache wurde auf Englisch zurückgesetzt. Öffnen Sie den Einstellungen-Dialog um eine verfügbare Sprache auszuwählen. Cppcheck Cppcheck TxtReport inconclusive unklar toFilterString All supported files (%1) Alle unterstützten Dateien (%1) All files (%1) Alle Dateien (%1) cppcheck-1.90/gui/cppcheck_es.ts000066400000000000000000003263311357737443600166610ustar00rootroot00000000000000 About About Cppcheck Acerca de Cppcheck Version %1 Versión %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Una utilidad para el análisis estático de código C/C++. Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2019 el equipo de cppcheck. This program is licensed under the terms of the GNU General Public License version 3 Este programa está licenciado bajo los términos de GNU General Public License versión 3 Visit Cppcheck homepage at %1 Visita el sitio de Cppcheck en %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Añade una aplicación Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) &Name: &Nombre: &Executable: &Ejecutable: &Parameters: &Parámetros: Browse Buscar Executable files (*.exe);;All files(*.*) Archivos ejecutables (*.exe);;Todos los archivos(*.*) Select viewer application Selecciona la aplicación para visualizar Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! ¡Debes especificar el nombre, la ruta y opcionalmente los parámetros para la aplicación! FileViewDialog Could not find the file: %1 No se ha encontrado el fichero: %1 Cppcheck Cppcheck Could not read the file: %1 No se ha podido leer el fichero: %1 LibraryAddFunctionDialog Add function Añadir función Function name(s) Nombre(s) de la función Number of arguments Número de argumentos LibraryDialog Library Editor Editor de bibliotecas Open Abrir Save Guardar Save as Functions Funciones Sort Add Añadir Filter: Comments noreturn False Falso True Verdadero Unknown Desconocido return value must be used ignore function in leaks checking Arguments Argumentos Edit Editar Library files (*.cfg) Archivos de biblioteca (*.cfg) Open library file Abrir archivo de biblioteca Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument Editar argumento <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool No bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null No null Not uninit No uninit String String Format string Min size of buffer Type Tipo None Ninguno argvalue mul strlen Arg Arg2 and Valid values Valores válidos LogView Checking Log Comprobando el log Clear Limpiar Save Log Guardar el log Text files (*.txt *.log);;All files (*.*) Archivos de texto (*.txt *.log);;Todos los archivos(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" No se pudo abrir el fichero para escritura: "%1" MainWindow Cppcheck Cppcheck &File &Archivo &View &Ver &Toolbars &Herramientas &Help &Ayuda &Check &Comprobar C++ standard C++ estándar &C standard C standard C estándar &Edit &Editar Standard Estándar Categories Categorías &License... &Licencia... A&uthors... A&utores... &About... &Acerca de... &Files... &Ficheros... Analyze files Check files Comprobar archivos Ctrl+F Ctrl+F &Directory... &Carpeta... Analyze directory Check directory Comprobar carpeta Ctrl+D Ctrl+D &Recheck files &Volver a revisar ficheros Ctrl+R Ctrl+R &Stop &Detener Stop analysis Stop checking Detener comprobación Esc Esc &Save results to file... &Guardar los resultados en el fichero... Ctrl+S Ctrl+S &Quit &Salir &Clear results &Limpiar resultados &Preferences &Preferencias Style warnings Advertencias de estilo Show style warnings Mostrar advertencias de estilo Errors Errores Show errors Mostrar errores Show S&cratchpad... Mostrar S&cratchpad... Information Información Show information messages Mostrar mensajes de información Portability Portabilidad Show portability warnings Mostrar advertencias de portabilidad Show Cppcheck results Clang Show Clang results &Filter &Filtro Filter results Resultados del filtro Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms Plataformas C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Im&primir... Print the Current Report Imprimir el informe actual Print Pre&view... Pre&visualización de impresión... Open a Print Preview Dialog for the Current Results Abre el diálogo de previsualización de impresión para el informe actual Library Editor... Editor de bibliotecas... Open library editor Abrir el editor de bibliotecas &Check all &Seleccionar todo A&nalyze Filter Filtro &Reanalyze modified files &Recheck modified files Reanal&yze all files Style war&nings E&rrors &Uncheck all &Deseleccionar todo Collapse &all Contraer &todo &Expand all &Expandir todo &Standard &Estándar Standard items Elementos estándar &Contents &Contenidos Open the help contents Abrir la ayuda de contenidos F1 F1 Toolbar Barra de herramientas &Categories &Categorías Error categories Categorías de error &Open XML... &Abrir XML... Open P&roject File... Abrir P&royecto... Sh&ow Scratchpad... &New Project File... &Nuevo Proyecto... &Log View &Visor del log Log View Visor del log C&lose Project File C&errar Proyecto &Edit Project File... &Editar Proyecto... &Statistics &Estadísticas &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 Warnings Advertencias Show warnings Mostrar advertencias Performance warnings Advertencias de rendimiento Show performance warnings Mostrar advertencias de rendimiento Show &hidden Mostrar &ocultos There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. No suitable files found to check! ¡No se han encontrado ficheros para comprobar! You must close the project file before selecting new files or directories! ¡Tienes que cerrar el proyecto antes de seleccionar nuevos ficheros o carpetas! Select directory to check Selecciona una carpeta para comprobar Select configuration File not found Archivo no encontrado Bad XML XML malformado Missing attribute Falta el atributo Bad attribute value Unsupported format Formato no soportado Failed to load the selected library '%1'. %2 XML files (*.xml) Archivos XML (*.xml) Open the report file Abrir informe Checking is running. Do you want to stop the checking and exit Cppcheck? El proceso de comprobación está en curso. ¿Quieres parar la comprobación y salir del Cppcheck? License Licencia Authors Autores XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) Archivos XML versión 2 (*.xml);;Archivos XML versión 1 (*.xml);;Archivos de texto (*.txt);;Archivos CSV (*.csv) Save the report file Guardar informe Quick Filter: Filtro rápido: Select files to check Selecciona los archivos a comprobar Found project file: %1 Do you want to load this project file instead? Se encontró el fichero de proyecto: %1 ¿Quiere cargar este fichero de proyecto en su lugar? Found project files from the directory. Do you want to proceed checking without using any of these project files? Se encontraron ficheros de proyecto en el directorio. ¿Quiere proceder a comprobar sin utilizar ninguno de estos ficheros de proyecto? The library '%1' contains unknown elements: %2 La biblioteca '%1' contiene elementos deconocidos: %2 Duplicate platform type Platform type redefined Unknown element Unknown issue Error Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Los resultados actuales serán eliminados. Abrir un nuevo fichero XML eliminará los resultados actuales. ¿Desea continuar? XML files version 1 (*.xml) Archivos XML versión 1 (*.xml) XML files version 2 (*.xml) Archivos XML versión 2 (*.xml) Text files (*.txt) Ficheros de texto (*.txt) CSV files (*.csv) Ficheros CVS (*.cvs) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Ficheros de proyecto (*.cppcheck;;Todos los ficheros (*.*) Select Project File Selecciona el archivo de proyecto Project: Proyecto: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename Selecciona el nombre del proyecto No project file loaded No hay ningún proyecto cargado The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? ¡El fichero de proyecto %1 no puede ser encontrado! ¿Quiere eliminar el fichero de la lista de proyectos recientes? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in Built-in Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. No se ha podido leer el fichero. Could not write the project file. No se ha podido escribir el fichero de proyecto. ProjectFile Project File Archivo de proyecto Project Proyecto Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' &Root: Root: Raíz: Libraries: Bibliotecas: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Nota: Ponga sus propios archivos .cfg en la misma carpeta que el proyecto. Debería verlos arriba. MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Paths: Rutas: Add... Añadir... Edit Editar Remove Eliminar Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Exclude source files in paths Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. External tools Includes Incluir Include directories: Incluir los directorios: Up Subir Down Bajar Checking Platform Warning options Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Excluir Suppressions Supresiones Suppression list: Lista de supresiones: Add Añadir Addons and tools Addons Y2038 Thread safety Coding standards Cert Clang analyzer Clang-tidy Defines: Definiciones: ProjectFileDialog Project file: %1 Archivo de proyecto: %1 Select Cppcheck build dir Select include directory Selecciona una carpeta para incluir Select a directory to check Selecciona la carpeta a comprobar (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Selecciona la carpeta a ignorar Add Suppression Añadir supresión Select MISRA rule texts file Misra rule texts file (%1) QDialogButtonBox OK Aceptar Cancel Cancelar Close Cerrar Save Guardar QObject Unknown language specified! ¡Idioma especificado desconocido! Language file %1 not found! ¡Fichero de idioma %1 no encontrado! Failed to load translation for language %1 from file %2 Fallo al cargar la traducción para el idioma %1 desde el fichero %2 line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Aceptar Cancel Cancelar Close Cerrar Save Guardar ResultsTree File Archivo Severity Severidad Line Línea Summary Resumen Undefined file Fichero no definido Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [No concluyente] portability portabilidad note information información debug depuración Recheck Copy filename Copiar nombre del archivo Copy full path Copiar ruta completa Copy message Copiar mensaje Copy message id Copiar id del mensaje Hide Ocultar Hide all with id Ocultar todos con el mismo id Suppress selected id(s) Open containing folder Abrir carpeta contenedora Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. No se ha configurado una aplicación para editar. Configura el programa para editar en Preferencias/Aplicaciones. No default editor application selected. Please select the default editor application in preferences/Applications. No se ha definido una aplicación para editar prefeterminada. Configura el programa para editar por defecto en Preferencias/Aplicaciones. Could not find the file! ¡No se ha encontrado el fichero! Could not start %1 Please check the application path and parameters are correct. No se ha podido ejecutar %1 Por favor comprueba que la ruta a la aplicación y los parámetros son correctos. Could not find file: %1 Please select the directory where file is located. No se ha encontrado el fichero: %1 Por favor selecciona la carpeta donde se encuentra. Select Directory Selecciona carpeta Id Id Inconclusive Since date style estilo error error warning advertencia performance ajuste ResultsView Results Resultados Analysis Log Warning Details No errors found, nothing to save. No se han encontrado errores, nada que guardar. Failed to save the report. Error al guardar el informe. Print Report Imprimir informe No errors found, nothing to print. No se encontraron errores, nada que imprimir. %p% (%1 of %2 files checked) %p% (%1 of %2 archivos comprobados) Cppcheck Cppcheck No errors found. No se han encontrado errores. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Se han encontrado errores, pero están configurados para que no se muestren. Para cambiar el tipo de comportamiento, abra el menú Ver. Failed to read the report. Error al leer el informe. XML format version 1 is no longer supported. Summary Resumen Message Mensaje First included by Id Id Clear Log Copy this Log entry Copy complete Log ScratchPad Scratchpad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename nombre de archivo Check Comprobar Settings Preferences Preferencias General General Ideal count: Cantidad ideal: Force checking all #ifdef configurations Forzar comprobación de todas las configuraciones #ifdef Display error Id in column "Id" Mostrar el Id del error en la columna "Id" Enable inline suppressions Habilitar supresiones inline Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths Rutas Include paths: Rutas incluidas: Add... Añadir... Edit... Editar... Set as default Definir por defecto Language Idioma Advanced Avanzado &Show inconclusive errors &Mostrar errores no concluyentes S&how internal warnings in log M&ostrar advertencias internas en el log Number of threads: Número de hilos: Show full path of files Mostrar la ruta completa de los ficheros Show "No errors found" message when no errors found Mostrar el mensaje "No se han encontrado errores" Edit Editar Remove Eliminar Applications Aplicaciones Reports Informes Save all errors when creating report Guardar todos los errores cuando se cree el informe Save full path to files in reports Guardar la ruta completa en los ficheros de informes SettingsDialog N/A N/A Add a new application Añadir una nueva aplicación Modify an application Modificar una aplicación [Default] [Default] [Predeterminada] Select python binary Select MISRA File Select clang path Select include directory Seleccionar carpeta a incluir StatsDialog Statistics Estadísticas Project Proyecto Project: Proyecto: Paths: Rutas: Include paths: Incluye las rutas: Defines: Definiciones: Undefines: Previous Scan Análisis anterior Path Selected: Ruta seleccionada: Number of Files Scanned: Número de archivos analizados: Scan Duration: Duración del análisis: Errors: Errores: Warnings: Advertencias: Stylistic warnings: Advertencias de estilo: Portability warnings: Advertencias de portabilidad: Performance issues: Problemas de rendimiento: Information messages: Mensajes de información: History File: Copy to Clipboard Copiar al portapapeles Pdf Export 1 day 1 día %1 days %1 días 1 hour 1 hora %1 hours %1 horas 1 minute 1 minuto %1 minutes %1 minutos 1 second 1 segundo %1 seconds %1 segundos 0.%1 seconds 0.%1 segundos and y Export PDF Project Settings Preferencias del proyecto Paths Rutas Include paths Incluye las rutas Defines Definiciones Undefines Path selected Ruta seleccionada Number of files scanned Número de archivos analizados Scan duration Duración del análisis Errors Errores File: No cppcheck build dir Warnings Advertencias Style warnings Advertencias de estilo Portability warnings Advertencias de portabilidad Performance warnings Advertencias de rendimiento Information messages Mensajes de información ThreadResult %1 of %2 files checked %1 de %2 archivos comprobados TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Ocurrió un error al cambiar el idioma de la interfaz gráfica: %1 El idioma de la interfaz gráfica ha sido cambiado a Inglés. Abra la ventana de Preferencias para seleccionar alguno de los idiomas disponibles. Cppcheck Cppcheck TxtReport inconclusive no concluyente toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppcheck_fi.ts000066400000000000000000003071221357737443600166450ustar00rootroot00000000000000 About About Cppcheck Tietoa ohjelmasta Cppcheck Version %1 Versio %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Työkalu C/C++ koodin staattiseen analysointiin. Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2018 Cppcheck team. Copyright (C) 2007-2019 Daniel Marjamäki ja cppcheck tiimi. This program is licensed under the terms of the GNU General Public License version 3 Tämä ohjelma on lisensoitu GNU General Public lisenssin version 3 alaisuuteen Visit Cppcheck homepage at %1 Cppcheckin kotisivu löytyy osoitteesta %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Lisää uusi ohjelma Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) &Name: &Executable: &Parameters: Browse Selaa Executable files (*.exe);;All files(*.*) Suoritettavat tiedostot (*.exe);;Kaikki tiedostot(*.*) Select viewer application Valitse ohjelma jolla avata virhetiedosto Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! FileViewDialog Could not find the file: %1 Could not find the file: Tiedostoa %1 ei löytynyt Cppcheck Cppcheck Could not read the file: %1 Tiedoston %1 lukeminen epäonnistui LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Cppcheck Cppcheck MainWindow Cppcheck Cppcheck A&nalyze Standard Vakio &File &Tiedosto &View &Näytä &Toolbars &Check &Tarkista C++ standard &C standard C standard &Edit &Muokkaa &License... &Lisenssi... A&uthors... &Tekijät... &About... &Tietoa ohjelmasta Cppcheck... &Files... &Tiedostot... Analyze files Check files Ctrl+F Ctrl+F &Directory... &Hakemisto... Analyze directory Check directory Ctrl+D Ctrl+D &Recheck files Tarkista tiedostot &uudelleen Ctrl+R Ctrl+R &Stop &Pysäytä Stop analysis Stop checking Esc Esc &Save results to file... &Tallenna tulokset tiedostoon... Ctrl+S Ctrl+S &Quit &Lopeta &Clear results &Tyhjennä tulokset &Preferences &Asetukset Show errors Show warnings Show performance warnings Show &hidden Information Show information messages Show portability warnings Show Cppcheck results Clang Show Clang results &Filter Filter results Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor &Check all &Valitse kaikki Filter &Reanalyze modified files &Recheck modified files Reanal&yze all files Style war&nings E&rrors &Uncheck all &Poista kaikista valinta Collapse &all &Pienennä kaikki &Expand all &Laajenna kaikki &Standard Standard items Toolbar &Categories Error categories &Open XML... Open P&roject File... Sh&ow Scratchpad... &New Project File... &Log View Log View C&lose Project File &Edit Project File... &Statistics &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++20 &Contents Categories Show style warnings Open the help contents F1 &Help &Ohje Select directory to check Valitse tarkistettava hakemisto No suitable files found to check! Tarkistettavaksi sopivia tiedostoja ei löytynyt! Quick Filter: Select configuration Found project file: %1 Do you want to load this project file instead? File not found Bad XML Missing attribute Bad attribute value Failed to load the selected library '%1'. %2 License Lisenssi Authors Tekijät XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML-tiedostot (*.xml);;Tekstitiedostot (*.txt);;CSV-tiedostot (*.csv) Save the report file Tallenna raportti XML files (*.xml) XML-tiedostot (*xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. You must close the project file before selecting new files or directories! Select files to check Valitse tarkistettavat tiedostot The library '%1' contains unknown elements: %2 Unsupported format Duplicate platform type Platform type redefined Unknown element Unknown issue Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Open the report file Text files (*.txt) Tekstitiedostot (*.txt) CSV files (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Select Project File Project: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename No project file loaded The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Native Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Project Cppcheck Cppcheck ProjectFile Project File Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Addons and tools MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Paths: Add... Edit Remove Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Up Down Checking Platform Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Warning options Root path: Warning tags (separated by semicolon) Exclude source files in paths Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. External tools Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Suppressions Add Addons Y2038 Thread safety Coding standards Cert Clang analyzer Clang-tidy Defines: ProjectFileDialog Project file: %1 Select Cppcheck build dir Select include directory Select a directory to check (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Select MISRA rule texts file Misra rule texts file (%1) QDialogButtonBox OK Cancel Close Save QObject Unknown language specified! Language file %1 not found! Language file %1.qm not found! Käännöstiedostoa %1 ei löytynyt! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2.qm Käänöksen lataaminen kielelle %1 tiedostosta %2 epäonnistui line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Close Save ResultsTree File Tiedosto Severity Tyyppi Line Rivi Summary Undefined file Määrittelemätön tiedosto Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. debug note Recheck Copy filename Kopioi tiedostonimi Copy full path Kopioi tiedoston koko polku Hide Hide all with id Suppress selected id(s) Open containing folder Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. Voit asetuksista määritellä muita ohjelmia joilla avata tämän virheen sisältävän tiedoston. No default editor application selected. Please select the default editor application in preferences/Applications. Could not find the file! Could not start %1 Please check the application path and parameters are correct. Ohjelman %1 käynnistäminen epäonnistui Tarkista että ohjelman polku ja parametrit ovat oikeat. Select Directory Id Inconclusive Since date style Tyyli error Yleinen warning performance portability information ResultsView Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) Cppcheck Cppcheck No errors found. Virheitä ei löytynyt. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Virheitä löytyi, mutta asetuksissa kyseiset virheet on määritelty piilotettavaksi. Määrittääksesi minkä tyyppisiä virheitä näytetään, avaa näkymä valikko. Failed to read the report. XML format version 1 is no longer supported. First included by Id Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. Virheitä ei löytynyt, ei mitään tallennettavaa. Failed to save the report. Raportin tallentaminen epäonnistui. Results Tulokset Analysis Log Warning Details ScratchPad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename Check Settings Preferences Asetukset General Yleiset Add... Number of threads: Säikeiden lukumäärä: Ideal count: Force checking all #ifdef configurations Check all #ifdef configurations Tarkista kaikki #ifdef kombinaatiot Show full path of files Näytä tiedostojen täysi polku Show "No errors found" message when no errors found Näytä "virheitä ei löytynyt"-viesti jos virheitä ei löydy Display error Id in column "Id" Enable inline suppressions Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Remove Applications Ohjelmat Edit... Set as default Reports Raportit Save all errors when creating report Tallenna kaikki virheet raporttia luodessa Save full path to files in reports Tallenna tiedostojen koko polku raportteihin Language SettingsDialog N/A Add a new application Lisää uusi ohjelma Modify an application Muokkaa ohjelmaa [Default] [Default] Select python binary Select MISRA File Select clang path StatsDialog Statistics Project Project: Paths: Include paths: Defines: Undefines: Previous Scan Path Selected: Number of Files Scanned: Scan Duration: Errors: Warnings: Stylistic warnings: Portability warnings: Performance issues: Information messages: History File: Copy to Clipboard Pdf Export 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Export PDF Project Settings Paths Include paths Defines Undefines Path selected Number of files scanned Scan duration Errors File: No cppcheck build dir Warnings Style warnings Portability warnings Performance warnings Information messages ThreadResult %1 of %2 files checked TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Cppcheck Cppcheck TxtReport inconclusive toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppcheck_fr.ts000066400000000000000000003163131357737443600166600ustar00rootroot00000000000000 About About Cppcheck A propos Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Un outil d'analyse statique de code C/C++. This program is licensed under the terms of the GNU General Public License version 3 Ce programme est sous licence GNU General Public License version 3 Visit Cppcheck homepage at %1 Visitez le site Cppcheck : %1 Copyright © 2007-2019 Cppcheck team. <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Ajouter une application Browse Parcourir Executable files (*.exe);;All files(*.*) Fichier exécutable (*.exe);;Tous les fichiers(*.*) Select viewer application Sélection de l'application Cppcheck &Executable: &Exécutable : &Parameters: &Paramètres : Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Vous pouvez ajouter une application capable d'ouvrir les fichiers d'erreurs. Spécifiez le nom de l'application, la commande à exécuter, ainsi que les paramètres de la commande Les textes en paramètres sont remplacés par les valeurs appropriées lorsque l'application est exécutée : (fichier) - Fichier contenant l'erreur (ligne) - Numéro de ligne contenant l'erreur (message) - Message d'erreur (sévérité) - Sévérité de l'erreur Exemple pour ouvrir un fichier avec Kate et se déplacer vers une ligne spécifique : Exécutable : kate Paramètres : -l(ligne) (fichier) &Name: &Nom : You must specify a name, a path and optionally parameters for the application! Vous devez spécifier un nom, un chemin, et eventuellement des paramètres en option à l'application ! FileViewDialog Could not find the file: %1 Ne trouve pas le fichier : %1 Cppcheck Could not read the file: %1 Ne peut pas lire le fichier : %1 LibraryAddFunctionDialog Add function Ajouter une fonction Function name(s) Nom(s) de la fonction Number of arguments Nombre d'arguments LibraryDialog Library Editor Open Save Sauvegarder Functions Add Ajouter noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Editer Library files (*.cfg) Open library file Sort Filter: Comments Save as Cppcheck Save the library as Failed to load %1. %2. Cannot open file %1. Cannot save file %1. LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values MainWindow Cppcheck &File &Fichier &View &Affichage &Help &Aide &Check &Vérifier &Edit &Édition Standard Standard &License... &Licence... A&uthors... A&uteurs... &About... À &Propos... &Files... &Fichiers... Ctrl+F &Directory... &Répertoires... Ctrl+D &Recheck files &Revérifier les fichiers Ctrl+R &Stop &Arrêter Esc &Save results to file... &Sauvegarder les résultats dans un fichier... Ctrl+S &Quit &Quitter &Clear results &Effacer les résultats &Preferences &Préférences &Check all &Tout cocher &Uncheck all &Tout décocher Collapse &all &Tout réduire &Expand all &Tout dérouler &Contents &Contenus Open the help contents Ouvir l'aide F1 No suitable files found to check! Pas de fichiers trouvés à vérifier ! Select directory to check Sélectionner le répertoire à vérifier License Licence Authors Auteurs Save the report file Sauvegarder le rapport XML files (*.xml) Fichiers XML (*.xml) Text files (*.txt) Fichiers Texte (*.txt) CSV files (*.csv) Fichiers CSV (*.csv) Cppcheck - %1 &Toolbars &Boite à outils Categories Catégories Check files Vérifier les fichiers Check directory Vérifier un répertoire Stop checking Arrêter la vérification Style warnings Avertissement de style Show style warnings Afficher les avertissements de style Errors Erreurs Show errors Afficher les erreurs &Standard Standard items Toolbar &Categories Error categories &Open XML... &Ouvrir un fichier XML... Open P&roject File... Ouvrir un P&rojet... &New Project File... &Nouveau Projet... &Log View &Journal Log View Journal C&lose Project File F&ermer le projet &Edit Project File... &Editer le projet &Statistics Statistiques Warnings Avertissements Show warnings Afficher les avertissements Performance warnings Avertissements de performance Show performance warnings Afficher les avertissements de performance Show &hidden Information Information Show information messages Afficher les messages d'information Portability Portabilité Show portability warnings Afficher les problèmes de portabilité You must close the project file before selecting new files or directories! Vous devez d'abord fermer le projet avant de choisir des fichiers/répertoires Open the report file Ouvrir le rapport Checking is running. Do you want to stop the checking and exit Cppcheck? Vérification en cours. Voulez-vous arrêter la vérification et quitter CppCheck ? Project files (*.cppcheck);;All files(*.*) Select Project File Select Project Filename No project file loaded There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. &Filter &Filtre Filter results Quick Filter: Filtre rapide : Found project file: %1 Do you want to load this project file instead? Project: Projet : The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Filter Filtre Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Les résultats courant seront effacés. L'ouverture d'un nouveau fichier XML effacera les resultats. Voulez-vous confirmar l'opération ? Select files to check Sélectionner les fichiers à vérifier Cppcheck GUI - Command line parameters C++ standard Error Erreur File not found Fichier introuvable Bad XML Mauvais fichier XML Missing attribute Attribut manquant Bad attribute value Mauvaise valeur d'attribut Failed to load the selected library '%1'. %2 Echec lors du chargement de la bibliothèque '%1'. %2 Unsupported format Format non supporté The library '%1' contains unknown elements: %2 La bibliothèque '%1' contient des éléments inconnus: %2 Duplicate platform type Platform type redefined &Print... &Imprimer... Print the Current Report Imprimer le rapport Print Pre&view... Apercu d'impression... Open a Print Preview Dialog for the Current Results Open library editor Auto-detect language Auto-detection du langage &Recheck modified files &Revérifier les fichiers modifiés &Recheck all files &Revérifier tous les fichiers Unknown element Unknown issue Select configuration Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Build dir '%1' does not exist, create it? Analyze files Analyze directory &Reanalyze modified files Stop analysis XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) No suitable files found to analyze! Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? A&nalyze &C standard Reanal&yze all files Style war&nings E&rrors Sh&ow Scratchpad... &Warnings Per&formance warnings &Information &Portability Show Cppcheck results Clang Show Clang results P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 Failed to import '%1', analysis is stopped Project files (*.cppcheck) Reanalyze and check library Check configuration (defines, includes) C++17 C++20 C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Native Project Could not read the project file. Impossible de lire le fichier projet. Could not write the project file. Impossible d'écrire dans le fichier projet. ProjectFile Project File Fichier Projet Paths: Chemins : Defines: Project Projet Add... Ajouter... Edit Editer Remove Supprimer Includes Inclusions Include directories: Inclure les répertoires Root: Répertoire racine Up Monter Down Descendre Exclude Exclure Libraries: Bibliothèques Suppressions Suppressions Suppression list: Liste de suppressions Add Ajouter Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. ... Include Paths: Paths and Defines <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Analyze all Visual Studio configurations Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Addons Y2038 Thread safety Coding standards Cert Clang analyzer Clang-tidy Browse... Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Checking Platform Warning options Addons and tools MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> External tools Import Project (Visual studio / compile database/ Borland C++ Builder 6) Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Exclude source files in paths Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. ProjectFileDialog Project file: %1 Fichier projet : %1 Select include directory Selectionner un répertoire à inclure Select directory to ignore Selectionner un répertoire à ignorer Select a directory to check Selectionner un répertoire à vérifier Select Cppcheck build dir Import Project Clang-tidy (not found) (no rule texts file) Select MISRA rule texts file Misra rule texts file (%1) Visual Studio Compile database Borland C++ Builder 6 QDialogButtonBox OK OK Cancel Annuler Close Fermer Save Sauvegarder QObject Language file %1 not found! Fichier de langue %1 non trouvé ! Failed to load translation for language %1 from file %2 Erreur lors du chargement de la langue %1 depuis le fichier %2 Unknown language specified! line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark Class Foreground Color QPlatformTheme OK OK Cancel Annuler Close Fermer Save Sauvegarder ResultsTree File Fichier Severity Sévérité Line Ligne Undefined file Fichier indéterminé Copy filename Copier le nom du fichier Copy full path Copier le chemin complet Copy message Copier le message Cppcheck Could not start %1 Please check the application path and parameters are correct. Ne peut pas démarrer %1 Merci de vérifier que le chemin de l'application et que les paramètres sont corrects. style erreur de style error erreur Summary Résumé Hide Cacher Could not find the file! Fichier introuvable ! Could not find file: %1 Please select the directory where file is located. Fichier introuvable: %1 Veuillez sélectionner le répertoire où est situé le fichier. Select Directory Selectionner dossier warning avertissement performance performance portability portabilité information information debug débogage No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. No default editor application selected. Please select the default editor application in preferences/Applications. Id Id Copy message id Copier l'identifiant du message Hide all with id Open containing folder Ouvrir l'emplacement du fichier Inconclusive Recheck Revérifier note Suppress selected id(s) Tag No tag Since date Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. Copy ResultsView Results Résultats Cppcheck No errors found. Pas d'erreurs trouvées. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Des erreurs ont été trouvées mais sont configurées pour rester cachées. Pour configurer les erreurs affichées, ouvrez le menu d'affichage. No errors found, nothing to save. Pas d'erreurs trouvées, rien à sauvegarder. Failed to save the report. Erreur lors de la sauvegarde du rapport. Failed to read the report. Erreur lors de la lecture du rapport Summary Résumé Message Message %p% (%1 of %2 files checked) %p% (%1 fichiers sur %2 vérifiés) Id Id Print Report Imprimer le rapport No errors found, nothing to print. Aucune erreur trouvée. Il n'y a rien à imprimer First included by XML format version 1 is no longer supported. Analysis Log Warning Details Clear Log Copy this Log entry Copy complete Log ScratchPad Scratchpad filename Check Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": Settings Preferences Préférences General Général Number of threads: Nombre de fils : Show full path of files Montrer le chemin complet des fichiers Show "No errors found" message when no errors found Afficher un message "Pas d'erreur trouvée" lorsque aucune erreur est trouvée Applications Applications Reports Rapports Save all errors when creating report Sauvegarder toutes les erreurs lorsqu'un rapport est créé Save full path to files in reports Sauvegarder le chemin complet des fichiers dans les rapports Include paths: Inclure les chemins Add... Ajouter... Ideal count: Force checking all #ifdef configurations Enable inline suppressions Language Langue Paths Chemins Edit Editer Remove Supprimer Edit... Editer... Set as default Display error Id in column "Id" Afficher l'identifiant d'erreur Id dans la colonne "Id" Check for inconclusive errors also Show internal warnings in log Montrer les avertissements internes dans le journal Show statistics on check completion Addons Python binary (leave this empty to use python in the PATH) ... Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Code Editor Code Editor Style Default Light Style Default Dark Style Custom System Style SettingsDialog Add a new application Ajouter une nouvelle application Modify an application Modifier une application N/A Select include directory Selectionner un répertoire à inclure [Default] [Default] Select python binary Select clang path Select MISRA File StatsDialog Statistics Statistiques Project Projet Project: Projet : Paths: Chemins : Include paths: Inclure les chemins : Defines: Previous Scan Analyse précédente Path Selected: Chemin sélectionné : Number of Files Scanned: Nombre de fichiers analysés : Scan Duration: Durée de l'analyse : Errors: Erreurs : Warnings: Avertissements Stylistic warnings: Avertissements de style Portability warnings: Avertissements de portabilité Performance issues: Problème de performance Information messages: Messages d'information : Copy to Clipboard Copier vers le presse-papier 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Project Settings Paths Chemins Include paths Defines Path selected Number of files scanned Scan duration Errors Erreurs Warnings Avertissements Style warnings Avertissement de style Portability warnings Performance warnings Avertissements de performance Information messages Pdf Export Export PDF History File: File: No cppcheck build dir Undefines: Undefines ThreadResult %1 of %2 files checked TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Cppcheck TxtReport inconclusive toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppcheck_it.ts000066400000000000000000003275641357737443600166770ustar00rootroot00000000000000 About About Cppcheck Informazioni su Cppcheck Version %1 Versione %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Uno strumento per l'analisi statica di codice C/C++ Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2019 il team Cppcheck. This program is licensed under the terms of the GNU General Public License version 3 Questo programma è rilasciato sotto i termini della GNU General Public License versione 3 Visit Cppcheck homepage at %1 Visita la pagina iniziale di Cppcheck all'indirizzo: %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Aggiungi un'applicazione Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Qui puoi aggiungere un'applicazione che può aprire i file contenente l'errore. Specifica un nome per l'applicazione, l'eseguibile dell'applicazione e i parametri di comando per l'applicazione. I seguenti testi nei parametri sono sostituiti con appropriati valori quando l'applicazione è eseguita: (file) - Il nome del file contenente l'errore (line) - La linea contenente l'errore (message) - Messaggio d'errore (severity) - Severità dell'errore Un'esempio di apertura di un file con Kate e lo scorrimento alla giusta linea: Eseguibile: kate Parametri: -l(line) (file) &Name: &Nome: &Executable: &Eseguibile: &Parameters: &Parametri: Browse Esplora Executable files (*.exe);;All files(*.*) File di esecuzione (*.exe);;Tutti i file(*.*) Select viewer application Seleziona l'applicazione di lettura Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Devi specificare un nome, un percorso ed, opzionalmente, i parametri per l'applicazione! FileViewDialog Could not find the file: %1 File non trovato: %1 Cppcheck Cppcheck Could not read the file: %1 Non è stato possibile leggere il file: %1 LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Modifica Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log Log sulla scansione Clear Cancella Save Log Salva il rapporto Text files (*.txt *.log);;All files (*.*) File di testo (*.txt *.log);;Tutti i files(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Non è stato possibile aprire il file per la scrittura: "%1" MainWindow Cppcheck Cppcheck A&nalyze Standard Standard &File &File &View &Visualizza &Toolbars &Barre degli strumenti &Check &Scansiona C++ standard &C standard C standard &Edit &Modifica &License... &Licenza... A&uthors... A&utori... &About... I&nformazioni su... &Files... &File... Analyze files Check files Scansiona i file Ctrl+F Ctrl+F &Directory... &Cartella... Analyze directory Check directory Scansiona la cartella Ctrl+D Ctrl+D &Recheck files &Riscansiona i file Ctrl+R Ctrl+R &Stop &Ferma Stop analysis Stop checking Ferma la scansione Esc Esc &Save results to file... &Salva i risultati nel file... Ctrl+S Ctrl+S &Quit &Esci &Clear results &Cancella i risultati &Preferences &Preferenze Errors Errori Show errors Mostra gli errori Show S&cratchpad... Mostra il blocchetto per appunti... Warnings Avvisi Show warnings Mostra gli avvisi Performance warnings Avvisi sulle prestazioni Show performance warnings Mostra gli avvisi sulle prestazioni Show &hidden Mostra &i nascosti Information Informazione Show information messages Mostra messaggi di informazione Portability Portabilità Show portability warnings Mostra gli avvisi sulla portabilità Show Cppcheck results Clang Show Clang results &Filter &Filtro Filter results Filtra i risultati Windows 32-bit ANSI Windows 32-bit, ANSI Windows 32-bit Unicode Windows 32-bit, Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms Piattaforme C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor &Check all &Seleziona tutto Filter Filtro &Reanalyze modified files &Recheck modified files Reanal&yze all files Style war&nings E&rrors &Uncheck all &Deseleziona tutto Collapse &all Riduci &tutto &Expand all &Espandi tutto &Standard &Standard Standard items Oggetti standard Toolbar Barra degli strumenti &Categories &Categorie Error categories Categorie di errore &Open XML... &Apri XML... Open P&roject File... Apri file di p&rogetto... Sh&ow Scratchpad... &New Project File... &Nuovo file di progetto... &Log View &Visualizza il rapporto Log View Visualizza il rapporto C&lose Project File C&hiudi il file di progetto &Edit Project File... &Modifica il file di progetto... &Statistics &Statistiche &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 &Contents &Contenuti Categories Categorie Style warnings Avvisi sullo stile Show style warnings Mostra gli avvisi sullo stile Open the help contents Apri i contenuti di aiuto F1 F1 &Help &Aiuto Select directory to check Seleziona una cartella da scansionare No suitable files found to check! Nessun file trovato idoneo alla scansione! Quick Filter: Rapido filtro: Select configuration Found project file: %1 Do you want to load this project file instead? Trovato il file di progetto: %1 Vuoi piuttosto caricare questo file di progetto? Found project files from the directory. Do you want to proceed checking without using any of these project files? Trovati file di progetto dalla directory. Vuoi procedere alla scansione senza usare qualcuno di questi file di progetto? File not found Bad XML Missing attribute Bad attribute value Unsupported format Failed to load the selected library '%1'. %2 License Licenza Authors Autori XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) File XML Versione 2 (*.xml);;File XML Versione 1 (*.xml);;File di testo (*.txt);;File CSV (*.csv) Save the report file Salva il file di rapporto XML files (*.xml) File XML (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. C'è stato un problema con il caricamento delle impostazioni delle applicazioni editor. Probabilmente ciò è avvenuto perché le impostazioni sono state modificate tra le versioni di Cppcheck. Per favore controlla (e sistema) le impostazioni delle applicazioni editor, altrimenti il programma editor può non partire correttamente. You must close the project file before selecting new files or directories! Devi chiudere il file di progetto prima di selezionare nuovi file o cartelle! Select files to check Seleziona i file da scansionare The library '%1' contains unknown elements: %2 Duplicate platform type Platform type redefined Unknown element Unknown issue Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? I risultati correnti verranno ripuliti. L'apertura di un nuovo file XML ripulirà i risultati correnti. Vuoi procedere? Open the report file Apri il file di rapporto Checking is running. Do you want to stop the checking and exit Cppcheck? La scansione è in esecuzione. Vuoi fermare la scansione ed uscire da Cppcheck? XML files version 1 (*.xml) Files XML versione 1 (*.xml) XML files version 2 (*.xml) Files XML versione 2 (*.xml) Text files (*.txt) File di testo (*.txt) CSV files (*.csv) Files CSV (*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Fallito il tentativo di cambio della lingua dell'interfaccia utente: %1 L'interfaccia utente è stata risettata in Inglese. Apri la finestra di dialogo Preferenze per selezionare una qualunque lingua a disposizione. Project files (*.cppcheck);;All files(*.*) Files di progetto (*.cppcheck);;Tutti i files(*.*) Select Project File Seleziona il file di progetto Project: Progetto: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename Seleziona il nome del file di progetto No project file loaded Nessun file di progetto caricato The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Il file di progetto %1 non è stato trovato! Vuoi rimuovere il file dalla lista dei progetti recentemente usati? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in Built-in Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit, ANSI Windows 32-bit Unicode Windows 32-bit, Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. Non è stato possibile leggere il file di progetto. Could not write the project file. Non è stato possibile scrivere il file di progetto. ProjectFile Project File File di progetto Project Progetto Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' &Root: Root: Root: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Paths: Percorsi: Add... Aggiungi... Edit Modifica Remove Rimuovi Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Exclude source files in paths Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. External tools Includes Inclusioni Include directories: Cartelle di inclusione: Up Su Down Giù Checking Platform Warning options Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Escludi Suppressions Add Addons and tools Addons Y2038 Thread safety Coding standards Cert Clang analyzer Clang-tidy Defines: Definizioni: ProjectFileDialog Project file: %1 File di progetto: %1 Select Cppcheck build dir Select include directory Seleziona la cartella da includere Select a directory to check Seleziona una cartella da scansionare (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Seleziona la cartella da ignorare Select MISRA rule texts file Misra rule texts file (%1) QDialogButtonBox OK Cancel Close Chiudi Save QObject Unknown language specified! Lingua specificata sconosciuta! Language file %1 not found! Il file di lingua %1 non trovato! Failed to load translation for language %1 from file %2 Fallito il tentativo di aprire la traduzione per la lingua %1 dal file %2 line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Close Chiudi Save ResultsTree File File Severity Severità Line Linea Summary Riassunto Undefined file File indefinito Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [Inconcludente] debug debug note Recheck Copy filename Copia nome file Copy full path Copia tutto il percorso Copy message Copia messaggio Copy message id Copia id del messaggio Hide Nascondi Hide all with id Suppress selected id(s) Open containing folder Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Nessun'applicazione di scrittura è stato configurato. Configura l'applicazione di scrittura per Cppcheck in Preferenze/Applicazioni. No default editor application selected. Please select the default editor application in preferences/Applications. Nessun'applicazione di scrittura predefinito Keine è stato selezionato. Per favore seleziona l'applicazione di scrittura predefinito in Preferenze/Applicazioni. Could not find the file! Non è stato possibile trovare il file! Could not start %1 Please check the application path and parameters are correct. Non è stato possibile avviare %1 Per favore verifica che il percorso dell'applicazione e i parametri siano corretti. Could not find file: %1 Please select the directory where file is located. Non è stato possibile trovare il file: %1 Per favore selezioa la cartella dove il file è posizionato. Select Directory Seleziona Cartella Id Id Inconclusive Since date style stile error errore warning avviso performance performance portability portabilità information Informazione ResultsView Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) %p% (%1 su %2 file scansionati) Cppcheck Cppcheck No errors found. Nessun errore trovato. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Sono stati trovati errori, ma sono stati configurati per essere nascosti. Per vedere il tipo di errori che sono mostrati, apri il menu Visualizza. Failed to read the report. Apertura del report fallito. XML format version 1 is no longer supported. Summary Riassunto Message Messaggio First included by Id Id Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. Nessun errore trovato, nulla da salvare. Failed to save the report. Salvataggio del report fallito. Results Risultati Analysis Log Warning Details ScratchPad Scratchpad Blocchetto per appunti Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename Nome file Check Scansiona Settings Preferences Preferenze General Generale Include paths: Percorsi d'inclusione: Add... Aggiungi... Number of threads: Numero di threads: Ideal count: Numero ideale: Force checking all #ifdef configurations Forza la scansione di tutte le configurazioni #ifdef Show full path of files Mostra tutto il percorso dei files Show "No errors found" message when no errors found Mostra il messaggio "Nessun errore trovato" quando nessun errore è stato trovato Display error Id in column "Id" Mostra l'id dell'errore nella colonna "Id" Enable inline suppressions Abilita le soppressioni Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths Percorsi Edit Modifica Remove Rimuovi Applications Applicazioni Edit... Modifica... Set as default Imposta come predefinito Reports Rapporti Save all errors when creating report Salva tutti gli errori quando viene creato il rapporto Save full path to files in reports Salva tutto il percorso ai files nei rapporti Language Lingua Advanced Avanzate &Show inconclusive errors &Mostra errori inconcludenti S&how internal warnings in log &Mostra avvisi interni nel log SettingsDialog N/A N/A Add a new application Aggiungi una nuova applicazione Modify an application Modifica un'applicazione [Default] [Default] [Predefinito] Select python binary Select MISRA File Select clang path Select include directory Seleziona la cartella da includere StatsDialog Statistics Statistiche Project Progetto Project: Progetto: Paths: Percorsi: Include paths: Percorsi di inclusione: Defines: Definizioni: Undefines: Previous Scan Precedente Scansione Path Selected: Selezionato percorso: Number of Files Scanned: Numero di Files Scansionati: Scan Duration: Durata della scansione: Errors: Errori: Warnings: Avvisi: Stylistic warnings: Avvisi sullo stile: Portability warnings: Avvisi sulla portabilità: Performance issues: Casi sulla performance: Information messages: Messaggi di informazione: History File: Copy to Clipboard Copia negli Appunti Pdf Export 1 day 1 giorno %1 days %1 giorni 1 hour 1 ora %1 hours %1 ore 1 minute 1 minuto %1 minutes %1 minuti 1 second 1 secondo %1 seconds %1 secondi 0.%1 seconds 0,%1 secondi and e Export PDF Project Settings Impostazioni progetto Paths Percorsi Include paths Percorsi di inclusione Defines Definizioni Undefines Path selected Selezionato percorso Number of files scanned Numero di file scansionati Scan duration Durata della scansione Errors Errori File: No cppcheck build dir Warnings Avvisi Style warnings Stilwarnungen Portability warnings Avvisi sulla portabilità Performance warnings Avvisi sulle performance Information messages Messaggi di informazione ThreadResult %1 of %2 files checked %1 su %2 file scansionati TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Fallito il tentativo di cambio della lingua dell'interfaccia utente: %1 L'interfaccia utente è stata risettata in Inglese. Apri la finestra di dialogo Preferenze per selezionare una qualunque lingua a disposizione. Cppcheck Cppcheck TxtReport inconclusive inconcludente toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppcheck_ja.ts000066400000000000000000003533401357737443600166440ustar00rootroot00000000000000 About About Cppcheck cppcheckについて Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. CppcheckはC/C++ 静的コード解析ツールです. Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2019 Cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 本ソフトウェアはGNU General Public License Version3 ライセンスの元で配布されます Visit Cppcheck homepage at %1 Cppcheckのホームページはこちら %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> <html><head/><body> <p>私達は以下のライブラリを使用しています。ここで感謝の意を表明します。</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application アプリケーションの追加 Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) fix Japanese word 名前 to 表示名 ここにエラー指摘のあるファイルを開くアプリケーションを追加できます。そのアプリケーションの表示名、実行ファイル名、コマンドラインパラメータを指定してください。 パラメータ中の以下の文字列を使用してパラメータ(Parameters:)に設定します。これらの文字列はアプリケーションが実行されたときに、適切な値に変換されます。: (file) - エラー指摘のあるファイル (line) - エラー指摘のある行 (message) - エラー指摘メッセージ (severity) - エラー指摘重大度 Kate テキストエディタでファイルを開き、該当する行に移動する例: Executable: kate Parameters: -l(line) (file) &Name: 表示名(&N): &Executable: 実行ファイルのパス(&E): &Parameters: パラメータ(&P): Browse 参照 Executable files (*.exe);;All files(*.*) 実行ファイル (*.exe);;すべてのファイル(*.*) Select viewer application 表示アプリケーションの選択 Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! アプリケーションの表示名と実行ファイルのパスと(オプションの)引数を指定してください! FileViewDialog Could not find the file: %1 ファイル:%1 が見つかりません Cppcheck Cppcheck Could not read the file: %1 ファイル:%1 が読み込めません LibraryAddFunctionDialog Add function 関数の追加 Function name(s) 関数の名称 Number of arguments 引数の数 LibraryDialog Library Editor ライブラリエディタ Open 開く Save 保存する Save as 別名で保存する Functions 関数 Sort ソート Add 追加 Filter: フィルタ: Comments コメント noreturn noreturn(返り値なし) False False(偽) True True(真) Unknown Unknown(不明) return value must be used 返り値は使用されなければならない ignore function in leaks checking リークの解析中に無視する関数 Arguments Arguments(引数) Edit 編集 Library files (*.cfg) ライブラリファイル(*.cfg) Open library file ライブラリファイルを開く Cppcheck Cppcheck Cannot open file %1. Can not open file %1. ファイルが見つかりません %1。 Failed to load %1. %2. 読み込みに失敗しました(%1.%2)。 Cannot save file %1. Can not save file %1. ファイルが保存できません %1。 Save the library as このライブラリに名前をつけて保存する LibraryEditArgDialog Edit argument 引数の編集 <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> <html><head/><body> <p>ブール値は許可されていますか? 例えば、比較の結果または '!' 演算子</p> <p>典型的に引数がポインタやサイズを表す場合、これを設定します。</p> <p>例:</p> <pre> memcmp(x, y, i == 123); // 最後の引数は、ブール型であってはならない </body></html> Not bool 非ブール型 非bool値 <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> <html><head/><body> <p>null値が許可されていますか?</p> <p>典型的には、nullを渡してはいけないポインタのパラメータに使用してください。</p> <p>例:</p> <pre> strcpy(x,y); // x も y も null であってはならない。</pre> </body></html> Not null 非NULL Not uninit 未初期化 String 文字列 Format string フォーマット文字列 Min size of buffer バッファの最小サイズ Type Type(型) None None(無) argvalue argvalue(引数の値) constant constant(定数) mul mul(積) strlen strlen(文字数) Arg Arg(引数) Arg2 Arg2(第二引数) and and(和) Valid values 妥当な値 LogView Checking Log Cppcheck ログ Clear 消去 Save Log ログ保存 Text files (*.txt *.log);;All files (*.*) テキストファイル (*.txt *.log);;すべてのファイル(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" ファイルを書き込みできない MainWindow Cppcheck Cppcheck &File ファイル(&F) &View 表示(&V) &Toolbars ツールバー(&T) &Help ヘルプ(&H) &Check 解析(&A) C++ standard C++標準 &C standard C standard &C標準 &Edit 編集(&E) Standard 言語規格 Categories カテゴリ &License... ライセンス(&L)... A&uthors... 作者(&u)... &About... Cppcheckについて(&A)... &Files... ファイル選択(&F)... Analyze files Check files ファイルをチェックする Ctrl+F Ctrl+F &Directory... ディレクトリ選択(&D)... Analyze directory Check directory ディレクトリをチェックする Ctrl+D Ctrl+D &Recheck files 再チェック(&R) Ctrl+R Ctrl+R &Reanalyze all files &Recheck all files 全ファイル再チェック &Stop 停止(&S) Stop analysis Stop checking チェックを停止する Esc Esc &Save results to file... 結果をファイルに保存(&S)... Ctrl+S Ctrl+S &Quit 終了(&Q) &Clear results 結果をクリア(&C) &Preferences 設定(&P) Style warnings スタイル警告 Show style warnings スタイル警告を表示 Errors エラー Show errors エラーを表示 Show S&cratchpad... スクラッチパッドを表示 Information 情報 Show information messages 情報メッセージを表示 Portability 移植可能性 Show portability warnings 移植可能性の問題を表示 Show Cppcheck results Cppcheck結果を表示する Clang Clang Show Clang results Clangの結果を表示 &Filter フィルター(&F) Filter results フィルタ結果 Windows 32-bit ANSI Windows 32-bit ANSIエンコード Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms プラットフォーム C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... 印刷(&P)... Print the Current Report 現在のレポートを印刷 Print Pre&view... 印刷プレビュー(&v)... Open a Print Preview Dialog for the Current Results 現在のレポートをプレビュー表示 Library Editor... ライブラリの編集 Open library editor ライブラリエディタを開く Auto-detect language 言語を自動検出 C&lose Project File プロジェクトを閉じる(&l) &Edit Project File... プロジェクトの編集(&E)... &Statistics 統計情報(&S) Warnings 警告 Show warnings 警告を表示 Performance warnings パフォーマンス警告 Show performance warnings パフォーマンス警告を表示 Show &hidden 非表示を表示(&h) &Check all すべてのエラーを表示(&C) A&nalyze チェック(&n) Filter フィルター &Reanalyze modified files &Recheck modified files 変更ありファイルを再解析(&R) Reanal&yze all files 全ファイル再解析(&y) Style war&nings スタイル警告(&n) E&rrors エラー(&r) &Uncheck all すべてのエラーを非表示(&U) Collapse &all ツリーを折り畳む(&a) &Expand all ツリーを展開(&E) &Standard 言語規格(&S) Standard items 標準項目 &Contents コンテンツ(&C) Open the help contents ヘルプファイルを開く F1 F1 Toolbar ツールバー &Categories カテゴリ(&C) Error categories エラーカテゴリ &Open XML... XMLを開く(&O)... Open P&roject File... プロジェクトを開く(&R)... Sh&ow Scratchpad... スクラッチパッドを表示(&o)... &New Project File... 新規プロジェクト(&N)... &Log View ログを表示(&L) Log View ログ表示 &Warnings 警告(&W) Per&formance warnings パフォーマンス警告(&f) &Information 情報(&I) &Portability 移植可能性(&P) P&latforms プラットフォーム(&l) C++&11 C++11(&1) C&99 C99(&9) &Posix Posix(&P) C&11 C11(&1) &C89 C89(&C) &C++03 C++03(&C) &Library Editor... ライブラリエディタ(&L)... &Auto-detect language 自動言語検出(&A) &Enforce C++ C++ 強制(&E) E&nforce C C 強制(&n) C++14 C++14 Reanalyze and check library ライブラリを再チェックする Check configuration (defines, includes) チェックの設定(define、インクルード) C++17 C++17 C++20 C++20 There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. エディタアプリの設定の読み込みで問題が発生しました。 Cppcheckの古いバージョンの設定には互換性がありません。エディタアプリケーションの設定を確認して修正してください、そうしないと正しく起動できないかもしれません。 No suitable files found to check! 解析可能なファイルではありません You must close the project file before selecting new files or directories! 新しいファイル/ディレクトリをチェックするには現在のプロジェクトを閉じてください! C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) C/C++ソースコード、プロジェクトファイル、Visual Studioソリューション(%1 %2 *.sln *.vcxproj) Select directory to check チェック対象のディレクトリを選択 Quick Filter: クイックフィルタ: Select files to check チェック対象のファイルを選択 C/C++ Source (%1) C/C++ ソース (%1) Select configuration コンフィグレーションの選択 Found project file: %1 Do you want to load this project file instead? プロジェクトファイルを検出しました: %1 現在のプロジェクトの代わりにこのプロジェクトファイルを読み込んでもかまいませんか? Found project files from the directory. Do you want to proceed checking without using any of these project files? ディレクトリからプロジェクトファイルが検出されました。 これらのプロジェクトファイルを使用せずに解析を進めてもかまいませんか? The library '%1' contains unknown elements: %2 このライブラリ '%1' には次の不明な要素が含まれています。 %2 File not found ファイルがありません Bad XML 不正なXML Missing attribute 属性がありません Bad attribute value 不正な属性があります Unsupported format サポートされていないフォーマット Duplicate platform type プラットフォームの種類が重複しています Platform type redefined プラットフォームの種類が再定義されました Unknown element 不明な要素 Unknown issue 不明な課題 Failed to load the selected library '%1'. %2 選択したライブラリの読み込みに失敗しました '%1' %2 Error エラー Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. %1の読み込みに失敗しました。CppCheckのインストールに失敗しています。コマンドライン引数に --data-dir=<directory> を指定して、このファイルの場所を指定してください。 Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. %1のロードに失敗しました。あなたの Cppcheck は正しくインストールされていません。あなたは --data-dir=<directory> コマンドラインオプションでロードするファイルの場所を指定できます。ただし、この --data-dir はインストールスクリプトによってサポートされており、GUI版ではサポートされていません。全ての設定は調整済みでなければなりません。 Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? 現在の結果を作成します。 新しくXMLファイルを開くと現在の結果が削除されます。実行しますか? XML files (*.xml) XML ファイル (*.xml) Open the report file レポートを開く Checking is running. Do you want to stop the checking and exit Cppcheck? 解析中です. 解析を停止してCppcheckを終了しますか?. License ライセンス Authors 作者 XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML ファイル (*.xml);;テキストファイル (*.txt);;CSV形式ファイル (*.csv) Save the report file レポートを保存 XML files version 1 (*.xml) XMLファイルのバージョン1 XML files version 2 (*.xml) XMLファイルのバージョン2 Text files (*.txt) テキストファイル (*.txt) CSV files (*.csv) CSV形式ファイル (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) プロジェクトファイル (*.cppcheck);;すべてのファイル(*.*) Select Project File プロジェクトファイルを選択 Project: プロジェクト: No suitable files found to analyze! チェック対象のファイルがみつかりません! C/C++ Source C/C++のソースコード Compile database コンパイルデータベース Visual Studio Visual Studio Borland C++ Builder 6 Borland C++ Builder 6 Select files to analyze チェック対象のファイルを選択 Select directory to analyze チェックするディレクトリを選択してください Select the configuration that will be analyzed チェックの設定を選択 Found project files from the directory. Do you want to proceed analysis without using any of these project files? ディレクトリ内にプロジェクトファイルがありました。 みつかったプロジェクトファイルを使用せずにチェックしますか? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? 現在の結果を作成します。 新しくXMLファイルを開くと現在の結果が削除されます。実行しますか? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? チェック中です。 チェックを中断して、Cppcheckを終了しますか? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML ファイル (*.xml);;テキストファイル (*.txt);;CSVファイル (*.csv) Build dir '%1' does not exist, create it? ビルドディレクトリ'%1'がありません。作成しますか? Failed to import '%1', analysis is stopped '%1'のインポートに失敗しました。(チェック中断) Project files (*.cppcheck) プロジェクトファイル (*.cppcheck) Select Project Filename プロジェクトファイル名を選択 No project file loaded プロジェクトファイルが読み込まれていません The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? このプロジェクトファイル %1 が見つかりません。 最近使用したプロジェクトのリストからこのファイルを取り除きますか? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI. シンタックス: cppcheck-gui [OPTIONS] [files または paths] オプション: -h, --help このヘルプを表示する。 -p <file> 指定のプロジェクトファイルを開き、チェックを開始する。 -l <file> 指定の、結果XMLファイルを開く -d <directory> フォルダを指定してチェックする。これは -l オプションで 指定した、結果XMLファイルを生成する。 -v, --version バージョンを表示する。 --data-dir=<directory> GUI のデータファイル(翻訳やcfg)のあるディレクトリを指定する。このオプションを指定した場合、GUIで起動しません。 Cppcheck GUI - Command line parameters Cppcheck GUI - コマンドラインパラメータ NewSuppressionDialog New suppression 新しい指摘の抑制 Error ID エラーID File name ファイル名 Line number 行数 Symbol name シンボル名 Edit suppression 抑制の編集 Platforms Built-in ビルトイン Native ネイティブ Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSIエンコード Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. プロジェクトファイルが読み込めませんでした Could not write the project file. プロジェクトファイルが保存できませんでした ProjectFile Project File プロジェクトファイル Project プロジェクト Paths and Defines パスと定義 Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) プロジェクトのインポート(Visual studio / compile database Borland C++ Builder 6)) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' 定義(Define)はセミコロン';'で区切る必要があります。 例: DEF1;DEF2=5;DEF3=int &Root: Root: ルート: Libraries: ライブラリ Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. カスタマイズした cfgファイルを同じフォルダにプロジェクトファイルとして保存してください。ここに表示できるようになります。 Exclude paths 除外するパス MISRA C 2012 MISRA C 2012 Misra rule texts Misra ルールテキスト <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>MISRA C 2012 pdfのAppendix A &quot;Summary of guidelines&quot; からテキストをコピーペーストしてください。</p></body></html> ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>選択済み:</p><p> * 全Debug と Release設定をチェックする</p><p> * 最初にマッチした Debug 設定のみチェックする</p><p><br/></p></body></html> Browse... 参照... Analyze all Visual Studio configurations Visual Studioの全ての設定をチェックする Paths: パス: Add... 追加... Edit 編集 Remove 取り除く Undefines: 定義取り消し(Undefines): Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 定義の取り消しはセミコロンで区切ります。例: UNDEF1;UNDEF2;UNDEF3 Include Paths: インクルードパス: Analysis チェック Check code in headers (slower analysis, more results) ヘッダファイルのコードもチェック(解析に時間がかかりますが結果は増えます) Check code in unused templates (slower and less accurate analysis) 未使用テンプレートのコードもチェック (解析に時間がかかり、また正確性は低い) Max CTU depth CTUの最大深さ Exclude source files in paths 除外するソースファイルのPATH Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. 注意: アドオンには<a href="https://www.python.org/">Python</a> が必要です。 External tools 外部ツール Includes インクルード Include directories: インクルードディレクトリ: Up Down Checking チェック Platform プラットフォーム Warning options 警告オプション Root path: ルートパス: Warning tags (separated by semicolon) 警告タグ(セミコロン区切り) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck ビルドディレクトリ (全プログラムチェック, 差分チェック, 統計等) Libraries ライブラリ Exclude 除外する Suppressions 指摘の抑制 Suppression list: 抑制リスト Add 追加 Addons and tools アドオンとツール Addons アドオン Y2038 Y2038 Thread safety スレッドセーフ Coding standards コーディング標準 Cert CERT Extra Tools エクストラツール It is common best practice to use several tools. 複数ツールの併用はよい結果を生みます。 Clang analyzer Clang Analyzer Clang-tidy Clang-tidy Defines: 定義(Defines): ProjectFileDialog Project file: %1 プロジェクトファイル:%1 Select Cppcheck build dir Cppcheckビルドディレクトリ Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Visual Studio (*.sln *.vcxproj);;コンパイルデータベース (compile_commands.json) Select include directory includeディレクトリを選択 Select a directory to check チェックするディレクトリを選択してください (no rule texts file) (ルールテキストファイルがない) Clang-tidy (not found) Clang-tidy (みつかりません) Visual Studio Visual Studio Compile database コンパイルデータベース Borland C++ Builder 6 Borland C++ Builder 6 Import Project プロジェクトのインポート Select directory to ignore 除外するディレクトリを選択してください Add Suppression 抑制する指摘を追加 Select error id suppress: 抑制するエラーID(error id)を選択してください Select MISRA rule texts file MISRAルールテキストファイルを選択 Misra rule texts file (%1) Misraルールテキストファイル (%1) QDialogButtonBox OK OK Cancel キャンセル Close 閉じる Save 保存する QObject Unknown language specified! 未知の言語が指定されました! Language file %1 not found! 言語ファイル %1 が見つかりません! Failed to load translation for language %1 from file %2 言語 %2 から %1 への翻訳ファイルの読み込みに失敗 line %1: Unhandled element %2 行 %1: 扱われていない要素(Unhandled element) %2 (Not found) (見つかりません) Thin シン(細) ExtraLight エクストラライト Light ライト Normal ノーマル Medium メディウム DemiBold デミボールト Bold ボールド ExtraBold エクストラボールド Black Editor Foreground Color エディタの前景色 Editor Background Color エディタの背景色 Highlight Background Color ハイライトの背景色 Line Number Foreground Color 行番号の前景色 Line Number Background Color 行番号の背景色 Keyword Foreground Color キーワードの前景色 Keyword Font Weight キーワードのフォントのウェイト Class Foreground Color Class ForegroundColor クラスの前景色 Class Font Weight クラスフォントのウェイト Quote Foreground Color クォートの前景色 Quote Font Weight クォートのフォントウェイト Comment Foreground Color コメントの前景色 Comment Font Weight コメントフォントのウェイト Symbol Foreground Color シンボルの前景色 Symbol Background Color シンボルの背景色 Symbol Font Weight シンボルのフォントウェイト Set to Default Light デフォルトをライトに設定 Set to Default Dark デフォルトをダークに設定 QPlatformTheme OK OK Cancel キャンセル Close 閉じる Save 保存する ResultsTree File ファイル Severity 警告の種別 Line Summary 要約 Undefined file 未定義ファイル Copy コピー Could not find file: ファイルが見つかりません: Please select the folder '%1' フォルダ '%1' を選択してください Select Directory '%1' ディレクトリ '%1' 選択 Please select the directory where file is located. ファイルのあるディレクトリを選択してください。 [Inconclusive] [結論の出ない] debug デバッグ note 注意 Recheck 再チェック Copy filename ファイル名をコピー Copy full path フルパスをコピー Copy message メッセージをコピー Copy message id メッセージidをコピー Hide 非表示 Hide all with id IDで非表示を指定 Suppress selected id(s) 選択したidを抑制 Open containing folder 含まれるフォルダを開く Tag タグ No tag タグなし Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. エディタアプリが設定されていません。 Cppcheckの「設定」からテキストファイルを編集するアプリケーションを設定してください。 No default editor application selected. Please select the default editor application in preferences/Applications. デフォルトのエディタアプリケーションが指定されていません。 設定からデフォルトのエディタアプリケーションを設定してください。 Could not find the file! ファイルが見つかりません! Could not start %1 Please check the application path and parameters are correct. %1 が実行できません。 実行ファイルパスや引数の設定を確認してください。 Could not find file: %1 Please select the directory where file is located. ファイルが見つかりません: %1 ディレクトリにファイルが存在するか確認してください。 Select Directory ディレクトリを選択 Id Id Inconclusive 結論のでない Since date 日付 style スタイル error エラー warning 警告 performance パフォーマンス portability 移植可能性 information 情報 ResultsView Results 結果 Analysis Log チェックログ Warning Details 警告の詳細 No errors found, nothing to save. 警告/エラーが見つからなかったため、保存しません。 Failed to save the report. レポートの保存に失敗しました。 Print Report レポートの印刷 No errors found, nothing to print. 指摘がないため、印刷するものがありません。 %p% (%1 of %2 files checked) %p% (%1 / %2 :ファイル数) Cppcheck Cppcheck No errors found. 警告/エラーは見つかりませんでした。 Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. 警告/エラーが見つかりましたが、非表示設定になっています。 Failed to read the report. レポートの読み込みに失敗. XML format version 1 is no longer supported. XML フォーマットバージョン 1 はもうサポートされていません。 Summary 内容 Message メッセージ First included by は次のものが最初にインクルードしました Id ID Clear Log ログの消去 Copy this Log entry このログ項目をコピー Copy complete Log ログ全体をコピー ScratchPad Scratchpad スクラッチパッド Copy or write some C/C++ code here: ここに C/C++のコードをコピーペーストまたは記入してください: Optionally enter a filename (mainly for automatic language detection) and click on "Check": オプション: ファイル名を入力(言語は自動判定)して"チェック"をクリックしてください: filename ファイル名 Check チェック Settings Preferences 設定 General 全般 Include paths: Include ディレクトリ: Add... 追加... Number of threads: 解析用のスレッド数: Ideal count: 理想的な数: Force checking all #ifdef configurations Check all #ifdef configurations すべての #ifdef をチェックする Show full path of files ファイルのフルパスを表示 Show "No errors found" message when no errors found エラーが無いときは"エラーなし"を表示 Display error Id in column "Id" エラーIDを "Id" に表示する Enable inline suppressions inline抑制を有効にする Check for inconclusive errors also 結論のでない指摘もチェックする Show statistics on check completion チェック完了時に統計情報を表示する Show internal warnings in log ログの内部警告も表示する Addons アドオン Python binary (leave this empty to use python in the PATH) Pythonインタプリタの場所(空白の場合システムのPATHから検索) ... ... Misra addon Misraアドオン Misra rule texts file Misra ルールテキストファイル <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdfのテキストをテキストファイルにコピー</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clangの場所(空白の場合システムのPATHから検索) Visual Studio headers Visual Studioのヘッダ <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Visual Studioのヘッダーファイル(セミコロン区切り';')。</p><p>Visual Studio コマンドプロンプトを開き、 &quot;SET INCLUDE&quot;. と入力後、そのパスをコピーペーストしてください。</p></body></html> Code Editor コードエディタ Code Editor Style コードエディタスタイル System Style システムのデフォルトのスタイル Default Light Style ライトスタイルをデフォルトに Default Dark Style ダークスタイルをデフォルトに Custom カスタム Paths パス Edit 編集 Remove 削除 Applications アプリケーション Edit... 編集... Set as default デフォルトとして設定 Reports レポート Save all errors when creating report レポート作成時にすべての警告/エラーを保存 Save full path to files in reports レポートにファイルのフルパスを保存 Language 言語 Advanced 高度 &Show inconclusive errors 結論の出ないのエラーを表示 SettingsDialog N/A N/A Add a new application 新しいアプリケーションの追加 Modify an application アプリケーションの変更 [Default] [デフォルト] [Default] [デフォルト] Select python binary pythonの場所の選択 Select MISRA File MISRAファイルの選択 Select clang path clangのパスの選択 Select include directory include ディレクトリを選択 StatsDialog Statistics 統計情報 Project プロジェクト Project: プロジェクト: Paths: パス: Include paths: インクルードパス: Defines: 定義(define): Undefines: 定義取り消し(undef): Previous Scan 前回の解析 Path Selected: ディレクトリ選択: Number of Files Scanned: 解析済みファイル数: Scan Duration: 解析にかかった時間: Errors: エラー: Warnings: 警告: Stylistic warnings: スタイル警告: Portability warnings: 移植可能性の警告: Performance issues: パフォーマンス警告: Information messages: 情報メッセージ: History ヒストリー File: ファイル: Copy to Clipboard クリップボードにコピー Pdf Export PDF エクスポート 1 day 一日 %1 days %1日 1 hour 一時間 %1 hours %1時間 1 minute 一分 %1 minutes %1分 1 second 一秒 %1 seconds %1秒 0.%1 seconds 0.%1秒 and Export PDF PDF エクスポート Project Settings プロジェクトの設定 Paths パス Include paths インクルードパス Defines 定義(define) Undefines 定義取り消し(Undef) Path selected 選択されたパス Number of files scanned スキャンしたファイルの数 Scan duration スキャン期間 Errors エラー File: ファイル: No cppcheck build dir cppcheckビルドディレクトリがありません Warnings 警告 Style warnings スタイル警告 Portability warnings 移植可能性警告 Performance warnings パフォーマンス警告 Information messages 情報メッセージ ThreadResult %1 of %2 files checked チェック: %1 / %2 (ファイル数) TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. ユーザーインターフェースの言語 %1 への変更に失敗しました。 そのため言語を 英語にリセットします。設定ダイアログから利用可能な言語を選択してください。 Cppcheck Cppcheck TxtReport inconclusive 結論の出ない toFilterString All supported files (%1) 全サポートファイル (%1) All files (%1) 全ファイル(%1) cppcheck-1.90/gui/cppcheck_ko.ts000066400000000000000000003233031357737443600166570ustar00rootroot00000000000000 About About Cppcheck Cppcheck 정보 Version %1 버전 %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - 정적 C/C++ 코드 분석 도구. This program is licensed under the terms of the GNU General Public License version 3 이 프로그램은 GNU General Public License version 3을 준수합니다 Visit Cppcheck homepage at %1 Cppcheck 홈페이지(%1)를 방문해보세요 Copyright © 2007-2019 Cppcheck team. <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application 응용 프로그램 추가 Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) 에러 파일을 열 응용 프로그램을 추가할 수 있습니다. 응용 프로그램의 이름, 실행 파일, 명령행 인자를 입력하세요. 인자 중에서 아래와 같은 텍스트는 응용 프로그램 실행 시 해당 값으로 대치됩니다: (file) - 에러를 포함한 파일이름 (line) - 에러를 포함한 행번호 (message) - 에러 메시지 (severity) - 에러 종류 Kate로 파일을 열고, 해당 행으로 이동하는 예제: 실행파일: kate 인자: -l(line) (file) &Name: 이름(&N): &Executable: 실행 파일(&E): &Parameters: 명령행 인자(&P): Browse 찾기 Executable files (*.exe);;All files(*.*) 실행 파일(*.exe);;모든 파일(*.*) Select viewer application 뷰어 프로그램 선택 Cppcheck Cppcheck You must specify a name, a path and parameters for the application! 응용 프로그램의 이름, 경로 및 인자를 명시해야 합니다! You must specify a name, a path and optionally parameters for the application! FileViewDialog Could not find the file: %1 파일 찾기 실패: %1 Cppcheck Cppcheck Could not read the file: %1 파일 읽기 실패: %1 LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Functions Add noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit 편집 Library files (*.cfg) Open library file Sort Filter: Comments Save as Cppcheck Cppcheck Save the library as Failed to load %1. %2. Cannot open file %1. Cannot save file %1. LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log 로그 확인 Clear 지우기 Save Log 로그 저장 Text files (*.txt *.log);;All files (*.*) 텍스트 파일 (*.txt *.log);;모든 파일 (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" 기록할 파일 열기 실패: "%1" MainWindow Cppcheck Cppcheck &File 파일(&F) &View 보기(&V) &Toolbars 도구바(&T) &Help 도움말(&H) &Check 검사(&C) &Edit 편집(&E) Standard 표준 도구 Categories 분류 도구 Filter 필터 도구 &License... 저작권(&L)... A&uthors... 제작자(&u)... &About... 정보(&A)... &Files... 파일(&F)... Check files 파일 검사 Ctrl+F Ctrl+F &Directory... 디렉토리(&D)... Check directory 디렉토리 검사 Ctrl+D Ctrl+D &Recheck files 파일 재검사(&R) Ctrl+R Ctrl+R &Stop 중지(&S) Stop checking 검사 중지 Esc Esc &Save results to file... 결과를 파일에 저장(&S)... Ctrl+S Ctrl+S &Quit 종료(&Q) &Clear results 결과 지우기(&C) &Preferences 설정(&P) Style warnings 스타일 경고 Show style warnings 스타일 경고 표시 Errors 에러 Show errors 애러 표시 &Check all 전체 선택(&C) &Uncheck all 전체 해제(&U) Collapse &all 전체 접기(&A) &Expand all 전체 펼치기(&E) &Standard 표준 도구(&S) Standard items 표준 아이템 &Contents 내용(&C) Open the help contents 도움말을 엽니다 F1 F1 Toolbar 도구바 &Categories 분류 도구(&C) Error categories 에러 종류 &Open XML... XML 열기(&O)... Open P&roject File... 프로젝트 파일 열기(&R)... &New Project File... 새 프로젝트 파일(&N)... &Log View 로그 보기(&L) Log View 로그 보기 C&lose Project File 프로젝트 파일 닫기(&L) &Edit Project File... 프로젝트 파일 편집(&E)... &Statistics 통계 보기(&S) Warnings 경고 Show warnings 경고 표시 Performance warnings 성능 경고 Show performance warnings 성능 경고 표시 Show &hidden 숨기기 보기(&H) Information 정보 Show information messages 정보 표시 Portability 이식성 경고 Show portability warnings 이식성 경고 표시 &Filter 필터 도구(&F) Filter results 필터링 결과 Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms 플랫폼 C++11 C++11 C99 C99 Posix Posix Quick Filter: 빠른 필터: There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. 편집기 설정을 불러오는데 문제가 있습니다. Cppcheck 버전간 설정 방법 차이때문인 것으로 보입니다. 편집기 설정을 검사(및 수정)해주세요, 그렇지 않으면 편집기가 제대로 시작하지 않습니다. No suitable files found to check! 검사할 수 있는 파일이 없습니다! You must close the project file before selecting new files or directories! 새로운 파일이나 디렉토리를 선택하기 전에 프로젝트 파일을 닫으세요! Select directory to check 검사할 디렉토리 선택 Found project file: %1 Do you want to load this project file instead? 프로젝트 파일 존재: %1 이 프로젝트 파일을 불러오겠습니까? Found project files from the directory. Do you want to proceed checking without using any of these project files? 디렉토리에 프로젝트 파일 존재. 이 프로젝트 파일을 사용하지 않고 검사를 계속하시겠습니까? XML files (*.xml) XML 파일 (*.xml) Open the report file 보고서 파일 열기 Checking is running. Do you want to stop the checking and exit Cppcheck? 검사 중. 검사를 중지하고 Cppcheck을 종료하시겠습니까? License 저작권 Authors 제작자 XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML 파일 버전 2 (*.xml);;XML 파일 버전 1 (*.xml);;텍스트 파일 (*.txt);;CSV 파일 (*.csv) Save the report file 보고서 파일 저장 XML files version 1 (*.xml) XML 파일 버전 1 (*.xml) XML files version 2 (*.xml) XML 파일 버전 2 (*.xml) Text files (*.txt) 텍스트 파일 (*.txt) CSV files (*.csv) CSV 파일 (*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. 언어 변경 실패: %1 언어가 영어로 초기화 됐습니다. 설정창을 열어서 설정 가능한 언어를 선택하세요. Project files (*.cppcheck);;All files(*.*) 프로젝트 파일 (*.cppcheck);;모든 파일(*.*) Select Project File 프로젝트 파일 선택 Project: 프로젝트: Select Project Filename 프로젝트 파일이름 선택 No project file loaded 프로젝트 파일 불러오기 실패 The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? 프로젝트 파일 %1 이 존재하지 않습니다! 최근 프로젝트 목록에서 파일을 제거하시겠습니까? Select files to check 검사할 파일 선택 Cppcheck GUI - Command line parameters C++ standard C11 C11 C89 C89 C++03 C++03 Error File not found Bad XML Missing attribute Bad attribute value Failed to load the selected library '%1'. %2 Unsupported format The library '%1' contains unknown elements: %2 Duplicate platform type Platform type redefined &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor Unknown element Unknown issue Select configuration Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Build dir '%1' does not exist, create it? Analyze files Analyze directory &Reanalyze modified files Stop analysis XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) No suitable files found to analyze! Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? A&nalyze &C standard Reanal&yze all files Style war&nings E&rrors Sh&ow Scratchpad... &Warnings Per&formance warnings &Information &Portability Show Cppcheck results Clang Show Clang results P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Failed to import '%1', analysis is stopped Project files (*.cppcheck) Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Built-in 내장 방식 Native Project Cppcheck Cppcheck Could not read the project file. 프로젝트 파일을 읽을 수 없습니다. Could not write the project file. 프로젝트 파일에 쓸 수 없습니다. ProjectFile Project File 프로젝트 파일 Project 프로젝트 Defines: Defines: Root: Root: Paths: 경로: Add... 추가... Edit 편집 Remove 제거 Includes Includes Include directories: Include 디렉토리: Up 위로 Down 아래로 Exclude Exclude Suppressions Add Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. ... Include Paths: Paths and Defines <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Analyze all Visual Studio configurations Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Addons Y2038 Thread safety Coding standards Cert Clang analyzer Clang-tidy Browse... Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Checking Platform Warning options Addons and tools MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> External tools Import Project (Visual studio / compile database/ Borland C++ Builder 6) Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Exclude source files in paths Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. ProjectFileDialog Project file: %1 프로젝트 파일: %1 Select include directory Include 디렉토리 선택 Select a directory to check 검사할 디렉토리 선택 Select directory to ignore 무시할 디렉토리 선택 Select Cppcheck build dir Import Project Clang-tidy (not found) (no rule texts file) Select MISRA rule texts file Misra rule texts file (%1) Visual Studio Compile database Borland C++ Builder 6 QDialogButtonBox OK Cancel Close 닫기 Save QObject Unknown language specified! 알 수 없는 언어입니다! Language file %1 not found! 언어 파일(%1)이 없습니다! Failed to load translation for language %1 from file %2 파일(%2)로부터 언어(%1) 불러오기 실패 line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark Class Foreground Color QPlatformTheme OK Cancel Close 닫기 Save ResultsTree File 파일 Severity 분류 Line Summary 요약 Undefined file 미정의된 파일 [Inconclusive] [불확실] style 스타일 error 에러 warning 경고 performance 성능 portability 이식성 information 정보 debug 디버그 Copy filename 파일이름 복사 Copy full path 전체 경로 복사 Copy message 메시지 복사 Hide 숨기기 Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. 편집기 미설정. [설정 - 응용 프로그램]에서 편집기를 설정하세요. No default editor application selected. Please select the default editor application in preferences/Applications. 기본 편집기 미선택. [설정 - 응용 프로그램]에서 기본 편집기를 선택하세요. Could not find the file! 파일을 찾을 수 없습니다! Could not start %1 Please check the application path and parameters are correct. %1을 시잘할 수 없습니다 경로와 인자가 정확한지 확인하세요. Could not find file: %1 Please select the directory where file is located. 파일 찾기 실패: %1 파일이 위치한 디렉토리를 선택하세요. Select Directory 디렉토리 선택 Id Hide all with id Open containing folder Inconclusive Recheck note Suppress selected id(s) Tag No tag Since date Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. Copy ResultsView Results 결과 No errors found, nothing to save. 에러가 발견되지 않았고, 저장할 내용이 없습니다. Failed to save the report. 결과 저장 실패. %p% (%1 of %2 files checked) %p% (%2 중 %1 파일 검사됨) Cppcheck Cppcheck No errors found. 에러가 발견되지 않았습니다. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. 에러가 발견되었지만, 감추도록 설정되어 있습니다. 에러 종류를 표시하도록 설정하려면, 보기 메뉴를 선택하세요. Failed to read the report. 결과 불러오기 실패. Summary 요약 Message 내용 Id Print Report No errors found, nothing to print. First included by XML format version 1 is no longer supported. Analysis Log Warning Details Clear Log Copy this Log entry Copy complete Log ScratchPad Scratchpad filename Check 검사 Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": Settings Preferences 설정 General 일반 Number of threads: 쓰레드 수: Ideal count: 최적 값: Force checking all #ifdef configurations 모든 #ifdef 설정을 강제로 검사 Show full path of files 파일의 전체 경로 표시 Show "No errors found" message when no errors found 에러가 발견되지 않는 경우 "에러가 없습니다." 메시지 표시 Enable inline suppressions Inline suppression 사용 Paths 경로 Include paths: Include 경로: Add... 추가... Edit 편집 Remove 제거 Applications 응용 프로그램 Edit... 편집... Set as default 기본으로 지정 Reports 보고서 Save all errors when creating report 보고서 생성 시 모든 에러 저장 Save full path to files in reports 보고서에 파일의 전체 경로 저장 Language 언어 Advanced 고급 &Show inconclusive errors 불확실한 에러 표시(&S) S&how internal warnings in log 로그에 내부 경고 표시(&H) Display error Id in column "Id" Check for inconclusive errors also Show internal warnings in log Show statistics on check completion Addons Python binary (leave this empty to use python in the PATH) ... Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Code Editor Code Editor Style Default Light Style Default Dark Style Custom System Style SettingsDialog N/A N/A Add a new application 새 응용 프로그램 추가 Modify an application 응용 프로그램 편집 [Default] [기본] Select include directory Include 디렉토리 선택 [Default] Select python binary Select clang path Select MISRA File StatsDialog Statistics 통계 Project 프로젝트 Project: 프로젝트: Paths: 경로: Include paths: Include 경로: Defines: Defines: Previous Scan 직전 검사 Path Selected: 선택된 경로: Number of Files Scanned: 검사된 파일 수: Scan Duration: 검사 시간: Errors: 에러: Warnings: 경고: Stylistic warnings: 스타일 경고: Portability warnings: 이식성 경고: Performance issues: 성능 경고: Information messages: 정보 메시지: Copy to Clipboard 클립보드에 복사 1 day 1일 %1 days %1일 1 hour 1시간 %1 hours %1시간 1 minute 1분 %1 minutes %1분 1 second 1초 %1 seconds %1초 0.%1 seconds 0.%1초 and Project Settings 프로젝트 설정 Paths 경로 Include paths Include 경로 Defines Defines Path selected 선택된 경로 Number of files scanned 검사된 파일 수 Scan duration 검사 시간 Errors 에러 Warnings 경고 Style warnings 스타일 경고 Portability warnings 이식성 경고 Performance warnings 성능 경고 Information messages 정보 메시지 Pdf Export Export PDF History File: File: No cppcheck build dir Undefines: Undefines ThreadResult %1 of %2 files checked %2 중 %1 파일 검사됨 TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. 언어 변경 실패: %1 언어가 영어로 초기화 됐습니다. 설정창을 열어서 설정 가능한 언어를 선택하세요. Cppcheck Cppcheck TxtReport inconclusive 불확실 toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppcheck_nl.ts000066400000000000000000003252741357737443600166700ustar00rootroot00000000000000 About About Cppcheck Over Cppcheck Version %1 Versie %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Een tool voor statische C/C++ code analyse. Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2019 het cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 Dit programma is beschikbaar onder te termen van de GNU General Public License versie 3 Visit Cppcheck homepage at %1 Bezoek de Cppcheck homepage op %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Voeg een nieuwe applicatie toe Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Hier kunt u toepassingen toevoegen die de foute bestanden kan openen. Geef naam op van de toepassing, het uitvoerbare bestand en command lijn parameters voor de toepassing De volgende tekst in de parameters word vervangen door de juiste waarden wanneer de toepassing wordt uitgevoerd: (bestand) - Bestandsnaam waarin de fout zit (lijn) - Lijnnummer waar de fout zit (bericht) Foutmelding (ernst) Ernst van foutmelding Voorbeeld een bestand openen met KonOs2 en laat KonOs2 scrollen naar de juiste lijn: Uitvoerbaar: KonOs2 Parameters: -l(lijn) (bestand) &Name: &Naam: &Executable: &Uitvoerbaar: &Parameters: Browse Bladeren Executable files (*.exe);;All files(*.*) Uitvoerbare bestanden (*.exe);;Alle bestanden(*.*) Select viewer application Selecteer applicatie Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Geef een naam op, een pad en eventueel parameters voor de toepassing! FileViewDialog Could not find the file: %1 Could not find the file: Kon het bestand niet vinden: %1 Cppcheck Cppcheck Could not read the file: %1 Kon het bestand niet lezen: %1 LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Opslaan Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Bewerk Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log Controleer log Clear Wis Save Log Opslaan log Text files (*.txt *.log);;All files (*.*) Tekst bestanden (*.txt *.log);;Alle bestanden(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Kan bestand: "%1" niet openen om te schrijven MainWindow Cppcheck Cppcheck A&nalyze Standard Standaard &File &Bestand &View &Weergave &Toolbars &Werkbalken &Check &Controleer C++ standard C++standaard &C standard C standard C standaard &Edit Be&werken &License... &Licentie... A&uthors... A&uteurs... &About... &Over... &Files... &Bestanden... Analyze files Check files Controleer bestanden Ctrl+F Ctrl+F &Directory... &Mappen... Analyze directory Check directory Controleer Map Ctrl+D Ctrl+D &Recheck files &Opnieuw controleren Ctrl+R Ctrl+R &Stop &Stop Stop analysis Stop checking Stop controle Esc Esc &Save results to file... &Resultaten opslaan... Ctrl+S Ctrl+S &Quit &Afsluiten &Clear results &Resultaten wissen &Preferences &Voorkeuren Errors Fouten Show errors Toon fouten Show S&cratchpad... Toon S&cratchpad... Warnings Waarschuwingen Show warnings Toon waarschuwingen Performance warnings Presentatie waarschuwingen Show performance warnings Toon presentatie waarschuwingen Show &hidden Toon &verborgen Information Informatie Show information messages Toon informatie bericht Portability Portabiliteit Show portability warnings Toon portabiliteit waarschuwingen Show Cppcheck results Clang Show Clang results &Filter &Filter Filter results Filter resultaten Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor &Check all &Controleer alles Filter &Reanalyze modified files &Recheck modified files Reanal&yze all files Style war&nings E&rrors &Uncheck all Selecteer &niets Collapse &all Alles Inkl&appen &Expand all Alles &Uitklappen &Standard &Standaard Standard items Standaard items Toolbar Werkbalk &Categories &Categorieën Error categories Foute Categorieën &Open XML... Open P&roject File... Open P&oject bestand... Sh&ow Scratchpad... &New Project File... &Nieuw Project Bestand... &Log View &Log weergave Log View Log weergave C&lose Project File &Sluit Project Bestand &Edit Project File... &Bewerk Project Bestand... &Statistics &Statistieken &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++20 &Contents &Inhoud Categories Categorieën Style warnings Stijl waarschuwingen Show style warnings Toon stijl waarschuwingen Open the help contents Open de help inhoud F1 &Help &Help Select directory to check Selecteer een map om te controleren No suitable files found to check! Geen geschikte bestanden gevonden om te controleren! Quick Filter: Snel Filter: Select configuration Found project file: %1 Do you want to load this project file instead? Project bestand gevonden: %1 Wilt u dit project laden in plaats van? Found project files from the directory. Do you want to proceed checking without using any of these project files? Project bestanden gevonden in de map. Wil je verder wilt gaan zonder controle van deze project bestanden? File not found Bad XML Missing attribute Bad attribute value Failed to load the selected library '%1'. %2 License Licentie Authors Auteurs XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML bestanden (*.xml);;Tekst bestanden (*.txt);;CSV bestanden (*.csv) Save the report file Rapport opslaan XML files (*.xml) XML bestanden (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. Er was een probleem met het laden van de bewerker instellingen. Dit is waarschijnlijk omdat de instellingen zijn gewijzigd tussen de versies van cppcheck. Controleer (en maak) de bewerker instellingen, anders zal de bewerker niet correct starten. You must close the project file before selecting new files or directories! Je moet project bestanden sluiten voordat je nieuwe bestanden of mappen selekteerd! Select files to check Selecteer bestanden om te controleren The library '%1' contains unknown elements: %2 Unsupported format Duplicate platform type Platform type redefined Unknown element Unknown issue Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Huidige resultaten zullen worden gewist Een nieuw XML-bestand openen zal de huidige resultaten wissen Wilt u verder gaan? Open the report file Open het rapport bestand Checking is running. Do you want to stop the checking and exit Cppcheck? Het controleren loopt. Wil je het controleren stoppen en Cppcheck sluiten? XML files version 1 (*.xml) XML files version 1 (*.xml) XML files version 2 (*.xml) XML files version 2 (*.xml) Text files (*.txt) Tekst bestanden (*.txt) CSV files (*.csv) CSV bestanden (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Project bestanden (*.cppcheck);;Alle bestanden(*.*) Select Project File Selecteer project bestand Project: Project: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename Selecteer project bestandsnaam No project file loaded Geen project bestand geladen The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Het project bestand %1 Kan niet worden gevonden! Wilt u het bestand van de onlangs gebruikte project verwijderen -lijst? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version Cppcheck GUI. Syntax: ....cppcheck-gui [Opies] [bestanden of paden] Opties: .....-h, --help Print deze help .....-p <bestand>......Open project bestand en start de controle .....-l <bestand>......Open gegeven resultaten xml bestand .....-d <map> Geef de map aan wat gecontroleerd werd om de xml resultaten te genereren met gespecificeerde -l .....-v,.--versie Toon versie van programma Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters Cppcheck GUI - Command lijn parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in Gemaakt in Native Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. Kon project bestand niet lezen. Could not write the project file. Kon niet naar project bestand schrijven. ProjectFile Project File Project Bestand Project Project Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' &Root: Root: Hoofdmap: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Paths: Paden: Add... Toevoegen... Edit Bewerk Remove Verwijder Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Exclude source files in paths Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. External tools Includes Inclusief Include directories: Include mappen: Up Omhoog Down Omlaag Checking Platform Warning options Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude Exclusief Suppressions Add Addons and tools Addons Y2038 Thread safety Coding standards Cert Clang analyzer Clang-tidy Defines: Omschrijft: ProjectFileDialog Project file: %1 Project Bestand %1 Select Cppcheck build dir Select include directory Selecteer include map Select a directory to check Selecteer een map om te controleren (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Selecteer een map om te negeren Select MISRA rule texts file Misra rule texts file (%1) QDialogButtonBox OK Cancel Annuleer Close Sluit Save Opslaan QObject Unknown language specified! Onbekende taal gekozen! Language file %1 not found! Language file %1.qm not found! Kon het taalbestand niet vinden: %1! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2.qm Kon de vertaling voor taal %1 in bestand %2 niet laden line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Annuleer Close Sluit Save Opslaan ResultsTree File Bestand Severity Ernst Line Regel Summary Overzicht Undefined file Niet gedefinieerd bestand Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [Onduidelijk] debug note Recheck Copy filename Kopier bestandsnaam Copy full path Kopieer volledig pad Copy message Kopieer bericht Copy message id Kopieer bericht id Hide Verberg Hide all with id Verberg alles met id Suppress selected id(s) Open containing folder Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. Er is geen bewerker toepassing geconfigureerd. Configureer de bewerker toepassing voor cppcheck in voorkeuren/Applicaties. No default editor application selected. Please select the default editor application in preferences/Applications. Geen standaard bewerker geselecteerd. Selecteer de standaard bewerker in voorkeuren/Applicaties. Could not find the file! Kon het bestand niet vinden! Could not start %1 Please check the application path and parameters are correct. Kon applicatie %1 niet starten Gelieve te controleren of de het pad en de parameters correct zijn. Could not find file: %1 Please select the directory where file is located. %1 Selecteer de map waarin het bestand zich bevindt. Select Directory Selecteer map Id Id Inconclusive Since date style Stijlfouten error Fouten warning Waarschuwing performance Presentatie portability Portabiliteit information Informatie ResultsView Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) %p% (%1 van %2 bestanden gecontroleerd) Cppcheck Cppcheck No errors found. Geen fouten gevonden. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Fouten werden gevonden, maar volgens de configuratie zijn deze verborgen. Gebruik het uitzicht menu om te selecteren welke fouten getoond worden. Failed to read the report. Kon rapport niet lezen. XML format version 1 is no longer supported. Summary Overzicht Message Bericht First included by Id Id Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. Geen fouten gevonden; geen data om op te slaan. Failed to save the report. Kon het rapport niet opslaan. Results Resultaten Analysis Log Warning Details ScratchPad Scratchpad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename bestandsnaam Check Controleer Settings Preferences Instellingen General Algemeen Include paths: Include paden: Add... Toevoegen... Number of threads: Aantal threads: Ideal count: Ideale telling: Force checking all #ifdef configurations Check all #ifdef configurations Controleer alle #ifdef combinaties Show full path of files Toon het volledige pad van bestanden Show "No errors found" message when no errors found Toon "Geen fouten gevonden" indien geen fouten gevonden werden Display error Id in column "Id" Toon fout ld in kolom "Id" Enable inline suppressions Schakel inline suppressies in Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths Paden Edit Bewerk Remove Verwijder Applications Applicaties Edit... Bewerk... Set as default Instellen als standaard Reports Rapporten Save all errors when creating report Alle fouten opslaan Save full path to files in reports Volledig pad opslaan Language Taal Advanced Geavanceerd &Show inconclusive errors &Toon onduidelijke fouten S&how internal warnings in log T&oon interne waarschuwingen in log SettingsDialog N/A Add a new application Nieuwe applicatie toevoegen Modify an application Applicatie wijzigen [Default] [Default] [Standaard] Select python binary Select MISRA File Select clang path Select include directory Selecteer include map StatsDialog Statistics Statistieken Project Project Project: Project: Paths: Paden: Include paths: Bevat paden: Defines: Omschrijft: Undefines: Previous Scan Vorige scan Path Selected: Pad Geselekteerd: Number of Files Scanned: Aantal bestanden gescanned: Scan Duration: Scan tijd: Errors: Fouten: Warnings: Waarschuwingen: Stylistic warnings: Stilistisch waarschuwingen: Portability warnings: Portabiliteit waarschuwingen: Performance issues: Presentatie problemen: Information messages: Informatie bericht: History File: Copy to Clipboard Kopieer naar Clipbord Pdf Export 1 day 1 dag %1 days %1 dagen 1 hour 1 uur %1 hours %1 uren 1 minute 1 minuut %1 minutes %1 minuten 1 second 1 seconde %1 seconds %1 secondes 0.%1 seconds 0.%1 secondes and en Export PDF Project Settings Project instellingen Paths Paden Include paths Bevat paden Defines Omschrijft Undefines Path selected Pad Geselekteerd Number of files scanned Aantal bestanden gescanned Scan duration Scan tijd Errors Fouten File: No cppcheck build dir Warnings Waarschuwingen Style warnings Stijl waarschuwingen Portability warnings Portabiliteit waarschuwingen Performance warnings Presentatie waarschuwingen Information messages Informatie bericht ThreadResult %1 of %2 files checked %1 van %2 bestanden gecontroleerd TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Mislukt om de gebruikers taal te wijzigen: %1 De gebruikerstaal is gereset naar Engels. Open het dialoogvenster om een van de beschikbare talen te selecteren. Cppcheck Cppcheck TxtReport inconclusive Onduidelijk toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppcheck_ru.ts000066400000000000000000003647711357737443600167120ustar00rootroot00000000000000 About About Cppcheck О Cppcheck Version %1 Версия %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - программа для статического анализа кода на языках С/С++. Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2018 Cppcheck team. Copyright © 2007-2019 Cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 Эта программа распространяется на условиях лицензии GNU General Public License, версии 3 Visit Cppcheck homepage at %1 Посетите домашнюю страницу: %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> <html><head/><body> <p>Создано при использовании библиотек:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Добавление приложения Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Вы можете добавить приложение, которое можно будет использовать для открытия файла с ошибками. Задайте название приложения, путь до него и параметры командной строки. Следующие текстовые параметры будут заменены реальными значениями при запуске приложения: (file) - файл, содержащий ошибку (line) - номер строки с ошибкой (message) - текст ошибки (severity) - тип ошибки Пример открытия файла с помощью Kate (скролл переместится на нужную строчку): Программа: kate Параметры: -l(line) (file) &Name: &Название: &Executable: &Программа: &Parameters: &Параметры: Browse Просмотреть Executable files (*.exe);;All files(*.*) Выполняемые файлы (*.exe);;Все файлы(*.*) Select viewer application Выберите приложение Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Вы должны задать название и путь к приложению! FileViewDialog Could not find the file: %1 Could not find the file: Невозможно найти файл: %1 Cppcheck Cppcheck Could not read the file: %1 Невозможно прочитать файл: %1 LibraryAddFunctionDialog Add function Добавить функцию Function name(s) Имя(имена) функции Number of arguments Количество аргументов LibraryDialog Library Editor Редактор библиотек Open Открыть Save Сохранить Save as Сохранить как Functions Функции Sort Сортировать Add Добавить Filter: Фильтр: Comments Комментарии noreturn False True Unknown return value must be used должно быть использовано возвращаемое значение ignore function in leaks checking пропускать функцию при проверке на утечки Arguments Аргументы Edit Изменить Library files (*.cfg) Файлы библиотек (*.cfg) Open library file Открыть файл библиотеки Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Невозможно открыть файл %1. Failed to load %1. %2. Ошибка загрузки %1. %2. Cannot save file %1. Can not save file %1. Невозможно сохранить файл %1. Save the library as Сохранить библиотеку как LibraryEditArgDialog Edit argument Редактировать аргумент <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log Лог проверки Clear Очистить Save Log Сохранить Text files (*.txt *.log);;All files (*.*) Текстовые файлы (*.txt *.log);;Все файлы (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Не удалось записать в файл: "%1" MainWindow Cppcheck Cppcheck A&nalyze Анализ Standard Стандартные &File &Файл &View &Вид &Toolbars &Панель инструментов &Check &Проверить C++ standard Стандарт C++ &C standard C standard &Стандарт C &Edit &Правка &License... &Лицензия... A&uthors... &Авторы... &About... &О программе... &Files... &Файлы... Analyze files Check files Проверить файлы Ctrl+F Ctrl+F &Directory... &Каталог... Analyze directory Check directory Проверка каталога Ctrl+D Ctrl+D &Recheck files &Перепроверить файлы Ctrl+R Ctrl+R &Reanalyze all files &Recheck all files Заново проверить все файлы &Stop Остановить Stop analysis Stop checking Остановить проверку Esc Esc &Save results to file... Сохранить отчёт в файл... Ctrl+S Ctrl+S &Quit Выход &Clear results Очистить отчёт &Preferences Параметры Errors Ошибки Show errors Показать ошибки Show S&cratchpad... Показать блокнот Warnings Предупреждения Show warnings Показать предупреждения Performance warnings Предупреждения производительности Show performance warnings Показать предупреждения производительности Show &hidden Показать скрытые Information Информационные сообщения Show information messages Показать информационные сообщения Portability Переносимость Show portability warnings Показать предупреждения переносимости Show Cppcheck results Просмотр результатов Cppcheck Clang Clang Show Clang results Просмотр результатов Clang &Filter Фильтр Filter results Результаты фильтрации Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms Платформы C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Печать... Print the Current Report Напечатать текущий отчет Print Pre&view... Предварительный просмотр... Open a Print Preview Dialog for the Current Results Открыть диалог печати для текущих результатов Library Editor... Редактор библиотек... Open library editor Открыть редактор библиотек Auto-detect language Автоопределение языка Enforce C++ Принудительно C++ Enforce C Принудительно C &Check all Отметить все Filter Фильтр &Reanalyze modified files &Recheck modified files Заново проверить измененные файлы Reanal&yze all files Заново проверить все файлы Style war&nings Стилистические предупреждения E&rrors Ошибки &Uncheck all Сбросить все Collapse &all Свернуть все &Expand all Развернуть все &Standard Стандартные Standard items Стандартные элементы Toolbar Панель инструментов &Categories Категории Error categories Категории ошибок &Open XML... &Открыть XML... Open P&roject File... Открыть файл &проекта... Sh&ow Scratchpad... Показать Блокнот &New Project File... &Новый файл проекта... &Log View Посмотреть &лог Log View Посмотреть лог C&lose Project File &Закрыть файл проекта &Edit Project File... &Изменить файл проекта... &Statistics &Статистика &Warnings Предупреждения Per&formance warnings Предупреждения производительности &Information Информационные предупреждения &Portability Предупреждения переносимости P&latforms Платформы C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... Редактор библиотеки &Auto-detect language Автоматическое определение языка &Enforce C++ Принудительно C++ E&nforce C Принудительно C C++14 C++14 Reanalyze and check library Повторный анализ библиотеки Check configuration (defines, includes) Проверить конфигурацию (defines, includes) C++17 C++17 C++20 C++20 &Contents Помощь Categories Категории Style warnings Стилистические предупреждения Show style warnings Показать стилистические предупреждения Open the help contents Открыть помощь F1 F1 &Help Помощь Select directory to check Выберите каталог для проверки No suitable files found to check! Не найдено подходящих файлов для проверки! Quick Filter: Быстрый фильтр: Select configuration Выбор конфигурации Found project file: %1 Do you want to load this project file instead? Найден файл проекта: %1 Вы хотите загрузить этот проект? Found project files from the directory. Do you want to proceed checking without using any of these project files? Найдены файлы проекта из каталога. Вы хотите продолжить проверку, не используя ни одного из этих файлов проекта? File not found Файл не найден Bad XML Некорректный XML Missing attribute Пропущен атрибут Bad attribute value Некорректное значение атрибута Unsupported format Неподдерживаемый формат Failed to load the selected library '%1'. %2 Не удалось загрузить выбранную библиотеку '%1'. %2 Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Не удалось загрузить %1. Установленный Cppcheck поврежден. Вы можете использовать ключ --data-dir=<directory> в командной строке, чтобы указать, где расположен этот файл. License Лицензия Authors Авторы XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML файлы версии 2 (*.xml);;XML файлы версии 1 (*.xml);;Текстовые файлы (*.txt);;CSV файлы (*.csv) Save the report file Сохранить файл с отчетом XML files (*.xml) XML-файлы (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. Возникла проблема при загрузке настроек программы. Возможно, это связано с изменениями в версии программы. Пожалуйста, проверьте (и исправьте) настройки приложения. You must close the project file before selecting new files or directories! Вы должны закрыть проект перед выбором новых файлов или каталогов! Select files to check Выберите файлы для проверки The library '%1' contains unknown elements: %2 Библиотека '%1' содержит неизвестные элементы: %2 Duplicate platform type Дубликат типа платформы Platform type redefined Переобъявление типа платформы Unknown element Неизвестный элемент Unknown issue Неизвестная проблема Error Ошибка Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Невозможно загрузить %1. Cppcheck установлен некорректно. Вы можете использовать --data-dir=<directory> в командной строке для указания расположения файлов конфигурации. Обратите внимание, что --data-dir предназначен для использования сценариями установки. При включении данной опции, графический интерфейс пользователя не запускается. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Текущие результаты будут очищены. Открытые нового XML файла приведет к очистке текущих результатов. Продолжить? Open the report file Открыть файл с отчетом Checking is running. Do you want to stop the checking and exit Cppcheck? Идет проверка. Вы хотите завершить проверку и выйти? XML files version 1 (*.xml) XML файлы версии 1 (*.xml) XML files version 2 (*.xml) XML файлы версии 2 (*.xml) Text files (*.txt) Текстовые файлы (*.txt) CSV files (*.csv) CSV файлы(*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Project files (*.cppcheck);;All files(*.*) Файлы проекта (*.cppcheck);;Все файлы(*.*) Select Project File Выберите файл проекта Project: Проект: No suitable files found to analyze! Не найдено подходящих файлов для анализа C/C++ Source Исходный код C/C++ Compile database Visual Studio Visual Studio Borland C++ Builder 6 Borland C++ Builder 6 Select files to analyze Выбор файлов для анализа Select directory to analyze Выбор каталога для анализа Select the configuration that will be analyzed Выбор используемой конфигурации Found project files from the directory. Do you want to proceed analysis without using any of these project files? Обнаружены файлы проекты из каталога. Вы хотите продолжить анализ без использования этих файлов проекта? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Текущие результаты будут очищены. Открытие нового XML-файла приведет к очистке текущих результатов. Вы хотите продолжить? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? Анализатор запущен. Вы хотите остановить анализ и выйти из Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML файлы (*.xml);;Текстовые файлы (*.txt);;CSV файлы (*.csv) Build dir '%1' does not exist, create it? Директория для сборки '%1' не существует, создать? Failed to import '%1', analysis is stopped Невозможно импортировать '%1', анализ остановлен Project files (*.cppcheck) Файлы проекта (*.cppcheck) Select Project Filename Выберите имя файла для проекта No project file loaded Файл с проектом не загружен The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Файл с проектом %1 не найден! Хотите удалить его из списка проектов? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version Cppcheck Графический Интерфейс Пользователя . Синтаксис: cppcheck-gui [ОПЦИИ] [файлы или пути] Опции: -h, --help Показать эту справку -p <file> Откройте данный файл проекта и начните проверять его -l <file> Откройте данные результаты xml файл -d <directory> Укажите каталог, который был проверен, чтобы генерировать результаты xml определенный с -l -v, --version Показать версию программы Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI. Синтаксис: cppcheck-gui [ОПЦИИ] [файлы или пути] Опции: -h, --help Выдать подсказку на стандартный вывод и успешно завершиться. -p <file> Открыть указанный файл проекта и начать проверку -l <file> Открыть xml-файл с полученными результатами -d <directory> Указать каталог, который был проверен для создания результатов xml, указанных с помощью -l -v, --version Выдать информацию о версии на стандартный вывод и успешно завершиться. --data-dir=<directory> Этот параметр предназначен для сценариев установки, чтобы они могли настроить каталог, в котором расположены файлы данных (конфигурация, переводы). Графический интерфейс пользователя не будет запущен, если указана эта опция. Cppcheck GUI - Command line parameters Cppcheck GUI - параметры Командной строки NewSuppressionDialog New suppression Новое подавление Error ID ID File name Имя файла Line number Номер строки Symbol name Имя символа Edit suppression Редактировать подавление Platforms Built-in Встроенная Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. Не удалось прочитать файл проекта. Could not write the project file. Не удалось записать файл проекта. ProjectFile Project File Файл проекта Project Проект Paths and Defines Каталоги и определения Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Импорт проекта (Visual studio / compile database/ Borland C++ Builder 6) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' Defines должны быть разделены точкой с запятой ';' &Root: Root: Корневой каталог: Libraries: Библиотеки: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Положите свои .cfg-файлы в один каталог с файлом проекта. Вы увидите их сверху. MISRA C 2012 MISRA C 2012 Misra rule texts Файл с текстами правил MISRA <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Скопируйте текст из Appendix A &quot;Summary of guidelines&quot; из фала правил MISRA C 2012 pdf в текстовый файл.</p></body></html> ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>Выберите:</p><p> * Анализ всех конфигураций Debug и Release</p><p> * Анализ только первой подходящей конфигурации Debug</p><p><br/></p></body></html> Browse... Обзор... Analyze all Visual Studio configurations Анализировать все конфигурации Visual Studio Paths: Пути: Add... Добавить... Edit Изменить Remove Удалить Undefines: Удаленные макроопределения: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Удаленные макроопределения должны быть разделены точкой с запятой, например: UNDEF1;UNDEF2;UNDEF3 Include Paths: Пути заголовочных файлов: Analysis Анализ Check code in headers (slower analysis, more results) Проверить код в заголовочных файлах Check code in unused templates (slower and less accurate analysis) Проверить код в неиспользуемых шаблонах Max CTU depth Максимальная глубина CTU Exclude source files in paths Исключить исходные файлы в путях Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. External tools Внешние инструменты Includes Пути для заголовочных файлов Include directories: Пути для поиска заголовочных файлов: Up Вверх Down Вниз Checking Проверка Platform Платформа Warning options Опции предупреждений Root path: Корневой каталог: Warning tags (separated by semicolon) Теги предупреждений (через ';') Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Каталог сборки Cppcheck Libraries Библиотеки Exclude Исключенные пути Suppressions Подавления Add Добавить Addons and tools Дополнения Addons Дополнения Y2038 Thread safety Coding standards Стандарты кодирования Cert Cert Clang analyzer Clang-tidy Defines: Объявленные макроопределения: ProjectFileDialog Project file: %1 Файл проекта: %1 Select Cppcheck build dir Выбрать директорию сборки Cppcheck Select include directory Выберите директорию для поиска заголовочных файлов Select a directory to check Выберите директорию для проверки (no rule texts file) (файл с текстами правил недоступен) Clang-tidy (not found) Clang-tidy (не найден) Visual Studio Visual Studio Compile database Borland C++ Builder 6 Borland C++ Builder 6 Import Project Импорт проекта Select directory to ignore Выберите директорию, которую надо проигнорировать Select MISRA rule texts file Выбрать файл текстов правил MISRA Misra rule texts file (%1) Файл текстов правил MISRA (%1) QDialogButtonBox OK OK Cancel Отмена Close Закрыть Save Сохранить QObject Unknown language specified! Неизвестный язык! Language file %1 not found! Language file %1.qm not found! Языковой файл %1 не найден! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2.qm Ошибка загрузки переводов для языка %1 из файла %2 line %1: Unhandled element %2 (Not found) (Недоступно) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK OK Cancel Отмена Close Закрыть Save Сохранить ResultsTree File Файл Severity Важность Line Строка Summary Кратко Undefined file Неопределенный файл Copy Копировать Could not find file: Невозможно найти файл: Please select the folder '%1' Выберите каталог '%1' Select Directory '%1' Выбрать каталог '%1' Please select the directory where file is located. Укажите каталог с расположением файла. [Inconclusive] [Неубедительный] debug отлаживать note заметка Recheck Проверить заново Copy filename Скопировать имя файла Copy full path Скопировать полный путь Copy message Скопировать сообщение Copy message id Скопировать номер сообщения Hide Скрыть Hide all with id Скрыть все с id Suppress selected id(s) Подавить выбранные id Open containing folder Открыть содержащую папку Tag Тег No tag Тег отсутствует Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. Никакое приложение редактора не сконфигурировано. Сконфигурируйте приложение редактора для Cppcheck в предпочтениях/Приложениях. No default editor application selected. Please select the default editor application in preferences/Applications. Никакое приложение редактора по умолчанию не выбрано. Выберите приложение редактора по умолчанию в предпочтениях/Приложениях. Could not find the file! Не удается найти файл! Could not start %1 Please check the application path and parameters are correct. Не удалось запустить %1 Пожалуйста, проверьте путь приложения, и верны ли параметры. Could not find file: %1 Please select the directory where file is located. Не удается найти файл: %1 Пожалуйста, выберите каталог, в котором находится файл. Select Directory Выберите директорию Id Id Inconclusive Спорное Since date Начиная с даты style стиль error ошибка warning предупреждение performance производительность portability переносимость information информация ResultsView Print Report Распечатать отчет No errors found, nothing to print. Ошибок не найдено, нечего распечатывать. %p% (%1 of %2 files checked) %p% (%1 из %2 файлов проверено) Cppcheck Cppcheck No errors found. Ошибок не найдено. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Были обнаружены ошибки, но они настроены быть скрыты. Для переключения какие ошибки отображаются, откройте меню представления. Failed to read the report. Не удалось прочитать отчет. XML format version 1 is no longer supported. XML формат версии 1 больше не поддерживается. Summary Кратко Message Сообщение First included by Только первый включенный Id Id Clear Log Очистить лог Copy this Log entry Скопировать данную запись Copy complete Log Скопировать полный лог No errors found, nothing to save. Ошибки не найдены, нечего сохранять. Failed to save the report. Не удалось сохранить отчет. Results Результаты Analysis Log Лог анализа Warning Details Детали предупреждения ScratchPad Scratchpad Блокнот Copy or write some C/C++ code here: Исходный код C/C++: Optionally enter a filename (mainly for automatic language detection) and click on "Check": При необходимости введите имя файла и нажмите "Проверить": filename имя файла Check Проверить Settings Preferences Параметры General Общие Include paths: Пути для поиска заголовочных файлов: Add... Добавить... Number of threads: Количество потоков исполнения: Ideal count: Рекомендуемое значение: Force checking all #ifdef configurations Check all #ifdef configurations Проверять все варианты #ifdef конфигураций Show full path of files Показывать полные пути к файлам Show "No errors found" message when no errors found Показывать сообщение, если ошибок не найдено Display error Id in column "Id" Отображать номер ошибки в колонке "id" Enable inline suppressions Включить inline-подавление ошибок Check for inconclusive errors also Показывать также спорные ошибки Show statistics on check completion Показывать статистику после завершения проверки Show internal warnings in log Показывать внутренние предупреждения в логе Addons Дополнения Python binary (leave this empty to use python in the PATH) Python (оставьте пустым для использования python из PATH) ... ... Misra addon Дополнение MISRA Misra rule texts file Файл с текстами правил MISRA: <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> <html><head/><body><p>Скопируйте текст из Appendix A &quot;Summary of guidelines&quot; из фала правил MISRA C 2012 pdf в текстовый файл.</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clang (оставьте пустым для использования clang из PATH) Visual Studio headers Заголовочные файлы Visual Studio <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Путь до заголовочных файлов Visual Studio headers, разделенных символом ';'.</p><p>Вы можете открыть командную строку Visual Studio, ввести &quot;SET INCLUDE&quot; и скопировать пути.</p></body></html> Code Editor Редактор Code Editor Style Оформление System Style Default Light Style Default Dark Style Custom Paths Пути Edit Изменить Remove Удалить Applications Приложения Edit... Изменить... Set as default Установить по умолчанию Reports Отчёты Save all errors when creating report Сохранять все ошибки при создании отчёта Save full path to files in reports Сохранять полные пути к файлам в отчётах Language Язык Advanced Прочие &Show inconclusive errors &Показывать незначительные ошибки S&how internal warnings in log &Записывать внутренние предупреждения в лог SettingsDialog N/A Нет данных Add a new application Добавить новое приложение Modify an application Изменить приложение [Default] [По умолчанию] [Default] [По умолчанию] Select python binary Выберите исполняемый файл python Select MISRA File Выберите файл текстов правил MISRA Select clang path Выберите исполняемый файл clang Select include directory Выберите директорию StatsDialog Statistics Статистика Project Проект Project: Проект: Paths: Пути: Include paths: Включенные пути: Defines: Объявленные макроопределения: Undefines: Удаленные макроопределения: Previous Scan Последнее сканирование Path Selected: Выбранный путь: Number of Files Scanned: Количество просканированных файлов: Scan Duration: Продолжительность сканирования: Errors: Ошибки: Warnings: Предупреждения: Stylistic warnings: Стилистические предупреждения: Portability warnings: Предупреждения переносимости: Performance issues: Проблемы с производительностью: Information messages: Информационные сообщения: History История File: Файл: Copy to Clipboard Скопировать в буфер обмена Pdf Export Экспорт PDF 1 day 1 день %1 days %1 дней 1 hour 1 час %1 hours %1 часов 1 minute 1 минута %1 minutes %1 минут 1 second 1 секунда %1 seconds %1 секунд 0.%1 seconds 0.1%1 секунд and и Export PDF Экспорт PDF Project Settings Настройки проекта Paths Пути Include paths Включенные пути Defines Объявленные макроопределения: Undefines Удаленные макроопределения: Path selected Выбранные пути Number of files scanned Количество просканированных файлов Scan duration Продолжительность сканирования Errors Ошибки File: Файл: No cppcheck build dir Не задана директория сборки Warnings Предупреждения Style warnings Стилистические предупреждения Portability warnings Предупреждения переносимости Performance warnings Предупреждения производительности Information messages Информационные сообщения ThreadResult %1 of %2 files checked %1 из %2 файлов проверены TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Не удалось изменить язык пользовательского интерфейса: %1 Язык пользовательского интерфейса был сброшен на английский. Откройте Настройки-диалог для выбора любого из доступных языков. Cppcheck Cppcheck TxtReport inconclusive незначительная toFilterString All supported files (%1) Все поддерживаемые файлы (%1) All files (%1) Все файлы (%1) cppcheck-1.90/gui/cppcheck_sr.ts000066400000000000000000003110311357737443600166650ustar00rootroot00000000000000 About About Cppcheck About Cppcheck Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - A tool for static C/C++ code analysis. Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2018 Cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 This program is licensed under the terms of the GNU General Public License version 3 Visit Cppcheck homepage at %1 Visit Cppcheck homepage at %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Add a new application Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) &Name: &Executable: &Parameters: Browse Browse Executable files (*.exe);;All files(*.*) Executable files (*.exe);;All files(*.*) Select viewer application Select viewer application Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! FileViewDialog Could not find the file: %1 Could not find the file: %1 Cppcheck Cppcheck Could not read the file: %1 Could not read the file: %1 LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Cppcheck Cppcheck MainWindow Cppcheck Cppcheck A&nalyze Standard Standard &File &File &View &View &Toolbars &Check &Check C++ standard &C standard C standard &Edit &Edit &License... &License... A&uthors... A&uthors... &About... &About... &Files... &Files... Analyze files Check files Ctrl+F Ctrl+F &Directory... &Directory... Analyze directory Check directory Ctrl+D Ctrl+D &Recheck files &Recheck files Ctrl+R Ctrl+R &Stop &Stop Stop analysis Stop checking Esc Esc &Save results to file... &Save results to file... Ctrl+S Ctrl+S &Quit &Quit &Clear results &Clear results &Preferences &Preferences Show errors Show warnings Show performance warnings Show &hidden Information Show information messages Show portability warnings Show Cppcheck results Clang Show Clang results &Filter Filter results Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit C++11 C++11 C99 C99 &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor Gtk Gtk Posix Posix C11 C11 C89 C89 C++03 C++03 &Check all &Check all Filter &Reanalyze modified files &Recheck modified files Reanal&yze all files Style war&nings E&rrors &Uncheck all &Uncheck all Collapse &all Collapse &all &Expand all &Expand all &Standard Standard items Toolbar &Categories Error categories &Open XML... Open P&roject File... Sh&ow Scratchpad... &New Project File... &Log View Log View C&lose Project File &Edit Project File... &Statistics &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 &Contents Categories Show style warnings Open the help contents F1 F1 &Help &Help Select directory to check Select directory to check No suitable files found to check! No suitable files found to check! Quick Filter: Select configuration Found project file: %1 Do you want to load this project file instead? File not found Bad XML Missing attribute Bad attribute value Failed to load the selected library '%1'. %2 License License Authors Authors XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Save the report file Save the report file XML files (*.xml) XML files (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. You must close the project file before selecting new files or directories! Select files to check Select files to check The library '%1' contains unknown elements: %2 Unsupported format Duplicate platform type Platform type redefined Unknown element Unknown issue Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Open the report file Text files (*.txt) Text files (*.txt) CSV files (*.csv) Cppcheck - %1 Cppcheck - %1 Project files (*.cppcheck);;All files(*.*) Select Project File Project: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename No project file loaded The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck ProjectFile Project File Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Addons and tools MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Paths: Add... Edit Remove Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Up Down Checking Platform Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Warning options Root path: Warning tags (separated by semicolon) Exclude source files in paths Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. External tools Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Suppressions Add Addons Y2038 Thread safety Coding standards Cert Clang analyzer Clang-tidy Defines: ProjectFileDialog Project file: %1 Select Cppcheck build dir Select include directory Select a directory to check (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore Select MISRA rule texts file Misra rule texts file (%1) QDialogButtonBox OK Cancel Close Save QObject Unknown language specified! Language file %1 not found! Could not find the file: %1! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2 line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Close Save ResultsTree File File Severity Severity Line Line Summary Undefined file Undefined file Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. debug note Recheck Copy filename Copy filename Copy full path Copy full path Hide Hide all with id Suppress selected id(s) Open containing folder Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. You can open this error by specifying applications in program's settings. No default editor application selected. Please select the default editor application in preferences/Applications. Could not find the file! Could not start %1 Please check the application path and parameters are correct. Could not start %1 Please check the application path and parameters are correct. Select Directory Id Inconclusive Since date style Style error Error warning performance portability information ResultsView Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) Cppcheck Cppcheck No errors found. No errors found. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Failed to read the report. XML format version 1 is no longer supported. First included by Id Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. No errors found, nothing to save. Failed to save the report. Failed to save the report. Results Results Analysis Log Warning Details ScratchPad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename Check Settings Preferences Preferences General General Add... Number of threads: Number of threads: Ideal count: Force checking all #ifdef configurations Check all #ifdef configurations Show full path of files Show full path of files Show "No errors found" message when no errors found Show "No errors found" message when no errors found Display error Id in column "Id" Enable inline suppressions Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Remove Applications Applications Edit... Set as default Reports Reports Save all errors when creating report Save all errors when creating report Save full path to files in reports Save full path to files in reports Language SettingsDialog N/A Add a new application Add a new application Modify an application Modify an application [Default] [Default] Select python binary Select MISRA File Select clang path StatsDialog Statistics Project Project: Paths: Include paths: Defines: Undefines: Previous Scan Path Selected: Number of Files Scanned: Scan Duration: Errors: Warnings: Stylistic warnings: Portability warnings: Performance issues: Information messages: History File: Copy to Clipboard Pdf Export 1 day %1 days 1 hour %1 hours 1 minute %1 minutes 1 second %1 seconds 0.%1 seconds and Export PDF Project Settings Paths Include paths Defines Undefines Path selected Number of files scanned Scan duration Errors File: No cppcheck build dir Warnings Style warnings Portability warnings Performance warnings Information messages ThreadResult %1 of %2 files checked TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Cppcheck Cppcheck TxtReport inconclusive toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppcheck_sv.ts000066400000000000000000003447331357737443600167100ustar00rootroot00000000000000 About About Cppcheck Om Cppcheck Version %1 Version %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - Ett verktyg för statisk analys av C/C++ kod. Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2019 Cppcheck team. This program is licensed under the terms of the GNU General Public License version 3 This program is licensed under the terms of the GNU General Public License version 3 Visit Cppcheck homepage at %1 Hemsida: %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application Lägg till program Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) Här kan du ange en applikation som kan användas för att visa fel. Ange applikationens namn, körbara fil samt kommandorads parametrar. Följande texter i parametrarna ersätts med motsvarande värden när applikationen körs: (file) - filnamn för källkodsfil (line) - radnummer (message) - felmeddelande (severity) - typ / svårighetsgrad Exempel för att öppna en fil med Kate och ange att Kate skall skrolla till rätt rad: Körbar fil: kate Parametrar: -l(line) (file) &Name: Namn: &Executable: Körbar fil: &Parameters: Parametrar: Browse Bläddra Executable files (*.exe);;All files(*.*) Exekverbara filer (*.exe);;Alla filer(*.*) Select viewer application Välj program Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! Du måste ange namn, sökväg samt eventuellt parametrar för applikationen! You must specify a name, a path and parameters for the application! Du måste ange ett namn, en sökväg samt parametrar för programmet! FileViewDialog Could not find the file: %1 Could not find the file: Kunde inte hitta filen: %1 Cppcheck Cppcheck Could not read the file: %1 Kunde inte läsa filen: %1 LibraryAddFunctionDialog Add function Lägg till funktion Function name(s) Funktion namn Number of arguments Antal argument LibraryDialog Library Editor Library Editor Open Öppna Save Spara Save as Spara som Functions Funktioner Sort Sortera Add Lägg till Filter: Filter: Comments Kommentar noreturn noreturn False False True True Unknown Vet ej return value must be used retur värde måste användas ignore function in leaks checking Ignorera funktionen när cppcheck letar efter läckor Arguments Argument Edit Redigera Library files (*.cfg) Library fil (*.cfg) Open library file Öppna Library fil Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Kunde ej öppna filen %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Kunde ej spara filen %1. Save the library as Spara library som LibraryEditArgDialog Edit argument Konfigurera argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Är bool värde tillåtet? Exempelvis resultatet från jämförelse eller från ! operatorn. Normalt bör inte bool värde användas om argumentet är en pekare eller en storlek etc. Exempel: memcmp(x, y, i == 123); // sista argumentet bör inte vara ett bool värde Not bool Ej bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Är null värde tillåtet? Klicka i denna om argumentet är en pointer parameter som ej tillåter null. Exempel: strcpy(x,y); // varken x eller y får vara null. Not null Ej null Not uninit Ej uninit String Sträng Format string Format sträng Min size of buffer Minsta storlek för buffer Type Typ None Ingen argvalue argvalue constant constant mul mul strlen strlen Arg Arg Arg2 Arg2 and och Valid values Tillåtna värden LogView Checking Log Analys logg &Save &Spara Clear Töm Close Stäng Save Log Spara logg Text files (*.txt *.log);;All files (*.*) Text filer (*.txt *.log);;Alla filer (*.*) Cppcheck Cppcheck Could not open file for writing: "%1" Kunde ej öppna fil för skrivning: "%1" MainWindow Cppcheck Cppcheck A&nalyze Analysera Standard Standard &File &Arkiv &View &Visa &Toolbars Verktygsfält &Check &Check C++ standard C++ standard &C standard C standard C standard &Edit &Redigera &License... &Licens... A&uthors... &Utvecklat av... &About... &Om... &Files... &Filer... Analyze files Check files Analysera filer Ctrl+F Ctrl+F &Directory... &Katalog... Analyze directory Check directory Analysera mapp Ctrl+D Ctrl+D &Recheck files Starta &om check Ctrl+R Ctrl+R &Reanalyze all files &Recheck all files Analysera om alla filer &Stop &Stoppa Stop analysis Stop checking Stoppa analys Esc Esc &Save results to file... &Spara resultat till fil... Ctrl+S Ctrl+S &Quit &Avsluta &Clear results &Töm resultat &Preferences &Inställningar Errors Fel Show errors Visa fel Show S&cratchpad... Visa s&cratchpad... Warnings Varningar Show warnings Visa varningar Performance warnings Prestanda varningar Show performance warnings Visa prestanda varningar Show &hidden Visa dolda Information Information Show information messages Visa informations meddelanden Portability Portabilitet Show portability warnings Visa portabilitets varningar Show Cppcheck results Visa Cppcheck resultat Clang Clang Show Clang results Visa Clang resultat &Filter &Filter Filter results Filtrera resultat Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 64-bit Windows 64-bit Platforms Plattformar C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Skriv ut... Print the Current Report Skriv ut aktuell rapport Print Pre&view... Förhandsgranska utskrift... Open a Print Preview Dialog for the Current Results Öppnar förhandsgranskning för nuvarande resultat Library Editor... Library Editor... Open library editor Öppna library editor Auto-detect language Välj språk automatiskt Enforce C++ C++ Enforce C C &Check all &Kryssa alla Filter Filter &Reanalyze modified files &Recheck modified files Analysera om ändrade filer Reanal&yze all files Analysera om alla filer Style war&nings Style varningar E&rrors Fel &Uncheck all Kryssa &ur alla Collapse &all Ingen bra översättning! &Fäll ihop alla &Expand all &Expandera alla &Standard &Standard Standard items Standard poster Toolbar Verktygsfält &Categories &Kategorier Error categories Fel kategorier &Open XML... &Öppna XML... Open P&roject File... Öppna Projektfil... Sh&ow Scratchpad... Visa Scratchpad... &New Project File... Ny projektfil... &Log View Log View Logg vy C&lose Project File Stäng projektfil &Edit Project File... Redigera projektfil... &Statistics Statistik &Warnings Varningar Per&formance warnings Optimerings varningar &Information Information &Portability Portabilitet P&latforms Plattformar C++&11 C++11 C&99 C99 &Posix Posix C&11 C11 &C89 C89 &C++03 C++03 &Library Editor... Library Editor... &Auto-detect language Detektera språk automatiskt &Enforce C++ Tvinga C++ E&nforce C Tvinga C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 &Contents &Innehåll Categories Kategorier Style warnings Stil varningar Show style warnings Visa stil varningar Open the help contents Öppna hjälp F1 F1 &Help &Hjälp Select directory to check Välj katalog som skall kontrolleras No suitable files found to check! Inga lämpliga filer hittades! Quick Filter: Snabbfilter: C/C++ Source, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) C/C++ källkod, Compile database, Visual Studio (%1 %2 *.sln *.vcxproj) Select configuration Välj konfiguration Select the configuration that will be checked Välj konfiguration som kommer analyseras Found project file: %1 Do you want to load this project file instead? Hittade projektfil: %1 Vill du ladda denna projektfil istället? Found project files from the directory. Do you want to proceed checking without using any of these project files? Hittade projektfil(er) i mappen. Vill du fortsätta analysen utan att använda någon av dessa projektfiler? File not found Filen hittades ej Bad XML Ogiltig XML Missing attribute Attribut finns ej Bad attribute value Ogiltigt attribut värde Unsupported format Format stöds ej Failed to load the selected library '%1'. %2 Misslyckades att ladda valda library '%1'. %2 License Licens Authors Utvecklare XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML filer version 2 (*.xml);;XML filer version 1 (*.xml);;Text filer (*.txt);;CSV filer (*.csv) Save the report file Spara rapport XML files (*.xml) XML filer (*.xml) There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. Det uppstod ett problem när programinställningarna skulle laddas. En trolig orsak är att inställningarna ändrats för olika Cppcheck versioner. Kontrollera programinställningarna. You must close the project file before selecting new files or directories! Du måste stänga projektfilen innan nya filer eller sökvägar kan väljas! Select files to check Välj filer att kontrollera The library '%1' contains unknown elements: %2 Library filen '%1' har element som ej hanteras: %2 Duplicate platform type Dubbel plattformstyp Platform type redefined Plattformstyp definieras igen Unknown element Element hanteras ej Unknown issue Något problem Error Fel Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Misslyckades att ladda %1. Din Cppcheck installation är ej komplett. Du kan använda --data-dir<directory> på kommandoraden för att specificera var denna fil finns. Det är meningen att --data-dir kommandot skall köras under installationen,så GUIt kommer ej visas när --data-dir används allt som händer är att en inställning görs. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? Nuvarande resultat kommer rensas bort. När en ny XML fil öppnas så tas alla nuvarande resultat bort. Vill du fortsätta? Open the report file Öppna rapportfilen Checking is running. Do you want to stop the checking and exit Cppcheck? Cppcheck kör. Vill du stoppa analysen och avsluta Cppcheck? XML files version 1 (*.xml) XML filer version 1 (*.xml) Deprecated XML format Gammalt XML format XML format 1 is deprecated and will be removed in cppcheck 1.81. XML format 1 är gammalt och stödet kommer tas bort i Cppcheck 1.81 XML files version 2 (*.xml) XML filer version 2 (*.xml) Text files (*.txt) Text filer (*.txt) CSV files (*.csv) CSV filer (*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Misslyckades att ändra språk: %1 Språket har nollställts till Engelska. Öppna Preferences och välj något av de tillgängliga språken. Project files (*.cppcheck);;All files(*.*) Projektfiler (*.cppcheck);;Alla filer(*.*) Select Project File Välj projektfil Project: Projekt: No suitable files found to analyze! Inga filer hittades att analysera! C/C++ Source Compile database Visual Studio Visual Studio Borland C++ Builder 6 Select files to analyze Välj filer att analysera Select directory to analyze Välj mapp att analysera Select the configuration that will be analyzed Välj konfiguration som kommer analyseras Found project files from the directory. Do you want to proceed analysis without using any of these project files? Hittade projekt filer i mappen. Vill du fortsätta analysen utan att använda någon av dessa projekt filer? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? Analys körs. Vill du stoppa analysen och avsluta Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML filer (*.xml);;Text filer (*.txt);;CSV filer (*.csv) Build dir '%1' does not exist, create it? Build dir '%1' existerar ej, skapa den? Failed to import '%1', analysis is stopped Misslyckades att importera '%1', analysen stoppas Project files (*.cppcheck) Projekt filer (*.cppcheck) Select Project Filename Välj Projektfil No project file loaded Inget projekt laddat The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? Projektfilen %1 kunde inte hittas! Vill du ta bort filen från 'senast använda projekt'-listan? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI - Command line parameters Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in Generell Native Native Unix 32-bit Unix 32-bit Unix 64-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit ANSI Windows 32-bit Unicode Windows 32-bit Unicode Windows 64-bit Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. Kunde ej läsa projektfilen. Could not write the project file. Kunde ej skriva projektfilen ProjectFile Project File Projektfil Project Projekt <html><head/><body><p>In the build dir, cppcheck stores data about each translation unit.</p><p>With a build dir you get whole program analysis.</p><p>Unchanged files will be analyzed much faster; Cppcheck skip the analysis of these files and reuse their old data.</p></body></html> I build dir sparar Cppcheck information för varje translation unit. Med build dir får du whole program analys. Omodifierade filer analyseras mycket fortare, Cppcheck hoppar över analysen och återanvänder den gamla informationen Cppcheck build dir (whole program analysis, faster analysis for unchanged files) Cppcheck build dir (whole program analys, snabbare analys för omodifierade filer) Paths and Defines Sökvägar och defines <html><head/><body><p>Cppcheck can import Visual studio solutions (*.sln), Visual studio projects (*.vcxproj) or compile databases.</p><p>Files to check, defines, include paths are imported.</p></body></html> Cppcheck kan importera Visual studio solutions (*.sln), Visual studio projekt (*.vcxproj) eller compile databases. Sökvägar och defines importeras. Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Importera Projekt (Visual Studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' Defines separeras med semicolon ';' &Root: Root: Rot: Libraries: Libraries: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. Obs: Lägg dina egna .cfg filer i samma folder som projekt filen. De skall isåfall visas ovan. Visual Studio Visual Studio ... ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> <html><head/><body><p>Du har ett val:</p><p> * Analysera alla Debug och Release konfigurationer</p><p> * Analysera bara den första matchande Debug konfigurationen</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Analysera alla Visual Studio konfigurationer Paths: Sökvägar: Add... Lägg till... Edit Redigera Remove Ta bort Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Include sökvägar: Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> External tools Includes Include Include directories: Include sökvägar Up Upp Down Ned Checking Platform Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Warning options Root path: Bas sökväg: Warning tags (separated by semicolon) Varnings taggar (separerade med semikolon) Exclude source files in paths Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Cppcheck build dir (whole program analys, incremental analys, statistik, etc) Libraries Libraries Exclude Exkludera Suppressions Suppressions Suppression list: Suppression-list: Add Lägg till Addons and tools Addons Addons Y2038 Y2038 Thread safety Tråd säkerhet Coding standards Kodstandarder Cert Cert Extra Tools Extra verktyg It is common best practice to use several tools. Best practice är att använda flera verktyg Clang analyzer Clang analyzer Clang-tidy Clang-tidy Defines: Defines: ProjectFileDialog Project file: %1 Projektfil: %1 (no rule texts file) Clang-tidy (not found) Select Cppcheck build dir Välj Cppcheck build dir Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Visual Studio (*.sln *.vcxproj);;Compile database (compile_commands.json) Select include directory Välj include sökväg Select MISRA rule texts file Misra rule texts file (%1) Select a directory to check Välj mapp att analysera Visual Studio Visual Studio Compile database Borland C++ Builder 6 Import Project Importera Projekt Visual Studio (*.sln *.vcxproj);;Compile database (compile_database.json) Visual Studio (*.sln *.vcxproj);;Compile database (compile_database.json) Select directory to ignore Välj sökväg att ignorera Add Suppression Lägg till Suppression Select error id suppress: Välj error Id suppress: QDialogButtonBox OK OK Cancel Avbryt Close Stäng Save Spara QObject Unknown language specified! Okänt språk valt! Language file %1 not found! Language file %1.qm not found! Språk filen %1 hittades ej! Failed to load translation for language %1 from file %2 Failed to load translation for language %1 from file %2.qm Misslyckades med att ladda översättningen för %1 från filen %2 line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK OK Cancel Avbryt Close Stäng Save Spara ResultsTree File Fil Severity Typ Line Rad Summary Sammanfattning Undefined file Odefinierad fil Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [Inconclusive] debug debug note note Recheck Analysera om Copy filename Kopiera filnamn Copy full path Kopiera full sökväg Copy message Kopiera meddelande Copy message id Kopiera meddelande id Hide Dölj Hide all with id Dölj alla med id Suppress selected id(s) Stäng av valda id Open containing folder Öppna mapp Tag Tag No tag Ingen tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. Ingen editor konfigurerad. Konfigurera program i inställningar/program. No default editor application selected. Please select the default editor application in preferences/Applications. Ingen standard editor vald. Vänligen välj standard editor i inställningar/Program. Could not find the file! Kunde inte hitta filen! Could not start %1 Please check the application path and parameters are correct. Kunde inte starta %1 Kontrollera att sökvägen och parametrarna är korrekta. Could not find file: %1 Please select the directory where file is located. Kunde inte hitta filen: %1 Välj mappen där filen finns. Select Directory Välj mapp Id Id Inconclusive Inconclusive Since date Sedan datum style stil error fel warning varning performance prestanda portability portabilitet information information ResultsView Print Report Skriv ut rapport No errors found, nothing to print. Inga fel hittades, inget att skriva ut. %p% (%1 of %2 files checked) %p% (%1 av %2 filer analyserade) Cppcheck Cppcheck No errors found. Inga fel hittades. Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. Fel hittades, men de visas ej. För att ställa in vilka fel som skall visas använd visa menyn. Failed to read the report. Misslyckades att läsa rapporten. XML format version 1 is no longer supported. XML format version 1 stöds ej längre. Summary Sammanfattning Message Meddelande First included by Först inkluderad av Id Id Clear Log Copy this Log entry Copy complete Log No errors found, nothing to save. Inga fel hittades, ingenting att spara. Failed to save the report. Misslyckades med att spara rapporten. Results Resultat Analysis Log Analys Log Warning Details Varningsdetaljer ScratchPad Scratchpad Scratchpad Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename Filnamn Check Analysera Settings Preferences Inställningar General Allmänt Include paths: Include sökvägar: Add... Lägg till... Number of threads: Antal trådar: Ideal count: Optimalt värde: Force checking all #ifdef configurations Check all #ifdef configurations Kontrollera alla #ifdef konfigurationer Show full path of files Visa den fulla sökvägen för filer Show "No errors found" message when no errors found Visa "Inga fel hittades" meddelande när inga fel hittas Display error Id in column "Id" Visa meddelande id i kolumn "Id" Enable inline suppressions Använd inline suppressions Check for inconclusive errors also Kör inconclusive analys Show statistics on check completion Visa statistik när analys är klar Show internal warnings in log Visa interna fel i loggen Addons Addons Python binary (leave this empty to use python in the PATH) Python binär fil (lämna tom för att använda python i PATH) ... ... Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang Clang path (leave empty to use system PATH) Clang sökväg (lämna tom för att använda PATH) Visual Studio headers Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> <html><head/><body><p>Sökvägar till Visual Studio headers, separerade med semikolon ';'.</p><p>Du kan öppna en Visual Studio command prompt, och skriva &quot;SET INCLUDE&quot;. Sedan kopiera och klistra in sökvägarna.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths Sökvägar Edit Redigera Remove Ta bort Applications Program Edit... Redigera... Set as default Sätt förvald Reports Rapporter Save all errors when creating report Spara alla fel Save full path to files in reports Spara fulla sökvägar Language Språk Advanced Avancerade &Show inconclusive errors Visa inconclusive meddelanden S&how internal warnings in log Visa interna fel i loggen SettingsDialog N/A Ej tillgängligt Add a new application Lägg till program Modify an application Ändra program [Default] [Vald] [Default] [Förvald] Select python binary Välj python binär Select MISRA File Select clang path Välj Clang sökväg Select include directory Välj include mapp StatsDialog Statistics Statistik Project Projekt Project: Projekt: Paths: Sökvägar: Include paths: Include sökvägar: Defines: Defines: Undefines: Previous Scan Föregående analys Path Selected: Vald sökväg: Number of Files Scanned: Antal analyserade filer: Scan Duration: Analys tid: Errors: Fel: Warnings: Varningar: Stylistic warnings: Stil varningar: Portability warnings: Portabilitets varningar: Performance issues: Prestanda varningar: Information messages: Informations meddelanden: History Historik File: Fil: Copy to Clipboard Kopiera Pdf Export Pdf Export 1 day 1 dag %1 days %1 dagar 1 hour 1 timme %1 hours %1 timmar 1 minute 1 minut %1 minutes %1 minuter 1 second 1 sekund %1 seconds %1 sekunder 0.%1 seconds 0.%1 sekunder and och Export PDF Exportera PDF Project Settings Projekt inställningar Paths Sökvägar Include paths Include sökvägar Defines Definitioner Undefines Path selected Vald sökväg Number of files scanned Antal analyserade filer Scan duration Tid Errors Fel File: Fil: No cppcheck build dir Ingen Cppcheck build dir Warnings Varningar Style warnings Stil varningar Portability warnings Portabilitetsvarningar Performance warnings Prestanda varningar Information messages Informationsmeddelanden ThreadResult %1 of %2 files checked %1 av %2 filer analyserade TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. Misslyckades att ändra språk: %1 Språket har nollställts till Engelska. Öppna Preferences och välj något av de tillgängliga språken. Cppcheck Cppcheck TxtReport inconclusive inconclusive toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppcheck_zh_CN.ts000066400000000000000000003243621357737443600172550ustar00rootroot00000000000000 About About Cppcheck 关于 Cppcheck Version %1 版本 %1 Cppcheck - A tool for static C/C++ code analysis. Cppcheck - C/C++ 静态代码分析工具。 Copyright © 2007-2019 Cppcheck team. Copyright © 2007-2018 Cppcheck team. 版权所有 © 2007-2018 Daniel Marjamäki 与 Cppcheck 团队。 This program is licensed under the terms of the GNU General Public License version 3 该程序在 GNU 通用公共授权版本 3 的条款下发布 Visit Cppcheck homepage at %1 访问 Cppcheck 主页: %1 <html><head/><body> <p>Many thanks to these libraries that we use:</p><ul> <li>tinyxml2</li> <li>picojson</li> <li>pcre</li> <li>qt</li></ul></body></html> ApplicationDialog Add an application 添加应用程序 Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. The following texts in parameters are replaced with appropriate values when application is executed: (file) - Filename containing the error (line) - Line number containing the error (message) - Error message (severity) - Error severity Example opening a file with Kate and make Kate scroll to the correct line: Executable: kate Parameters: -l(line) (file) 在这里,你可以添加一个应用程序,用于打开错误文件。请为该应用程序指定一个名称、可执行文件和命令行参数。 当应用程序执行时,在参数中的以下文本将会替换为适当的值: (file) - 包含错误的文件名称 (line) - 包含错误的行号 (message) - 错误消息 (severity) - 错误严重性 示例:使用 Kate 打开一个文件,并使之滚动到相应的行: 可执行文件: kate 参数: -l(line) (file) &Name: 名称(&N): &Executable: 可执行文件(&E): &Parameters: 参数(&P): Browse 浏览 Executable files (*.exe);;All files(*.*) 可执行文件(*.exe);;所有文件(*.*) Select viewer application 选择查看应用程序 Cppcheck Cppcheck You must specify a name, a path and optionally parameters for the application! 你必须为应用程序指定名称、路径以及可选参数! FileViewDialog Could not find the file: %1 无法找到文件: %1 Cppcheck Cppcheck Could not read the file: %1 无法读取文件: %1 LibraryAddFunctionDialog Add function Function name(s) Number of arguments LibraryDialog Library Editor Open Save Save as Functions Sort Add Filter: Comments noreturn False True Unknown return value must be used ignore function in leaks checking Arguments Edit 编辑 Library files (*.cfg) Open library file Cppcheck Cppcheck Cannot open file %1. Can not open file %1. Failed to load %1. %2. Cannot save file %1. Can not save file %1. Save the library as LibraryEditArgDialog Edit argument <html><head/><body> <p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> <p>Typically, set this if the argument is a pointer, size, etc.</p> <p>Example:</p> <pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> </body></html> Not bool <html><head/><body> <p>Is a null parameter value allowed?</p> <p>Typically this should be used on any pointer parameter that does not allow null.</p> <p>Example:</p> <pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> </body></html> Not null Not uninit String Format string Min size of buffer Type None argvalue mul strlen Arg Arg2 and Valid values LogView Checking Log 正在检查记录 Clear 清空 Save Log 保存记录 Text files (*.txt *.log);;All files (*.*) 文本文件(*.txt *.log);;所有文件(*.*) Cppcheck Cppcheck Could not open file for writing: "%1" 无法打开并写入文件: “%1” MainWindow Cppcheck Cppcheck &File 文件(&F) &View 查看(&V) &Toolbars 工具栏(&T) &Help 帮助(&H) &Check 检查(&C) C++ standard &C standard C standard &Edit 编辑(&E) Standard 标准 Categories 分类 &License... 许可证(&L)... A&uthors... 作者(&U)... &About... 关于(&A)... &Files... 文件(&F)... Analyze files Check files 检查文件 Ctrl+F Ctrl+F &Directory... 目录(&D)... Analyze directory Check directory 检查目录 Ctrl+D Ctrl+D &Recheck files 重新检查文件(&R) Ctrl+R Ctrl+R &Stop 停止(&S) Stop analysis Stop checking 停止检查 Esc Esc &Save results to file... 保存结果到文件(&S)... Ctrl+S Ctrl+S &Quit 退出(&Q) &Clear results 清空结果(&C) &Preferences 首选项(&P) Style warnings 风格警告 Show style warnings 显示风格警告 Errors 错误 Show errors 显示错误 Show S&cratchpad... 显示便条(&C)... Information 信息 Show information messages 显示信息消息 Portability 移植可能性 Show portability warnings 显示可移植性警告 Show Cppcheck results Clang Show Clang results &Filter 滤器(&F) Filter results 过滤结果 Windows 32-bit ANSI Windows 32-bit Unicode Unix 32-bit Unix 64-bit Windows 64-bit Platforms 平台 C++11 C++11 C99 C99 Posix Posix C11 C11 C89 C89 C++03 C++03 &Print... Print the Current Report Print Pre&view... Open a Print Preview Dialog for the Current Results Open library editor C&lose Project File 关闭项目文件(&L) &Edit Project File... 编辑项目文件(&E)... &Statistics 统计(&S) Warnings 警告 Show warnings 显示警告 Performance warnings 性能警告 Show performance warnings 显示性能警告 Show &hidden 显示隐藏项(&H) &Check all 全部选中(&C) A&nalyze Filter 滤器 &Reanalyze modified files &Recheck modified files Reanal&yze all files Style war&nings E&rrors &Uncheck all 全部取消选中(&U) Collapse &all 全部折叠(&A) &Expand all 全部展开(&E) &Standard 标准(&S) Standard items 标准项 &Contents 内容(&C) Open the help contents 打开帮助内容 F1 F1 Toolbar 工具栏 &Categories 分类(&C) Error categories 错误分类 &Open XML... 打开 XML (&O)... Open P&roject File... 打开项目文件(&R)... Sh&ow Scratchpad... &New Project File... 新建项目文件(&N)... &Log View 日志视图(&L) Log View 日志视图 &Warnings Per&formance warnings &Information &Portability P&latforms C++&11 C&99 &Posix C&11 &C89 &C++03 &Library Editor... &Auto-detect language &Enforce C++ E&nforce C C++14 C++14 Reanalyze and check library Check configuration (defines, includes) C++17 C++17 C++20 C++20 There was a problem with loading the editor application settings. This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. 加载编辑器应用程序设置出错。 这可能是因为 Cppcheck 不同版本间的设置有所不同。请检查(并修复)编辑器应用程序设置,否则编辑器程序可能不会正确启动。 No suitable files found to check! 未发现适合检查的文件! You must close the project file before selecting new files or directories! 在选择新的文件或目录之前,你必须先关闭此项目文件! Select directory to check 选择目录来检查 Quick Filter: 快速滤器: Select files to check 选择要检查的文件 Select configuration Found project file: %1 Do you want to load this project file instead? 找到项目文件: %1 你是否想加载该项目文件? Found project files from the directory. Do you want to proceed checking without using any of these project files? 在目录中找到项目文件。 你是否想在不使用这些项目文件的情况下,执行检查? The library '%1' contains unknown elements: %2 File not found Bad XML Missing attribute Bad attribute value Unsupported format Duplicate platform type Platform type redefined Unknown element Unknown issue Failed to load the selected library '%1'. %2 Error Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. Current results will be cleared. Opening a new XML file will clear current results.Do you want to proceed? 当前结果将被清空。 打开一个新的 XML 文件将会清空当前结果。你要继续吗? XML files (*.xml) XML 文件(*.xml) Open the report file 打开报告文件 Checking is running. Do you want to stop the checking and exit Cppcheck? 检查正在执行。 你是否需要停止检查并退出 Cppcheck? License 许可证 Authors 作者 XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv) XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) XML 文件版本 2 (*.xml);;XML 文件版本 1 (*.xml);; 文本文件(*.txt);; CSV 文件(*.csv) Save the report file 保存报告文件 XML files version 1 (*.xml) XML 文件版本 1 (*.xml) XML files version 2 (*.xml) XML 文件版本 2 (*.xml) Text files (*.txt) 文本文件(*.txt) CSV files (*.csv) CSV 文件(*.csv) Cppcheck - %1 Cppcheck - %1 Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. 更改用户界面语言失败: %1 用户界面语言已被重置为英语。打开“首选项”对话框,选择任何可用的语言。 Project files (*.cppcheck);;All files(*.*) 项目文件(*.cppcheck);;所有文件(*.*) Select Project File 选择项目文件 Project: 项目: No suitable files found to analyze! C/C++ Source Compile database Visual Studio Borland C++ Builder 6 Select files to analyze Select directory to analyze Select the configuration that will be analyzed Found project files from the directory. Do you want to proceed analysis without using any of these project files? Current results will be cleared. Opening a new XML file will clear current results. Do you want to proceed? Analyzer is running. Do you want to stop the analysis and exit Cppcheck? XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) Build dir '%1' does not exist, create it? Failed to import '%1', analysis is stopped Project files (*.cppcheck) Select Project Filename 选择项目文件名 No project file loaded 项目文件未加载 The project file %1 could not be found! Do you want to remove the file from the recently used projects -list? 项目文件 %1 未找到! 你要从最近使用的项目列表中删除此文件吗? Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> This option is for installation scripts so they can configure the directory where datafiles are located (translations, cfg). The GUI is not started when this option is used. Cppcheck GUI. Syntax: cppcheck-gui [OPTIONS] [files or paths] Options: -h, --help Print this help -p <file> Open given project file and start checking it -l <file> Open given results xml file -d <directory> Specify the directory that was checked to generate the results xml specified with -l -v, --version Show program version --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) Cppcheck GUI - Command line parameters NewSuppressionDialog New suppression Error ID File name Line number Symbol name Edit suppression Platforms Built-in 内置 Native Unix 32-bit Unix 64-bit Windows 32-bit ANSI Windows 32-bit Unicode Windows 64-bit Project Cppcheck Cppcheck Could not read the project file. 无法读取项目文件。 Could not write the project file. 无法写入项目文件。 ProjectFile Project File 项目文件 Project 项目 Paths and Defines Import Project (Visual studio / compile database/ Borland C++ Builder 6) Import Project (Visual studio / compile database) Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int Defines must be separated by a semicolon ';' &Root: Root: 根目录: Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. MISRA C 2012 Misra rule texts <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> ... <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> Browse... Analyze all Visual Studio configurations Paths: 路径: Add... 添加... Edit 编辑 Remove 移除 Undefines: Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 Include Paths: Analysis Check code in headers (slower analysis, more results) Check code in unused templates (slower and less accurate analysis) Max CTU depth Exclude source files in paths Note: Addons require <a href="https://www.python.org/">Python</a> beeing installed. External tools Includes 包含 Include directories: Include 目录: Up 向上 Down 向下 Checking Platform Warning options Root path: Warning tags (separated by semicolon) Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) Libraries Exclude 排除 Suppressions Add Addons and tools Addons Y2038 Thread safety Coding standards Cert Clang analyzer Clang-tidy Defines: 定义: ProjectFileDialog Project file: %1 项目文件: %1 Select Cppcheck build dir Select include directory 选择 Include 目录 Select a directory to check 选择一个检查目录 (no rule texts file) Clang-tidy (not found) Visual Studio Compile database Borland C++ Builder 6 Import Project Select directory to ignore 选择忽略的目录 Select MISRA rule texts file Misra rule texts file (%1) QDialogButtonBox OK Cancel Close 关闭 Save QObject Unknown language specified! 指定了未知语言! Language file %1 not found! 语言文件 %1 不存在! Failed to load translation for language %1 from file %2 无法从文件 %2 中为语言 %1 加载翻译文件 line %1: Unhandled element %2 (Not found) Thin ExtraLight Light Normal Medium DemiBold Bold ExtraBold Black Editor Foreground Color Editor Background Color Highlight Background Color Line Number Foreground Color Line Number Background Color Keyword Foreground Color Keyword Font Weight Class Foreground Color Class ForegroundColor Class Font Weight Quote Foreground Color Quote Font Weight Comment Foreground Color Comment Font Weight Symbol Foreground Color Symbol Background Color Symbol Font Weight Set to Default Light Set to Default Dark QPlatformTheme OK Cancel Close 关闭 Save ResultsTree File 文件 Severity 严重性 Line Summary 概要 Undefined file 未定义文件 Copy Could not find file: Please select the folder '%1' Select Directory '%1' Please select the directory where file is located. [Inconclusive] [不确定的] debug 调试 note Recheck Copy filename 复制文件名 Copy full path 复制完整路径 Copy message 复制消息 Copy message id 复制消息 ID Hide 隐藏 Hide all with id Suppress selected id(s) Open containing folder Tag No tag Cppcheck Cppcheck No editor application configured. Configure the editor application for Cppcheck in preferences/Applications. Configure the text file viewer program in Cppcheck preferences/Applications. 编辑应用程序未配置。 在“首先项 / 应用程序”中为 Cppcheck 配置编辑应用程序。 No default editor application selected. Please select the default editor application in preferences/Applications. 未选中默认编辑应用程序。 请在“首先项 / 应用程序”中选择默认应用程序。 Could not find the file! 找不到文件! Could not start %1 Please check the application path and parameters are correct. 无法启动 %1 请检查此应用程序的路径与参数是否正确。 Could not find file: %1 Please select the directory where file is located. 无法找到文件: %1 请选择文件所在目录。 Select Directory 选择目录 Id Id Inconclusive Since date style 风格 error 错误 warning 警告 performance 性能 portability 移植可能性 information 信息 ResultsView Results 结果 Analysis Log Warning Details No errors found, nothing to save. 未发现错误,没有结果可保存。 Failed to save the report. 保存报告失败。 Print Report No errors found, nothing to print. %p% (%1 of %2 files checked) %p% (%2 个文件已检查 %1 个) Cppcheck Cppcheck No errors found. 未发现错误。 Errors were found, but they are configured to be hidden. To toggle what kind of errors are shown, open view menu. 发现错误,但它们被设为隐藏。 打开“查看”菜单,切换需要显示的错误。 Failed to read the report. 读取报告失败。 XML format version 1 is no longer supported. Summary 概要 Message 消息 First included by Id Id Clear Log Copy this Log entry Copy complete Log ScratchPad Scratchpad 便条 Copy or write some C/C++ code here: Optionally enter a filename (mainly for automatic language detection) and click on "Check": filename 文件名 Check 检查 Settings Preferences 首选项 General 常规 Include paths: Include 路径: Add... 添加... Number of threads: 线程个数: Ideal count: 理想个数: Force checking all #ifdef configurations Check all #ifdef configurations 强制检查所有 #ifdef 配置 Show full path of files 显示文件的完整路径 Show "No errors found" message when no errors found 当未找到错误,显示“未发现错误”消息 Display error Id in column "Id" 在列“Id”中显示错误 Id Enable inline suppressions 启用内联方案 Check for inconclusive errors also Show statistics on check completion Show internal warnings in log Addons Python binary (leave this empty to use python in the PATH) ... Misra addon Misra rule texts file <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> Clang Clang path (leave empty to use system PATH) Visual Studio headers <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> Code Editor Code Editor Style System Style Default Light Style Default Dark Style Custom Paths 路径 Edit 编辑 Remove 移除 Applications 应用程序 Edit... 编辑... Set as default 设为默认 Reports 报告 Save all errors when creating report 创建报告时,保存所有错误 Save full path to files in reports 在报告中保存文件的完整路径 Language 语言 Advanced 高级 &Show inconclusive errors 显示不确定的错误(&S) S&how internal warnings in log 在日记中显示内部警告(&H) SettingsDialog N/A N/A Add a new application 添加一个新的应用程序 Modify an application 修改一个应用程序 [Default] [Default] [默认] Select python binary Select MISRA File Select clang path Select include directory 选择包含目录 StatsDialog Statistics 统计 Project 项目 Project: 项目: Paths: 路径: Include paths: 包含路径: Defines: 定义: Undefines: Previous Scan 上一次扫描 Path Selected: 选中的路径: Number of Files Scanned: 扫描的文件数: Scan Duration: 扫描时间: Errors: 错误: Warnings: 警告: Stylistic warnings: Stylistic 警告: Portability warnings: 可移植性警告: Performance issues: 性能警告: Information messages: 信息: History File: Copy to Clipboard 复制到剪贴板 Pdf Export 1 day 1 天 %1 days %1 天 1 hour 1 小时 %1 hours %1 小时 1 minute 1 分钟 %1 minutes %1 分钟 1 second 1 秒 %1 seconds %1 秒 0.%1 seconds 0.%1 秒 and Export PDF Project Settings 项目设置 Paths 路径 Include paths 包含路径 Defines 定义 Undefines Path selected 选中的路径 Number of files scanned 扫描的文件数 Scan duration 扫描时间 Errors 错误 File: No cppcheck build dir Warnings 警告 Style warnings 风格警告 Portability warnings 移植可能性警告 Performance warnings 性能警告 Information messages 信息 ThreadResult %1 of %2 files checked %2 个文件已检查 %1 个 TranslationHandler Failed to change the user interface language: %1 The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. 更改用户界面语言失败: %1 用户界面语言已被重置为英语。打开“首选项”对话框,选择任何可用的语言。 Cppcheck Cppcheck TxtReport inconclusive 不确定的 toFilterString All supported files (%1) All files (%1) cppcheck-1.90/gui/cppchecklibrarydata.cpp000066400000000000000000000515101357737443600205370ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppchecklibrarydata.h" #include #include const unsigned int CppcheckLibraryData::Function::Arg::ANY = ~0U; const unsigned int CppcheckLibraryData::Function::Arg::VARIADIC = ~1U; CppcheckLibraryData::CppcheckLibraryData() { } static std::string unhandledElement(const QXmlStreamReader &xmlReader) { throw std::runtime_error(QObject::tr("line %1: Unhandled element %2").arg(xmlReader.lineNumber()).arg(xmlReader.name().toString()).toStdString()); } static CppcheckLibraryData::Container loadContainer(QXmlStreamReader &xmlReader) { CppcheckLibraryData::Container container; container.id = xmlReader.attributes().value("id").toString(); container.inherits = xmlReader.attributes().value("inherits").toString(); container.startPattern = xmlReader.attributes().value("startPattern").toString(); container.endPattern = xmlReader.attributes().value("endPattern").toString(); container.opLessAllowed = xmlReader.attributes().value("opLessAllowed").toString(); container.itEndPattern = xmlReader.attributes().value("itEndPattern").toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "container") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "type") { container.type.templateParameter = xmlReader.attributes().value("templateParameter").toString(); container.type.string = xmlReader.attributes().value("string").toString(); } else if (elementName == "size" || elementName == "access" || elementName == "other") { const QString indexOperator = xmlReader.attributes().value("indexOperator").toString(); if (elementName == "access" && indexOperator == "array-like") container.access_arrayLike = true; const QString templateParameter = xmlReader.attributes().value("templateParameter").toString(); if (elementName == "size" && !templateParameter.isEmpty()) container.size_templateParameter = templateParameter.toInt(); for (;;) { type = xmlReader.readNext(); if (xmlReader.name().toString() == elementName) break; if (type != QXmlStreamReader::StartElement) continue; struct CppcheckLibraryData::Container::Function function; function.name = xmlReader.attributes().value("name").toString(); function.action = xmlReader.attributes().value("action").toString(); function.yields = xmlReader.attributes().value("yields").toString(); if (elementName == "size") container.sizeFunctions.append(function); else if (elementName == "access") container.accessFunctions.append(function); else container.otherFunctions.append(function); } } else { unhandledElement(xmlReader); } } return container; } static CppcheckLibraryData::Define loadDefine(const QXmlStreamReader &xmlReader) { CppcheckLibraryData::Define define; define.name = xmlReader.attributes().value("name").toString(); define.value = xmlReader.attributes().value("value").toString(); return define; } static QString loadUndefine(const QXmlStreamReader &xmlReader) { return xmlReader.attributes().value("name").toString(); } static CppcheckLibraryData::Function::Arg loadFunctionArg(QXmlStreamReader &xmlReader) { CppcheckLibraryData::Function::Arg arg; QString argnr = xmlReader.attributes().value("nr").toString(); if (argnr == "any") arg.nr = CppcheckLibraryData::Function::Arg::ANY; else if (argnr == "variadic") arg.nr = CppcheckLibraryData::Function::Arg::VARIADIC; else arg.nr = argnr.toUInt(); arg.defaultValue = xmlReader.attributes().value("default").toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "arg") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "not-bool") arg.notbool = true; else if (elementName == "not-null") arg.notnull = true; else if (elementName == "not-uninit") arg.notuninit = true; else if (elementName == "strz") arg.strz = true; else if (elementName == "formatstr") arg.formatstr = true; else if (elementName == "valid") arg.valid = xmlReader.readElementText(); else if (elementName == "minsize") { CppcheckLibraryData::Function::Arg::MinSize minsize; minsize.type = xmlReader.attributes().value("type").toString(); minsize.arg = xmlReader.attributes().value("arg").toString(); minsize.arg2 = xmlReader.attributes().value("arg2").toString(); arg.minsizes.append(minsize); } else if (elementName == "iterator") { arg.iterator.container = xmlReader.attributes().value("container").toInt(); arg.iterator.type = xmlReader.attributes().value("type").toString(); } else { unhandledElement(xmlReader); } } return arg; } static CppcheckLibraryData::Function loadFunction(QXmlStreamReader &xmlReader, const QString &comments) { CppcheckLibraryData::Function function; function.comments = comments; function.name = xmlReader.attributes().value("name").toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != "function") { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "noreturn") function.noreturn = (xmlReader.readElementText() == "true") ? CppcheckLibraryData::Function::True : CppcheckLibraryData::Function::False; else if (elementName == "pure") function.gccPure = true; else if (elementName == "const") function.gccConst = true; else if (elementName == "leak-ignore") function.leakignore = true; else if (elementName == "use-retval") function.useretval = true; else if (elementName == "returnValue") { const QString container = xmlReader.attributes().value("container").toString(); function.returnValue.container = container.isNull() ? -1 : container.toInt(); function.returnValue.type = xmlReader.attributes().value("type").toString(); function.returnValue.value = xmlReader.readElementText(); } else if (elementName == "formatstr") { function.formatstr.scan = xmlReader.attributes().value("scan").toString(); function.formatstr.secure = xmlReader.attributes().value("secure").toString(); } else if (elementName == "arg") function.args.append(loadFunctionArg(xmlReader)); else if (elementName == "warn") { function.warn.severity = xmlReader.attributes().value("severity").toString(); function.warn.cstd = xmlReader.attributes().value("cstd").toString(); function.warn.reason = xmlReader.attributes().value("reason").toString(); function.warn.alternatives = xmlReader.attributes().value("alternatives").toString(); function.warn.msg = xmlReader.readElementText(); } else { unhandledElement(xmlReader); } } return function; } static CppcheckLibraryData::MemoryResource loadMemoryResource(QXmlStreamReader &xmlReader) { CppcheckLibraryData::MemoryResource memoryresource; memoryresource.type = xmlReader.name().toString(); QXmlStreamReader::TokenType type; while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || xmlReader.name().toString() != memoryresource.type) { if (type != QXmlStreamReader::StartElement) continue; const QString elementName = xmlReader.name().toString(); if (elementName == "alloc") { CppcheckLibraryData::MemoryResource::Alloc alloc; alloc.init = (xmlReader.attributes().value("init").toString() == "true"); alloc.name = xmlReader.readElementText(); memoryresource.alloc.append(alloc); } else if (elementName == "dealloc") memoryresource.dealloc.append(xmlReader.readElementText()); else if (elementName == "use") memoryresource.use.append(xmlReader.readElementText()); else unhandledElement(xmlReader); } return memoryresource; } static CppcheckLibraryData::PodType loadPodType(const QXmlStreamReader &xmlReader) { CppcheckLibraryData::PodType podtype; podtype.name = xmlReader.attributes().value("name").toString(); podtype.size = xmlReader.attributes().value("size").toString(); podtype.sign = xmlReader.attributes().value("sign").toString(); return podtype; } QString CppcheckLibraryData::open(QIODevice &file) { clear(); QString comments; QXmlStreamReader xmlReader(&file); while (!xmlReader.atEnd()) { const QXmlStreamReader::TokenType t = xmlReader.readNext(); switch (t) { case QXmlStreamReader::Comment: if (!comments.isEmpty()) comments += "\n"; comments += xmlReader.text().toString(); break; case QXmlStreamReader::StartElement: try { const QString elementName(xmlReader.name().toString()); if (elementName == "def") ; else if (elementName == "container") containers.append(loadContainer(xmlReader)); else if (elementName == "define") defines.append(loadDefine(xmlReader)); else if (elementName == "undefine") undefines.append(loadUndefine(xmlReader)); else if (elementName == "function") functions.append(loadFunction(xmlReader, comments)); else if (elementName == "memory" || elementName == "resource") memoryresource.append(loadMemoryResource(xmlReader)); else if (elementName == "podtype") podtypes.append(loadPodType(xmlReader)); else unhandledElement(xmlReader); } catch (std::runtime_error &e) { return e.what(); } comments.clear(); break; default: break; } } return QString(); } static void writeContainerFunctions(QXmlStreamWriter &xmlWriter, const QString &name, int extra, const QList &functions) { if (functions.isEmpty() && extra < 0) return; xmlWriter.writeStartElement(name); if (extra >= 0) { if (name == "access") xmlWriter.writeAttribute("indexOperator", "array-like"); else if (name == "size") xmlWriter.writeAttribute("templateParameter", QString::number(extra)); } foreach (const CppcheckLibraryData::Container::Function &function, functions) { xmlWriter.writeStartElement("function"); xmlWriter.writeAttribute("name", function.name); if (!function.action.isEmpty()) xmlWriter.writeAttribute("action", function.action); if (!function.yields.isEmpty()) xmlWriter.writeAttribute("yields", function.yields); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } static void writeContainer(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Container &container) { xmlWriter.writeStartElement("container"); xmlWriter.writeAttribute("id", container.id); if (!container.startPattern.isEmpty()) xmlWriter.writeAttribute("startPattern", container.startPattern); if (!container.endPattern.isNull()) xmlWriter.writeAttribute("endPattern", container.endPattern); if (!container.inherits.isEmpty()) xmlWriter.writeAttribute("inherits", container.inherits); if (!container.opLessAllowed.isEmpty()) xmlWriter.writeAttribute("opLessAllowed", container.opLessAllowed); if (!container.itEndPattern.isEmpty()) xmlWriter.writeAttribute("itEndPattern", container.itEndPattern); if (!container.type.templateParameter.isEmpty() || !container.type.string.isEmpty()) { xmlWriter.writeStartElement("type"); if (!container.type.templateParameter.isEmpty()) xmlWriter.writeAttribute("templateParameter", container.type.templateParameter); if (!container.type.string.isEmpty()) xmlWriter.writeAttribute("string", container.type.string); xmlWriter.writeEndElement(); } writeContainerFunctions(xmlWriter, "size", container.size_templateParameter, container.sizeFunctions); writeContainerFunctions(xmlWriter, "access", container.access_arrayLike?1:-1, container.accessFunctions); writeContainerFunctions(xmlWriter, "other", -1, container.otherFunctions); xmlWriter.writeEndElement(); } static void writeFunction(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Function &function) { QString comments = function.comments; while (comments.startsWith("\n")) comments = comments.mid(1); while (comments.endsWith("\n")) comments.chop(1); foreach (const QString &comment, comments.split('\n')) { if (comment.length() >= 1) xmlWriter.writeComment(comment); } xmlWriter.writeStartElement("function"); xmlWriter.writeAttribute("name", function.name); if (function.useretval) xmlWriter.writeEmptyElement("use-retval"); if (function.gccConst) xmlWriter.writeEmptyElement("const"); if (function.gccPure) xmlWriter.writeEmptyElement("pure"); if (!function.returnValue.empty()) { xmlWriter.writeStartElement("returnValue"); if (!function.returnValue.type.isNull()) xmlWriter.writeAttribute("type", function.returnValue.type); if (function.returnValue.container >= 0) xmlWriter.writeAttribute("container", QString::number(function.returnValue.container)); if (!function.returnValue.value.isNull()) xmlWriter.writeCharacters(function.returnValue.value); xmlWriter.writeEndElement(); } if (function.noreturn != CppcheckLibraryData::Function::Unknown) xmlWriter.writeTextElement("noreturn", (function.noreturn == CppcheckLibraryData::Function::True) ? "true" : "false"); if (function.leakignore) xmlWriter.writeEmptyElement("leak-ignore"); // Argument info.. foreach (const CppcheckLibraryData::Function::Arg &arg, function.args) { if (arg.formatstr) { xmlWriter.writeStartElement("formatstr"); if (!function.formatstr.scan.isNull()) xmlWriter.writeAttribute("scan", function.formatstr.scan); if (!function.formatstr.secure.isNull()) xmlWriter.writeAttribute("secure", function.formatstr.secure); xmlWriter.writeEndElement(); } xmlWriter.writeStartElement("arg"); if (arg.nr == CppcheckLibraryData::Function::Arg::ANY) xmlWriter.writeAttribute("nr", "any"); else if (arg.nr == CppcheckLibraryData::Function::Arg::VARIADIC) xmlWriter.writeAttribute("nr", "variadic"); else xmlWriter.writeAttribute("nr", QString::number(arg.nr)); if (!arg.defaultValue.isNull()) xmlWriter.writeAttribute("default", arg.defaultValue); if (arg.formatstr) xmlWriter.writeEmptyElement("formatstr"); if (arg.notnull) xmlWriter.writeEmptyElement("not-null"); if (arg.notuninit) xmlWriter.writeEmptyElement("not-uninit"); if (arg.notbool) xmlWriter.writeEmptyElement("not-bool"); if (arg.strz) xmlWriter.writeEmptyElement("strz"); if (!arg.valid.isEmpty()) xmlWriter.writeTextElement("valid",arg.valid); foreach (const CppcheckLibraryData::Function::Arg::MinSize &minsize, arg.minsizes) { xmlWriter.writeStartElement("minsize"); xmlWriter.writeAttribute("type", minsize.type); xmlWriter.writeAttribute("arg", minsize.arg); if (!minsize.arg2.isEmpty()) xmlWriter.writeAttribute("arg2", minsize.arg2); xmlWriter.writeEndElement(); } if (arg.iterator.container >= 0 || !arg.iterator.type.isNull()) { xmlWriter.writeStartElement("iterator"); if (arg.iterator.container >= 0) xmlWriter.writeAttribute("container", QString::number(arg.iterator.container)); if (!arg.iterator.type.isNull()) xmlWriter.writeAttribute("type", arg.iterator.type); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } if (!function.warn.isEmpty()) { xmlWriter.writeStartElement("warn"); if (!function.warn.severity.isEmpty()) xmlWriter.writeAttribute("severity", function.warn.severity); if (!function.warn.cstd.isEmpty()) xmlWriter.writeAttribute("cstd", function.warn.cstd); if (!function.warn.alternatives.isEmpty()) xmlWriter.writeAttribute("alternatives", function.warn.alternatives); if (!function.warn.reason.isEmpty()) xmlWriter.writeAttribute("reason", function.warn.reason); if (!function.warn.msg.isEmpty()) xmlWriter.writeCharacters(function.warn.msg); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); } static void writeMemoryResource(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::MemoryResource &mr) { xmlWriter.writeStartElement(mr.type); foreach (const CppcheckLibraryData::MemoryResource::Alloc &alloc, mr.alloc) { xmlWriter.writeStartElement("alloc"); xmlWriter.writeAttribute("init", alloc.init ? "true" : "false"); xmlWriter.writeCharacters(alloc.name); xmlWriter.writeEndElement(); } foreach (const QString &dealloc, mr.dealloc) { xmlWriter.writeTextElement("dealloc", dealloc); } foreach (const QString &use, mr.use) { xmlWriter.writeTextElement("use", use); } xmlWriter.writeEndElement(); } QString CppcheckLibraryData::toString() const { QString outputString; QXmlStreamWriter xmlWriter(&outputString); xmlWriter.setAutoFormatting(true); xmlWriter.setAutoFormattingIndent(2); xmlWriter.writeStartDocument("1.0"); xmlWriter.writeStartElement("def"); xmlWriter.writeAttribute("format","2"); foreach (const Define &define, defines) { xmlWriter.writeStartElement("define"); xmlWriter.writeAttribute("name", define.name); xmlWriter.writeAttribute("value", define.value); xmlWriter.writeEndElement(); } foreach (const QString &undef, undefines) { xmlWriter.writeStartElement("undefine"); xmlWriter.writeAttribute("name", undef); xmlWriter.writeEndElement(); } foreach (const Function &function, functions) { writeFunction(xmlWriter, function); } foreach (const MemoryResource &mr, memoryresource) { writeMemoryResource(xmlWriter, mr); } foreach (const Container &container, containers) { writeContainer(xmlWriter, container); } foreach (const PodType &podtype, podtypes) { xmlWriter.writeStartElement("podtype"); xmlWriter.writeAttribute("name", podtype.name); if (!podtype.sign.isEmpty()) xmlWriter.writeAttribute("sign", podtype.sign); if (!podtype.size.isEmpty()) xmlWriter.writeAttribute("size", podtype.size); xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); return outputString; } cppcheck-1.90/gui/cppchecklibrarydata.h000066400000000000000000000116331357737443600202060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CPPCHECKLIBRARYDATA_H #define CPPCHECKLIBRARYDATA_H #include #include #include #include class CppcheckLibraryData { public: CppcheckLibraryData(); struct Container { Container() : access_arrayLike(false), size_templateParameter(-1) {} QString id; QString inherits; QString startPattern; QString endPattern; QString opLessAllowed; QString itEndPattern; bool access_arrayLike; int size_templateParameter; struct { QString templateParameter; QString string; } type; struct Function { QString name; QString yields; QString action; }; QList accessFunctions; QList otherFunctions; QList sizeFunctions; }; struct Define { QString name; QString value; }; struct Function { Function() : noreturn(Unknown), gccPure(false), gccConst(false), leakignore(false), useretval(false) { } QString comments; QString name; enum TrueFalseUnknown { False, True, Unknown } noreturn; bool gccPure; bool gccConst; bool leakignore; bool useretval; struct ReturnValue { ReturnValue() : container(-1) {} QString type; QString value; int container; bool empty() const { return type.isNull() && value.isNull() && container < 0; } } returnValue; struct { QString scan; QString secure; } formatstr; struct Arg { Arg() : nr(0), notbool(false), notnull(false), notuninit(false), formatstr(false), strz(false) { } QString name; unsigned int nr; static const unsigned int ANY; static const unsigned int VARIADIC; QString defaultValue; bool notbool; bool notnull; bool notuninit; bool formatstr; bool strz; QString valid; struct MinSize { QString type; QString arg; QString arg2; }; QList minsizes; struct Iterator { Iterator() : container(-1) {} int container; QString type; } iterator; }; QList args; struct { QString severity; QString cstd; QString reason; QString alternatives; QString msg; bool isEmpty() const { return cstd.isEmpty() && severity.isEmpty() && reason.isEmpty() && alternatives.isEmpty() && msg.isEmpty(); } } warn; }; struct MemoryResource { QString type; // "memory" or "resource" struct Alloc { Alloc() : init(false) {} bool init; QString name; }; QList alloc; QStringList dealloc; QStringList use; }; struct PodType { QString name; QString size; QString sign; }; void clear() { containers.clear(); defines.clear(); undefines.clear(); functions.clear(); memoryresource.clear(); podtypes.clear(); } void swap(CppcheckLibraryData &other) { containers.swap(other.containers); defines.swap(other.defines); undefines.swap(other.undefines); functions.swap(other.functions); memoryresource.swap(other.memoryresource); podtypes.swap(other.podtypes); } QString open(QIODevice &file); QString toString() const; QList containers; QList defines; QList functions; QList memoryresource; QList podtypes; QStringList undefines; }; #endif // LIBRARYDATA_H cppcheck-1.90/gui/csvreport.cpp000066400000000000000000000032541357737443600165710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "report.h" #include "csvreport.h" CsvReport::CsvReport(const QString &filename) : Report(filename) { } CsvReport::~CsvReport() { } bool CsvReport::create() { if (Report::create()) { mTxtWriter.setDevice(Report::getFile()); return true; } return false; } void CsvReport::writeHeader() { // No header for CSV report } void CsvReport::writeFooter() { // No footer for CSV report } void CsvReport::writeError(const ErrorItem &error) { /* Error as CSV line gui/test.cpp,23,error,Mismatching allocation and deallocation: k */ const QString file = QDir::toNativeSeparators(error.errorPath.back().file); QString line = QString("%1,%2,").arg(file).arg(error.errorPath.back().line); line += QString("%1,%2").arg(GuiSeverity::toString(error.severity)).arg(error.summary); mTxtWriter << line << endl; } cppcheck-1.90/gui/csvreport.h000066400000000000000000000035031357737443600162330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CSV_REPORT_H #define CSV_REPORT_H #include #include #include "report.h" /// @addtogroup GUI /// @{ /** * @brief CSV text file report. * This report exports results as CSV (comma separated values). CSV files are * easy to import to many other programs. * @todo This class should be inherited from TxtReport? */ class CsvReport : public Report { public: explicit CsvReport(const QString &filename); virtual ~CsvReport(); /** * @brief Create the report (file). * @return true if succeeded, false if file could not be created. */ virtual bool create() override; /** * @brief Write report header. */ virtual void writeHeader() override; /** * @brief Write report footer. */ virtual void writeFooter() override; /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error) override; private: /** * @brief Text stream writer for writing the report in text format. */ QTextStream mTxtWriter; }; /// @} #endif // CSV_REPORT_H cppcheck-1.90/gui/erroritem.cpp000066400000000000000000000060141357737443600165470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "erroritem.h" #include "common.h" QErrorPathItem::QErrorPathItem(const ErrorLogger::ErrorMessage::FileLocation &loc) : file(QString::fromStdString(loc.getfile(false))) , line(loc.line) , column(loc.column) , info(QString::fromStdString(loc.getinfo())) { } bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2) { return i1.file == i2.file && i1.column == i2.column && i1.line == i2.line && i1.info == i2.info; } ErrorItem::ErrorItem() : severity(Severity::none) , inconclusive(false) , cwe(-1) { } ErrorItem::ErrorItem(const ErrorLogger::ErrorMessage &errmsg) : errorId(QString::fromStdString(errmsg.id)) , severity(errmsg.severity) , inconclusive(errmsg.inconclusive) , summary(QString::fromStdString(errmsg.shortMessage())) , message(QString::fromStdString(errmsg.verboseMessage())) , cwe(errmsg.cwe.id) , symbolNames(QString::fromStdString(errmsg.symbolNames())) { for (std::list::const_iterator loc = errmsg.callStack.begin(); loc != errmsg.callStack.end(); ++loc) { errorPath << QErrorPathItem(*loc); } } QString ErrorItem::tool() const { if (errorId == CLANG_ANALYZER) return CLANG_ANALYZER; if (errorId.startsWith(CLANG_TIDY)) return CLANG_TIDY; if (errorId.startsWith("clang-")) return "clang"; return "cppcheck"; } QString ErrorItem::ToString() const { QString str = errorPath.back().file + " - " + errorId + " - "; if (inconclusive) str += "inconclusive "; str += GuiSeverity::toString(severity) +"\n"; str += summary + "\n"; str += message + "\n"; for (int i = 0; i < errorPath.size(); i++) { str += " " + errorPath[i].file + ": " + QString::number(errorPath[i].line) + "\n"; } return str; } bool ErrorItem::sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2) { // TODO: Implement some better CID calculation return errorItem1.errorId == errorItem2.errorId && errorItem1.errorPath == errorItem2.errorPath && errorItem1.file0 == errorItem2.file0 && errorItem1.message == errorItem2.message && errorItem1.inconclusive == errorItem2.inconclusive && errorItem1.severity == errorItem2.severity; } cppcheck-1.90/gui/erroritem.h000066400000000000000000000061571357737443600162240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ERRORITEM_H #define ERRORITEM_H #include #include #include #include "errorlogger.h" class ErrorLine; /// @addtogroup GUI /// @{ /** * @brief GUI versions of severity conversions. * GUI needs wrappers for conversion functions since GUI uses Qt's QString * instead of the std::string used by lib/cli. */ class GuiSeverity { public: static QString toString(Severity::SeverityType severity) { return QString::fromStdString(Severity::toString(severity)); } static Severity::SeverityType fromString(const QString &severity) { return Severity::fromString(severity.toStdString()); } }; /** * @brief A class containing data for one error path item */ class QErrorPathItem { public: QErrorPathItem() : line(0), column(-1) {} explicit QErrorPathItem(const ErrorLogger::ErrorMessage::FileLocation &loc); QString file; int line; int column; QString info; }; bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2); /** * @brief A class containing error data for one error. * * The paths are stored with internal ("/") separators. Only when we show the * path or copy if for user (to clipboard) we convert to native separators. * Full path is stored instead of relative path for flexibility. It is easy * to get the relative path from full path when needed. */ class ErrorItem { public: ErrorItem(); explicit ErrorItem(const ErrorLogger::ErrorMessage &errmsg); /** * @brief Convert error item to string. * @return Error item as string. */ QString ToString() const; QString tool() const; QString file0; QString errorId; Severity::SeverityType severity; bool inconclusive; QString summary; QString message; int cwe; QList errorPath; QString symbolNames; // Special GUI properties QString sinceDate; QString tags; /** * Compare "CID" */ static bool sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2); }; Q_DECLARE_METATYPE(ErrorItem) /** * @brief A class containing error data for one shown error line. */ class ErrorLine { public: QString file; unsigned int line; QString file0; QString errorId; bool inconclusive; Severity::SeverityType severity; QString summary; QString message; QString sinceDate; QString tags; }; /// @} #endif // ERRORITEM_H cppcheck-1.90/gui/file.ui000066400000000000000000000030171357737443600153110ustar00rootroot00000000000000 Fileview 0 0 400 300 Fileview true Qt::Horizontal QDialogButtonBox::Close mButtons accepted() Fileview accept() 248 254 157 274 mButtons rejected() Fileview reject() 316 260 286 274 cppcheck-1.90/gui/filelist.cpp000066400000000000000000000073131357737443600163550ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "filelist.h" #include "path.h" #include "pathmatch.h" QStringList FileList::getDefaultFilters() { QStringList extensions; extensions << "*.cpp" << "*.cxx" << "*.cc" << "*.c" << "*.c++" << "*.txx" << "*.tpp"; return extensions; } bool FileList::filterMatches(const QFileInfo &inf) { if (inf.isFile()) { const QStringList filters = FileList::getDefaultFilters(); QString ext("*."); ext += inf.suffix(); if (filters.contains(ext, Qt::CaseInsensitive)) return true; } return false; } void FileList::addFile(const QString &filepath) { QFileInfo inf(filepath); if (filterMatches(inf)) mFileList << inf; } void FileList::addDirectory(const QString &directory, bool recursive) { QDir dir(directory); dir.setSorting(QDir::Name); const QStringList filters = FileList::getDefaultFilters(); const QStringList origNameFilters = dir.nameFilters(); dir.setNameFilters(filters); if (!recursive) { dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); QFileInfoList items = dir.entryInfoList(); mFileList += items; } else { dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); QFileInfoList items = dir.entryInfoList(); mFileList += items; dir.setNameFilters(origNameFilters); dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList list = dir.entryInfoList(); QFileInfo item; foreach (item, list) { const QString path = item.canonicalFilePath(); addDirectory(path, recursive); } } } void FileList::addPathList(const QStringList &paths) { QString path; foreach (path, paths) { QFileInfo inf(path); if (inf.isFile()) addFile(path); else addDirectory(path, true); } } QStringList FileList::getFileList() const { if (mExcludedPaths.empty()) { QStringList names; foreach (QFileInfo item, mFileList) { QString name = QDir::fromNativeSeparators(item.filePath()); names << name; } return names; } else { return applyExcludeList(); } } void FileList::addExcludeList(const QStringList &paths) { mExcludedPaths = paths; } static std::vector toStdStringList(const QStringList &stringList) { std::vector ret; foreach (const QString &s, stringList) { ret.push_back(s.toStdString()); } return ret; } QStringList FileList::applyExcludeList() const { #ifdef _WIN32 const PathMatch pathMatch(toStdStringList(mExcludedPaths), true); #else const PathMatch pathMatch(toStdStringList(mExcludedPaths), false); #endif QStringList paths; foreach (QFileInfo item, mFileList) { QString name = QDir::fromNativeSeparators(item.canonicalFilePath()); if (!pathMatch.match(name.toStdString())) paths << name; } return paths; } cppcheck-1.90/gui/filelist.h000066400000000000000000000061211357737443600160160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILELIST_H #define FILELIST_H #include #include /** * @brief A class for listing files and directories to check. * This class creates a list of files to check. If directory name is given then * all files in the directory matching the filter will be added. The directory * can be also added recursively when all files in subdirectories are added too. * The filenames are matched against the filter and only those files whose * filename extension is included in the filter list are added. * * This class also handles filtering of paths against ignore filters given. If * there is ignore filters then only paths not matching those filters are * returned. */ class FileList { public: /** * @brief Add filename to the list. * @param filepath Full path to the file. */ void addFile(const QString &filepath); /** * @brief Add files in the directory to the list. * @param directory Full pathname to directory to add. * @param recursive If true also files in subdirectories are added. */ void addDirectory(const QString &directory, bool recursive = false); /** * @brief Add list of filenames and directories to the list. * @param paths List of paths to add. */ void addPathList(const QStringList &paths); /** * @brief Return list of filenames (to check). * @return list of filenames to check. */ QStringList getFileList() const; /** * @brief Add list of paths to exclusion list. * @param paths Paths to exclude. */ void addExcludeList(const QStringList &paths); /** * @brief Return list of default filename extensions included. * @return list of default filename extensions included. */ static QStringList getDefaultFilters(); protected: /** * @brief Test if filename matches the filename extensions filtering. * @return true if filename matches filtering. */ static bool filterMatches(const QFileInfo &inf); /** * @brief Get filtered list of paths. * This method takes the list of paths and applies the exclude lists to * it. And then returns the list of paths that did not match the * exclude filters. * @return Filtered list of paths. */ QStringList applyExcludeList() const; private: QFileInfoList mFileList; QStringList mExcludedPaths; }; #endif // FILELIST_H cppcheck-1.90/gui/fileviewdialog.cpp000066400000000000000000000042131357737443600175300ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "fileviewdialog.h" FileViewDialog::FileViewDialog(const QString &file, const QString &title, QWidget *parent) : QDialog(parent) { mUI.setupUi(this); setWindowTitle(title); connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(accept())); loadTextFile(file, mUI.mText); } void FileViewDialog::loadTextFile(const QString &filename, QTextEdit *edit) { QFile file(filename); if (!file.exists()) { QString msg(tr("Could not find the file: %1")); msg = msg.arg(filename); QMessageBox msgbox(QMessageBox::Critical, tr("Cppcheck"), msg, QMessageBox::Ok, this); msgbox.exec(); return; } file.open(QIODevice::ReadOnly | QIODevice::Text); if (!file.isReadable()) { QString msg(tr("Could not read the file: %1")); msg = msg.arg(filename); QMessageBox msgbox(QMessageBox::Critical, tr("Cppcheck"), msg, QMessageBox::Ok, this); msgbox.exec(); return; } QByteArray filedata = file.readAll(); file.close(); edit->setPlainText(filedata); } cppcheck-1.90/gui/fileviewdialog.h000066400000000000000000000030261357737443600171760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef FILEVIEW_DIALOG_H #define FILEVIEW_DIALOG_H #include #include #include "ui_file.h" class QWidget; class QTextEdit; /// @addtogroup GUI /// @{ /** * @brief File view -dialog. * This dialog shows text files. It is used for showing the license file and * the authors list. * */ class FileViewDialog : public QDialog { Q_OBJECT public: FileViewDialog(const QString &file, const QString &title, QWidget *parent = nullptr); protected: /** * @brief Load text file contents to edit control. * * @param filename File to load. * @param edit Control where to load the file contents. */ void loadTextFile(const QString &filename, QTextEdit *edit); Ui::Fileview mUI; }; /// @} #endif // FILEVIEW_DIALOG_H cppcheck-1.90/gui/gui.cppcheck000066400000000000000000000004441357737443600163220ustar00rootroot00000000000000 cppcheck-1.90/gui/gui.pro000066400000000000000000000111721357737443600153420ustar00rootroot00000000000000lessThan(QT_MAJOR_VERSION, 5): error(requires >= Qt 5 (You used: $$QT_VERSION)) TEMPLATE = app TARGET = cppcheck-gui CONFIG += warn_on debug DEPENDPATH += . \ ../lib INCLUDEPATH += . \ ../lib QT += widgets QT += printsupport contains(LINKCORE, [yY][eE][sS]) { LIBS += -l../bin/cppcheck-core DEFINES += CPPCHECKLIB_IMPORT } LIBS += -L$$PWD/../externals DESTDIR = . RCC_DIR = temp MOC_DIR = temp OBJECTS_DIR = temp UI_DIR = temp isEmpty(QMAKE_CXX) { isEmpty(CXX)) { QMAKE_CXX = gcc } else { QMAKE_CXX = $$(CXX) } } win32 { CONFIG += windows contains(LINKCORE, [yY][eE][sS]) { DESTDIR = ../bin RCC_DIR = temp/generated MOC_DIR = temp/generated OBJECTS_DIR = temp/generated UI_DIR = temp/generated } else { DESTDIR = ../Build/gui RCC_DIR = ../BuildTmp/gui MOC_DIR = ../BuildTmp/gui OBJECTS_DIR = ../BuildTmp/gui UI_DIR = ../BuildTmp/gui } } RESOURCES = gui.qrc FORMS = about.ui \ application.ui \ file.ui \ mainwindow.ui \ projectfiledialog.ui \ resultsview.ui \ scratchpad.ui \ settings.ui \ stats.ui \ librarydialog.ui \ libraryaddfunctiondialog.ui \ libraryeditargdialog.ui \ newsuppressiondialog.ui TRANSLATIONS = cppcheck_de.ts \ cppcheck_es.ts \ cppcheck_fi.ts \ cppcheck_fr.ts \ cppcheck_it.ts \ cppcheck_ja.ts \ cppcheck_ko.ts \ cppcheck_nl.ts \ cppcheck_ru.ts \ cppcheck_sr.ts \ cppcheck_sv.ts \ cppcheck_zh_CN.ts # Windows-specific options CONFIG += embed_manifest_exe contains(LINKCORE, [yY][eE][sS]) { } else { BASEPATH = ../lib/ include($$PWD/../lib/lib.pri) } HEADERS += aboutdialog.h \ application.h \ applicationdialog.h \ applicationlist.h \ checkstatistics.h \ checkthread.h \ codeeditstylecontrols.h \ codeeditorstyle.h \ codeeditstyledialog.h \ codeeditor.h \ common.h \ csvreport.h \ erroritem.h \ filelist.h \ fileviewdialog.h \ mainwindow.h \ platforms.h \ printablereport.h \ projectfile.h \ projectfiledialog.h \ report.h \ resultstree.h \ resultsview.h \ scratchpad.h \ settingsdialog.h \ showtypes.h \ statsdialog.h \ threadhandler.h \ threadresult.h \ translationhandler.h \ txtreport.h \ xmlreport.h \ xmlreportv2.h \ librarydialog.h \ cppchecklibrarydata.h \ libraryaddfunctiondialog.h \ libraryeditargdialog.h \ newsuppressiondialog.h SOURCES += aboutdialog.cpp \ application.cpp \ applicationdialog.cpp \ applicationlist.cpp \ checkstatistics.cpp \ checkthread.cpp \ codeeditorstyle.cpp \ codeeditstylecontrols.cpp \ codeeditstyledialog.cpp \ codeeditor.cpp \ common.cpp \ csvreport.cpp \ erroritem.cpp \ filelist.cpp \ fileviewdialog.cpp \ main.cpp \ mainwindow.cpp\ platforms.cpp \ printablereport.cpp \ projectfile.cpp \ projectfiledialog.cpp \ report.cpp \ resultstree.cpp \ resultsview.cpp \ scratchpad.cpp \ settingsdialog.cpp \ showtypes.cpp \ statsdialog.cpp \ threadhandler.cpp \ threadresult.cpp \ translationhandler.cpp \ txtreport.cpp \ xmlreport.cpp \ xmlreportv2.cpp \ librarydialog.cpp \ cppchecklibrarydata.cpp \ libraryaddfunctiondialog.cpp \ libraryeditargdialog.cpp \ newsuppressiondialog.cpp win32 { RC_FILE = cppcheck-gui.rc HEADERS += ../lib/version.h contains(LINKCORE, [yY][eE][sS]) { } else { LIBS += -lshlwapi } } contains(QMAKE_CC, gcc) { QMAKE_CXXFLAGS += -std=c++11 -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-deprecated-declarations } contains(QMAKE_CXX, clang++) { QMAKE_CXXFLAGS += -std=c++11 } contains(HAVE_QCHART, [yY][eE][sS]) { QT += charts DEFINES += HAVE_QCHART } else { message("Charts disabled - to enable it pass HAVE_QCHART=yes to qmake.") } cppcheck-1.90/gui/gui.qrc000066400000000000000000000025031357737443600153250ustar00rootroot00000000000000 cppcheck-gui.png images/dialog-error.png images/dialog-information.png images/dialog-warning.png images/edit-clear.png images/go-down.png images/help-browser.png images/media-floppy.png images/preferences-system.png images/process-stop.png images/text-x-generic.png images/view-recheck.png images/view-refresh.png images/showerrors.png images/showstylewarnings.png images/openproject.png images/scratchpad.png images/showwarnings.png images/showperformance.png images/utilities-system-monitor.png ../COPYING ../AUTHORS images/go-home.png images/go-next.png images/go-previous.png images/applications-development.png images/applications-system.png images/llvm-dragon.png cppcheck-1.90/gui/help/000077500000000000000000000000001357737443600147625ustar00rootroot00000000000000cppcheck-1.90/gui/help/buildhelp.bat000066400000000000000000000002741357737443600174250ustar00rootroot00000000000000@echo off pushd %~dp0 if exist online-help.qhc del online-help.qhc if exist online-help.qch del online-help.qch qcollectiongenerator online-help.qhcp -o online-help.qhc popd cppcheck-1.90/gui/help/manual.html000066400000000000000000000410031357737443600171230ustar00rootroot00000000000000 Cppcheck 1.46

Chapter 1. Introduction

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

Supported code and platforms:

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

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

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

Accuracy

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

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


Chapter 2. Getting started

2.1. First test

Here is a simple code

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

If you save that into file1.c and execute:

cppcheck file1.c

The output from cppcheck will then be:

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

2.2. Checking all files in a folder

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

cppcheck path

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

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

2.3. Excluding a file or folder from checking

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

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

cppcheck src/a src/b

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

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


2.4. Severities

The possible severities for messages are:

error

used when bugs are found

warning

suggestions about defensive programming to prevent bugs

style

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

performance

suggestions for making the code faster


2.5. Enable messages

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


2.5.1. Stylistic issues

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

Here is a simple code example:

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

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

cppcheck --enable=style file3.c

The output from Cppcheck is now:

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


2.5.2. Unused functions

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

cppcheck --enable=unusedFunction path

2.5.3. Enable all checks

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

cppcheck --enable=all path

2.6. Saving results in file

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

cppcheck file1.c 2> err.txt

2.7. Multithreaded checking

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

cppcheck -j 4 path

Chapter 3. Preprocessor configurations

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

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

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

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

cppcheck -DDEBUG=1 -D__cplusplus path

Chapter 4. XML output

Cppcheck can generate the output in XML format.

Use the --xml flag when you execute cppcheck:

cppcheck --xml file1.cpp

The xml format is:

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

Attributes:

file

filename. Both relative and absolute paths are possible

line

a number

id

id of error. These are always valid symbolnames.

severity

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

msg

the error message


Chapter 5. Reformatting the output

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

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

cppcheck --template vs gui/test.cpp

This output will look like this:

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

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

cppcheck --template gcc gui/test.cpp

The output will look like this:

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

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

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

The output will look like this:

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


Chapter 6. Suppressions

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

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

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

Here is an example:

memleak:file1.cpp
exceptNew:file1.cpp
uninitvar

You can then use the suppressions file:

cppcheck --suppressions suppressions.txt src/


Chapter 7. Exception safety

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

Example:

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

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

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

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

The output will be:

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

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

Here is another example:

int *p;

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

Check that with Cppcheck:

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

The output from Cppcheck is:

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

Chapter 8. Html report

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

This command generates the help screen:

htmlreport/cppcheck-htmlreport -h

The output screen says:

Usage: cppcheck-htmlreport [options]

Options:
  -h, --help      show this help message and exit
  --file=FILE     The cppcheck xml output file to read defects from.
                  Default is reading from stdin.
  --report-dir=REPORT_DIR
                  The directory where the html report content is written.
  --source-dir=SOURCE_DIR
                  Base directory where source code files can be found.

An example usage:

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

Chapter 9. Graphical user interface

9.1. Introduction

A Cppcheck GUI is available.

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


9.2. Check source code

Use the Check menu.


9.3. Inspecting results

The results are shown in a list.

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

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


9.4. Settings

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

More settings are available in Edit>Preferences.


9.5. Project files

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

  • include folders

  • preprocessor defines

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

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

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

%1 %2

\n" "

%3 : %4

\n" "

%5 : %6

\n" "

%7 : %8

\n" "

%9 : %10

\n" "

%11 : %12

\n" "

%13 : %14

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

%1

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

%1

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

%1

\n" " %2:%3\n" " %4:%5\n" " %6:%7\n" " %8:%9\n" " %10:%11\n" " %12:%13\n" "\n" ) .arg(stats) .arg(errors) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowErrors)) .arg(warnings) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowWarnings)) .arg(style) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowStyle)) .arg(portability) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPortability)) .arg(performance) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPerformance)) .arg(information) .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowInformation)); const QString htmlSummary = htmlSettings + htmlPrevious + htmlStatistics; QMimeData *mimeData = new QMimeData(); mimeData->setText(textSummary); mimeData->setHtml(htmlSummary); clipboard->setMimeData(mimeData); } void StatsDialog::setStatistics(const CheckStatistics *stats) { mStatistics = stats; mUI.mLblErrors->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowErrors))); mUI.mLblWarnings->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings))); mUI.mLblStyle->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowStyle))); mUI.mLblPortability->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPortability))); mUI.mLblPerformance->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance))); mUI.mLblInformation->setText(QString("%1").arg(stats->getCount(CPPCHECK,ShowTypes::ShowInformation))); } #ifdef HAVE_QCHART QChartView *StatsDialog::createChart(const QString &statsFile, const QString &tool) { QChart *chart = new QChart; chart->addSeries(numberOfReports(statsFile, tool + "-error")); chart->addSeries(numberOfReports(statsFile, tool + "-warning")); chart->addSeries(numberOfReports(statsFile, tool + "-style")); chart->addSeries(numberOfReports(statsFile, tool + "-performance")); chart->addSeries(numberOfReports(statsFile, tool + "-portability")); QDateTimeAxis *axisX = new QDateTimeAxis; axisX->setTitleText("Date"); chart->addAxis(axisX, Qt::AlignBottom); foreach (QAbstractSeries *s, chart->series()) { s->attachAxis(axisX); } QValueAxis *axisY = new QValueAxis; axisY->setLabelFormat("%i"); axisY->setTitleText("Count"); chart->addAxis(axisY, Qt::AlignLeft); qreal maxY = 0; foreach (QAbstractSeries *s, chart->series()) { s->attachAxis(axisY); if (QLineSeries *ls = dynamic_cast(s)) { foreach (QPointF p, ls->points()) { if (p.y() > maxY) maxY = p.y(); } } } axisY->setMax(maxY); //chart->createDefaultAxes(); chart->setTitle(tool); QChartView *chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); return chartView; } QLineSeries *StatsDialog::numberOfReports(const QString &fileName, const QString &severity) const { QLineSeries *series = new QLineSeries(); series->setName(severity); QFile f(fileName); if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { quint64 t = 0; QTextStream in(&f); while (!in.atEnd()) { QString line = in.readLine(); QRegExp rxdate("\\[(\\d\\d)\\.(\\d\\d)\\.(\\d\\d\\d\\d)\\]"); if (rxdate.exactMatch(line)) { int y = rxdate.cap(3).toInt(); int m = rxdate.cap(2).toInt(); int d = rxdate.cap(1).toInt(); QDateTime dt; dt.setDate(QDate(y,m,d)); if (t == dt.toMSecsSinceEpoch()) t += 1000; else t = dt.toMSecsSinceEpoch(); } if (line.startsWith(severity + ':')) { int y = line.mid(1+severity.length()).toInt(); series->append(t, y); } } } return series; } #endif cppcheck-1.90/gui/statsdialog.h000066400000000000000000000041541357737443600165250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef STATSDIALOG_H #define STATSDIALOG_H #include #ifdef HAVE_QCHART #include #endif #include "ui_stats.h" class ProjectFile; class CheckStatistics; /// @addtogroup GUI /// @{ /** * @brief A dialog that shows project and scan statistics. * */ class StatsDialog : public QDialog { Q_OBJECT public: explicit StatsDialog(QWidget *parent = nullptr); /** * @brief Sets the project to extract statistics from */ void setProject(const ProjectFile *projectFile); /** * @brief Sets the string to display beside "Path Selected:" */ void setPathSelected(const QString& path); /** * @brief Sets the number to display beside "Number of Files Scanned:" */ void setNumberOfFilesScanned(int num); /** * @brief Sets the number of seconds to display beside "Scan Duration:" */ void setScanDuration(double seconds); /** * @brief Sets the numbers of different error/warnings found." */ void setStatistics(const CheckStatistics *stats); private slots: void copyToClipboard(); void pdfExport(); #ifdef HAVE_QCHART QChartView *createChart(const QString &statsFile, const QString &tool); QLineSeries *numberOfReports(const QString &fileName, const QString &severity) const; #endif private: Ui::StatsDialog mUI; const CheckStatistics *mStatistics; }; /// @} #endif // STATSDIALOG_H cppcheck-1.90/gui/test/000077500000000000000000000000001357737443600150115ustar00rootroot00000000000000cppcheck-1.90/gui/test/benchmark/000077500000000000000000000000001357737443600167435ustar00rootroot00000000000000cppcheck-1.90/gui/test/benchmark/benchmark.pro000066400000000000000000000000701357737443600214140ustar00rootroot00000000000000CONFIG += ordered TEMPLATE = subdirs SUBDIRS = simple cppcheck-1.90/gui/test/benchmark/simple/000077500000000000000000000000001357737443600202345ustar00rootroot00000000000000cppcheck-1.90/gui/test/benchmark/simple/benchmarksimple.cpp000066400000000000000000000042621357737443600241100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "benchmarksimple.h" #include "tokenize.h" #include "token.h" #include "settings.h" #include "errorlogger.h" void BenchmarkSimple::tokenize() { QFile file(QString(SRCDIR) + "/../../data/benchmark/simple.cpp"); QByteArray data = file.readAll(); Settings settings; settings.debugwarnings = true; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(data.constData()); QBENCHMARK { tokenizer.tokenize(istr, "test.cpp"); } } void BenchmarkSimple::simplify() { QFile file(QString(SRCDIR) + "/../../data/benchmark/simple.cpp"); QByteArray data = file.readAll(); Settings settings; settings.debugwarnings = true; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(data.constData()); tokenizer.tokenize(istr, "test.cpp"); QBENCHMARK { tokenizer.simplifyTokenList2(); } } void BenchmarkSimple::tokenizeAndSimplify() { QFile file(QString(SRCDIR) + "/../../data/benchmark/simple.cpp"); QByteArray data = file.readAll(); Settings settings; settings.debugwarnings = true; // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(data.constData()); QBENCHMARK { tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); } } QTEST_MAIN(BenchmarkSimple) cppcheck-1.90/gui/test/benchmark/simple/benchmarksimple.h000066400000000000000000000023421357737443600235520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "errorlogger.h" class BenchmarkSimple: public QObject, public ErrorLogger { Q_OBJECT private slots: void tokenize(); void simplify(); void tokenizeAndSimplify(); private: // Empty implementations of ErrorLogger methods. // We don't care about the output in the benchmark tests. void reportOut(const std::string & outmsg) override { } void reportErr(const ErrorLogger::ErrorMessage &msg) override { } }; cppcheck-1.90/gui/test/benchmark/simple/simple.pro000066400000000000000000000003741357737443600222530ustar00rootroot00000000000000TEMPLATE = app TARGET = benchmark-simple DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../../build MOC_DIR = ../../build include(../../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += benchmarksimple.cpp HEADERS += benchmarksimple.h cppcheck-1.90/gui/test/common.pri000066400000000000000000000003021357737443600170100ustar00rootroot00000000000000QT += testlib INCLUDEPATH += $${PWD}/.. \ $${PWD}/../../lib contains(QMAKE_CC, gcc) { QMAKE_CXXFLAGS += -std=c++11 } contains(QMAKE_CXX, clang++) { QMAKE_CXXFLAGS += -std=c++11 } cppcheck-1.90/gui/test/data/000077500000000000000000000000001357737443600157225ustar00rootroot00000000000000cppcheck-1.90/gui/test/data/benchmark/000077500000000000000000000000001357737443600176545ustar00rootroot00000000000000cppcheck-1.90/gui/test/data/benchmark/simple.cpp000066400000000000000000004105201357737443600216530ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Remove includes from the benchmark run // Included files aren't found anyway //#include "checkother.h" //#include "mathlib.h" //#include "symboldatabase.h" //#include // std::isupper //#include // fabs() //#include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckOther instance; } //--------------------------------------------------------------------------- void CheckOther::checkIncrementBoolean() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%var% ++")) { if (tok->varId()) { const Token *declTok = Token::findmatch(_tokenizer->tokens(), "bool %varid%", tok->varId()); if (declTok) incrementBooleanError(tok); } } } } void CheckOther::incrementBooleanError(const Token *tok) { reportError( tok, Severity::style, "incrementboolean", "The use of a variable of type bool with the ++ postfix operator is always true and deprecated by the C++ Standard.\n" "The operand of a postfix increment operator may be of type bool but it is deprecated by C++ Standard (Annex D-1) and the operand is always set to true\n" ); } //--------------------------------------------------------------------------- void CheckOther::clarifyCalculation() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->strAt(1) == "?") { // condition const Token *cond = tok; if (cond->isName() || cond->isNumber()) cond = cond->previous(); else if (cond->str() == ")") cond = cond->link()->previous(); else continue; // calculation if (!cond->isArithmeticalOp()) continue; const std::string &op = cond->str(); cond = cond->previous(); // skip previous multiplications.. while (cond && cond->strAt(-1) == "*" && (cond->isName() || cond->isNumber())) cond = cond->tokAt(-2); if (!cond) continue; // first multiplication operand if (cond->str() == ")") { clarifyCalculationError(cond, op); } else if (cond->isName() || cond->isNumber()) { if (Token::Match(cond->previous(),("return|=|+|-|,|(|"+op).c_str())) clarifyCalculationError(cond, op); } } } } void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) { // suspicious calculation const std::string calc("'a" + op + "b?c:d'"); // recommended calculation #1 const std::string s1("'(a" + op + "b)?c:d'"); // recommended calculation #2 const std::string s2("'a" + op + "(b?c:d)'"); reportError(tok, Severity::style, "clarifyCalculation", "Clarify calculation precedence for " + op + " and ?\n" "Suspicious calculation. Please use parentheses to clarify the code. " "The code " + calc + " should be written as either " + s1 + " or " + s2 + "."); } // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))' void CheckOther::clarifyCondition() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "( %var% =")) { for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); else if (Token::Match(tok2, "&&|%oror%|?|)")) break; else if (Token::Match(tok2, "<|<=|==|!=|>|>= %num% )")) { clarifyConditionError(tok); break; } } } } } void CheckOther::clarifyConditionError(const Token *tok) { reportError(tok, Severity::style, "clarifyCondition", "Suspicious condition (assignment+comparison), it can be clarified with parentheses"); } void CheckOther::warningOldStylePointerCast() { if (!_settings->isEnabled("style") || (_tokenizer->tokens() && _tokenizer->fileLine(_tokenizer->tokens()).find(".cpp") == std::string::npos)) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Old style pointer casting.. if (!Token::Match(tok, "( const| %type% * ) %var%") && !Token::Match(tok, "( const| %type% * ) (| new")) continue; int addToIndex = 0; if (tok->tokAt(1)->str() == "const") addToIndex = 1; if (tok->tokAt(4 + addToIndex)->str() == "const") continue; // Is "type" a class? const std::string pattern("class " + tok->tokAt(1 + addToIndex)->str()); if (!Token::findmatch(_tokenizer->tokens(), pattern.c_str())) continue; cstyleCastError(tok); } } //--------------------------------------------------------------------------- // fflush(stdin) <- fflush only applies to output streams in ANSI C //--------------------------------------------------------------------------- void CheckOther::checkFflushOnInputStream() { const Token *tok = _tokenizer->tokens(); while (tok && ((tok = Token::findsimplematch(tok, "fflush ( stdin )")) != NULL)) { fflushOnInputStreamError(tok, tok->strAt(2)); tok = tok->tokAt(4); } } void CheckOther::checkSizeofForNumericParameter() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %num% )") || Token::Match(tok, "sizeof ( - %num% )") || Token::Match(tok, "sizeof %num%") || Token::Match(tok, "sizeof - %num%") ) { sizeofForNumericParameterError(tok); } } } void CheckOther::checkSizeofForArrayParameter() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %var% )") || Token::Match(tok, "sizeof %var%")) { int tokIdx = 1; if (tok->tokAt(tokIdx)->str() == "(") { ++tokIdx; } if (tok->tokAt(tokIdx)->varId() > 0) { const Token *declTok = Token::findmatch(_tokenizer->tokens(), "%varid%", tok->tokAt(tokIdx)->varId()); if (declTok) { if (Token::simpleMatch(declTok->next(), "[")) { declTok = declTok->next()->link(); // multidimensional array while (Token::simpleMatch(declTok->next(), "[")) { declTok = declTok->next()->link(); } if (!(Token::Match(declTok->next(), "= %str%")) && !(Token::simpleMatch(declTok->next(), "= {")) && !(Token::simpleMatch(declTok->next(), ";"))) { if (Token::simpleMatch(declTok->next(), ",")) { declTok = declTok->next(); while (!Token::simpleMatch(declTok, ";")) { if (Token::simpleMatch(declTok, ")")) { sizeofForArrayParameterError(tok); break; } if (Token::Match(declTok, "(|[|{")) { declTok = declTok->link(); } declTok = declTok->next(); } } } if (Token::simpleMatch(declTok->next(), ")")) { sizeofForArrayParameterError(tok); } } } } } } } //--------------------------------------------------------------------------- // switch (x) // { // case 2: // y = a; // <- this assignment is redundant // case 3: // y = b; // <- case 2 falls through and sets y twice // } //--------------------------------------------------------------------------- void CheckOther::checkRedundantAssignmentInSwitch() { const char switchPattern[] = "switch ( %any% ) { case"; const char breakPattern[] = "break|continue|return|exit|goto|throw"; const char functionPattern[] = "%var% ("; // Find the beginning of a switch. E.g.: // switch (var) { ... const Token *tok = Token::findmatch(_tokenizer->tokens(), switchPattern); while (tok) { // Check the contents of the switch statement std::map varsAssigned; int indentLevel = 0; for (const Token *tok2 = tok->tokAt(5); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.: // case 3: b = 1; // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) { const Token* endOfConditional = tok2->link(); for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) { if (tok3->varId() != 0) varsAssigned.erase(tok3->varId()); else if (Token::Match(tok3, functionPattern) || Token::Match(tok3, breakPattern)) varsAssigned.clear(); } tok2 = endOfConditional; } else ++ indentLevel; } else if (tok2->str() == "}") { -- indentLevel; // End of the switch block if (indentLevel < 0) break; } // Variable assignment. Report an error if it's assigned to twice before a break. E.g.: // case 3: b = 1; // <== redundant // case 4: b = 2; if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;") && tok2->varId() != 0) { std::map::iterator i = varsAssigned.find(tok2->varId()); if (i == varsAssigned.end()) varsAssigned[tok2->varId()] = tok2; else redundantAssignmentInSwitchError(i->second, i->second->str()); } // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.: // case 3: b = 1; // case 4: b++; else if (tok2->varId() != 0) varsAssigned.erase(tok2->varId()); // Reset our record of assignments if there is a break or function call. E.g.: // case 3: b = 1; break; if (Token::Match(tok2, functionPattern) || Token::Match(tok2, breakPattern)) varsAssigned.clear(); } tok = Token::findmatch(tok->next(), switchPattern); } } void CheckOther::checkSwitchCaseFallThrough() { if (!(_settings->isEnabled("style") && _settings->experimental)) return; const char switchPattern[] = "switch ("; const char breakPattern[] = "break|continue|return|exit|goto|throw"; // Find the beginning of a switch. E.g.: // switch (var) { ... const Token *tok = Token::findmatch(_tokenizer->tokens(), switchPattern); while (tok) { // Check the contents of the switch statement std::stack > ifnest; std::stack loopnest; std::stack scopenest; bool justbreak = true; bool firstcase = true; for (const Token *tok2 = tok->tokAt(1)->link()->tokAt(2); tok2; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "if (")) { tok2 = tok2->tokAt(1)->link()->next(); if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched if in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } ifnest.push(std::make_pair(tok2->link(), false)); justbreak = false; } else if (Token::simpleMatch(tok2, "while (")) { tok2 = tok2->tokAt(1)->link()->next(); // skip over "do { } while ( ) ;" case if (tok2->str() == "{") { if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched while in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); } justbreak = false; } else if (Token::simpleMatch(tok2, "do {")) { tok2 = tok2->tokAt(1); if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched do in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); justbreak = false; } else if (Token::simpleMatch(tok2, "for (")) { tok2 = tok2->tokAt(1)->link()->next(); if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched for in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } loopnest.push(tok2->link()); justbreak = false; } else if (Token::Match(tok2, switchPattern)) { // skip over nested switch, we'll come to that soon tok2 = tok2->tokAt(1)->link()->next()->link(); } else if (Token::Match(tok2, breakPattern)) { if (loopnest.empty()) { justbreak = true; } tok2 = Token::findsimplematch(tok2, ";"); } else if (Token::Match(tok2, "case|default")) { if (!justbreak && !firstcase) { switchCaseFallThrough(tok2); } tok2 = Token::findsimplematch(tok2, ":"); justbreak = true; firstcase = false; } else if (tok2->str() == "{") { scopenest.push(tok2->link()); } else if (tok2->str() == "}") { if (!ifnest.empty() && tok2 == ifnest.top().first) { if (tok2->next()->str() == "else") { tok2 = tok2->tokAt(2); ifnest.pop(); if (tok2->link() == NULL) { std::ostringstream errmsg; errmsg << "unmatched if in switch: " << tok2->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); break; } ifnest.push(std::make_pair(tok2->link(), justbreak)); justbreak = false; } else { justbreak &= ifnest.top().second; ifnest.pop(); } } else if (!loopnest.empty() && tok2 == loopnest.top()) { loopnest.pop(); } else if (!scopenest.empty() && tok2 == scopenest.top()) { scopenest.pop(); } else { if (!ifnest.empty() || !loopnest.empty() || !scopenest.empty()) { std::ostringstream errmsg; errmsg << "unexpected end of switch: "; errmsg << "ifnest=" << ifnest.size(); if (!ifnest.empty()) errmsg << "," << ifnest.top().first->linenr(); errmsg << ", loopnest=" << loopnest.size(); if (!loopnest.empty()) errmsg << "," << loopnest.top()->linenr(); errmsg << ", scopenest=" << scopenest.size(); if (!scopenest.empty()) errmsg << "," << scopenest.top()->linenr(); reportError(_tokenizer->tokens(), Severity::debug, "debug", errmsg.str()); } // end of switch block break; } } else if (tok2->str() != ";") { justbreak = false; } } tok = Token::findmatch(tok->next(), switchPattern); } } //--------------------------------------------------------------------------- // int x = 1; // x = x; // <- redundant assignment to self // // int y = y; // <- redundant initialization to self //--------------------------------------------------------------------------- void CheckOther::checkSelfAssignment() { if (!_settings->isEnabled("style")) return; // POD variables.. std::set pod; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->isStandardType() && Token::Match(tok->tokAt(2), "[,);]") && tok->next()->varId()) pod.insert(tok->next()->varId()); } const char selfAssignmentPattern[] = "%var% = %var% ;|=|)"; const Token *tok = Token::findmatch(_tokenizer->tokens(), selfAssignmentPattern); while (tok) { if (Token::Match(tok->previous(), "[;{}]") && tok->varId() && tok->varId() == tok->tokAt(2)->varId() && pod.find(tok->varId()) != pod.end()) { selfAssignmentError(tok, tok->str()); } tok = Token::findmatch(tok->next(), selfAssignmentPattern); } } //--------------------------------------------------------------------------- // int a = 1; // assert(a = 2); // <- assert should not have a side-effect //--------------------------------------------------------------------------- void CheckOther::checkAssignmentInAssert() { if (!_settings->isEnabled("style")) return; const char assertPattern[] = "assert ( %any%"; const Token *tok = Token::findmatch(_tokenizer->tokens(), assertPattern); const Token *endTok = tok ? tok->next()->link() : NULL; while (tok && endTok) { const Token* varTok = Token::findmatch(tok->tokAt(2), "%var% --|++|+=|-=|*=|/=|&=|^=|=", endTok); if (varTok) { assignmentInAssertError(tok, varTok->str()); } else if (NULL != (varTok = Token::findmatch(tok->tokAt(2), "--|++ %var%", endTok))) { assignmentInAssertError(tok, varTok->strAt(1)); } tok = Token::findmatch(endTok->next(), assertPattern); endTok = tok ? tok->next()->link() : NULL; } } //--------------------------------------------------------------------------- // if ((x != 1) || (x != 3)) // <- always true // if ((x == 1) && (x == 3)) // <- always false // if ((x < 1) && (x > 3)) // <- always false // if ((x > 3) || (x < 10)) // <- always true //--------------------------------------------------------------------------- void CheckOther::checkIncorrectLogicOperator() { if (!_settings->isEnabled("style")) return; const char conditionPattern[] = "if|while ("; const Token *tok = Token::findmatch(_tokenizer->tokens(), conditionPattern); const Token *endTok = tok ? tok->next()->link() : NULL; while (tok && endTok) { // Find a pair of OR'd terms, with or without parentheses // e.g. if (x != 3 || x != 4) const Token *logicTok = NULL, *term1Tok = NULL, *term2Tok = NULL; const Token *op1Tok = NULL, *op2Tok = NULL, *op3Tok = NULL, *nextTok = NULL; if (NULL != (logicTok = Token::findmatch(tok, "( %any% !=|==|<|>|>=|<= %any% ) &&|%oror% ( %any% !=|==|<|>|>=|<= %any% ) %any%", endTok))) { term1Tok = logicTok->next(); term2Tok = logicTok->tokAt(7); op1Tok = logicTok->tokAt(2); op2Tok = logicTok->tokAt(5); op3Tok = logicTok->tokAt(8); nextTok = logicTok->tokAt(11); } else if (NULL != (logicTok = Token::findmatch(tok, "%any% !=|==|<|>|>=|<= %any% &&|%oror% %any% !=|==|<|>|>=|<= %any% %any%", endTok))) { term1Tok = logicTok; term2Tok = logicTok->tokAt(4); op1Tok = logicTok->tokAt(1); op2Tok = logicTok->tokAt(3); op3Tok = logicTok->tokAt(5); nextTok = logicTok->tokAt(7); } if (logicTok) { // Find the common variable and the two different-valued constants unsigned int variableTested = 0; std::string firstConstant, secondConstant; bool varFirst1, varFirst2; unsigned int varId; if (Token::Match(term1Tok, "%var% %any% %num%")) { varId = term1Tok->varId(); if (!varId) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } varFirst1 = true; firstConstant = term1Tok->tokAt(2)->str(); } else if (Token::Match(term1Tok, "%num% %any% %var%")) { varId = term1Tok->tokAt(2)->varId(); if (!varId) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } varFirst1 = false; firstConstant = term1Tok->str(); } else { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } if (Token::Match(term2Tok, "%var% %any% %num%")) { const unsigned int varId2 = term2Tok->varId(); if (!varId2 || varId != varId2) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } varFirst2 = true; secondConstant = term2Tok->tokAt(2)->str(); variableTested = varId; } else if (Token::Match(term2Tok, "%num% %any% %var%")) { const unsigned int varId2 = term1Tok->tokAt(2)->varId(); if (!varId2 || varId != varId2) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } varFirst2 = false; secondConstant = term2Tok->str(); variableTested = varId; } else { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } if (variableTested == 0 || firstConstant.empty() || secondConstant.empty()) { tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; continue; } enum Position { First, Second, NA }; enum Relation { Equal, NotEqual, Less, LessEqual, More, MoreEqual }; struct Condition { const char *before; Position position1; const char *op1TokStr; const char *op2TokStr; Position position2; const char *op3TokStr; const char *after; Relation relation; bool state; } conditions[] = { { "!!&&", NA, "!=", "||", NA, "!=", "!!&&", NotEqual, true }, // (x != 1) || (x != 3) <- always true { "(", NA, "==", "&&", NA, "==", ")", NotEqual, false }, // (x == 1) && (x == 3) <- always false { "(", First, "<", "&&", First, ">", ")", LessEqual, false }, // (x < 1) && (x > 3) <- always false { "(", First, ">", "&&", First, "<", ")", MoreEqual, false }, // (x > 3) && (x < 1) <- always false { "(", Second, ">", "&&", First, ">", ")", LessEqual, false }, // (1 > x) && (x > 3) <- always false { "(", First, ">", "&&", Second, ">", ")", MoreEqual, false }, // (x > 3) && (1 > x) <- always false { "(", First, "<", "&&", Second, "<", ")", LessEqual, false }, // (x < 1) && (3 < x) <- always false { "(", Second, "<", "&&", First, "<", ")", MoreEqual, false }, // (3 < x) && (x < 1) <- always false { "(", Second, ">", "&&", Second, "<", ")", LessEqual, false }, // (1 > x) && (3 < x) <- always false { "(", Second, "<", "&&", Second, ">", ")", MoreEqual, false }, // (3 < x) && (1 > x) <- always false { "(", First, ">|>=", "||", First, "<|<=", ")", Less, true }, // (x > 3) || (x < 10) <- always true { "(", First, "<|<=", "||", First, ">|>=", ")", More, true }, // (x < 10) || (x > 3) <- always true { "(", Second, "<|<=", "||", First, "<|<=", ")", Less, true }, // (3 < x) || (x < 10) <- always true { "(", First, "<|<=", "||", Second, "<|<=", ")", More, true }, // (x < 10) || (3 < x) <- always true { "(", First, ">|>=", "||", Second, ">|>=", ")", Less, true }, // (x > 3) || (10 > x) <- always true { "(", Second, ">|>=", "||", First, ">|>=", ")", More, true }, // (10 > x) || (x > 3) <- always true { "(", Second, "<|<=", "||", Second, ">|<=", ")", Less, true }, // (3 < x) || (10 > x) <- always true { "(", Second, ">|>=", "||", Second, "<|<=", ")", More, true }, // (10 > x) || (3 < x) <- always true }; for (unsigned int i = 0; i < (sizeof(conditions) / sizeof(conditions[0])); i++) { if (!((conditions[i].position1 == NA) || (((conditions[i].position1 == First) && varFirst1) || ((conditions[i].position1 == Second) && !varFirst1)))) continue; if (!((conditions[i].position2 == NA) || (((conditions[i].position2 == First) && varFirst2) || ((conditions[i].position2 == Second) && !varFirst2)))) continue; if (!Token::Match(op1Tok, conditions[i].op1TokStr)) continue; if (!Token::Match(op2Tok, conditions[i].op2TokStr)) continue; if (!Token::Match(op3Tok, conditions[i].op3TokStr)) continue; if (!Token::Match(logicTok->previous(), conditions[i].before)) continue; if (!Token::Match(nextTok, conditions[i].after)) continue; if ((conditions[i].relation == Equal && MathLib::isEqual(firstConstant, secondConstant)) || (conditions[i].relation == NotEqual && MathLib::isNotEqual(firstConstant, secondConstant)) || (conditions[i].relation == Less && MathLib::isLess(firstConstant, secondConstant)) || (conditions[i].relation == LessEqual && MathLib::isLessEqual(firstConstant, secondConstant)) || (conditions[i].relation == More && MathLib::isGreater(firstConstant, secondConstant)) || (conditions[i].relation == MoreEqual && MathLib::isGreaterEqual(firstConstant, secondConstant))) incorrectLogicOperatorError(term1Tok, conditions[i].state); } } tok = Token::findmatch(endTok->next(), conditionPattern); endTok = tok ? tok->next()->link() : NULL; } } //--------------------------------------------------------------------------- // try {} catch (std::exception err) {} <- Should be "std::exception& err" //--------------------------------------------------------------------------- void CheckOther::checkCatchExceptionByValue() { if (!_settings->isEnabled("style")) return; const char catchPattern[] = "} catch ("; const Token *tok = Token::findmatch(_tokenizer->tokens(), catchPattern); const Token *endTok = tok ? tok->tokAt(2)->link() : NULL; while (tok && endTok) { // Find a pass-by-value declaration in the catch(), excluding basic types // e.g. catch (std::exception err) const Token *tokType = Token::findmatch(tok, "%type% %var% )", endTok); if (tokType && !tokType->isStandardType()) { catchExceptionByValueError(tokType); } tok = Token::findmatch(endTok->next(), catchPattern); endTok = tok ? tok->tokAt(2)->link() : NULL; } } //--------------------------------------------------------------------------- // strtol(str, 0, radix) <- radix must be 0 or 2-36 //--------------------------------------------------------------------------- void CheckOther::invalidFunctionUsage() { // strtol and strtoul.. for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "strtol|strtoul (")) continue; // Locate the third parameter of the function call.. int param = 1; for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ")") break; else if (tok2->str() == ",") { ++param; if (param == 3) { if (Token::Match(tok2, ", %num% )")) { const MathLib::bigint radix = MathLib::toLongNumber(tok2->next()->str()); if (!(radix == 0 || (radix >= 2 && radix <= 36))) { dangerousUsageStrtolError(tok2); } } break; } } } } // sprintf|snprintf overlapping data for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Get variable id of target buffer.. unsigned int varid = 0; if (Token::Match(tok, "sprintf|snprintf ( %var% ,")) varid = tok->tokAt(2)->varId(); else if (Token::Match(tok, "sprintf|snprintf ( %var% . %var% ,")) varid = tok->tokAt(4)->varId(); if (varid == 0) continue; // goto "," const Token *tok2 = tok->tokAt(3); while (tok2 && tok2->str() != ",") tok2 = tok2->next(); if (!tok2) continue; // is any source buffer overlapping the target buffer? int parlevel = 0; while ((tok2 = tok2->next()) != NULL) { if (tok2->str() == "(") ++parlevel; else if (tok2->str() == ")") { --parlevel; if (parlevel < 0) break; } else if (parlevel == 0 && Token::Match(tok2, ", %varid% [,)]", varid)) { sprintfOverlappingDataError(tok2->next(), tok2->next()->str()); break; } } } } //--------------------------------------------------------------------------- void CheckOther::invalidScanf() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { const Token *formatToken = 0; if (Token::Match(tok, "scanf|vscanf ( %str% ,")) formatToken = tok->tokAt(2); else if (Token::Match(tok, "fscanf|vfscanf ( %var% , %str% ,")) formatToken = tok->tokAt(4); else continue; bool format = false; // scan the string backwards, so we don't need to keep states const std::string &formatstr(formatToken->str()); for (unsigned int i = 1; i < formatstr.length(); i++) { if (formatstr[i] == '%') format = !format; else if (!format) continue; else if (std::isdigit(formatstr[i])) { format = false; } else if (std::isalpha(formatstr[i])) { invalidScanfError(tok); format = false; } } } } //--------------------------------------------------------------------------- // if (!x==3) <- Probably meant to be "x!=3" //--------------------------------------------------------------------------- void CheckOther::checkComparisonOfBoolWithInt() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "( ! %var% ==|!= %num% )")) { const Token *numTok = tok->tokAt(4); if (numTok && numTok->str() != "0") { comparisonOfBoolWithIntError(numTok, tok->strAt(2)); } } else if (Token::Match(tok, "( %num% ==|!= ! %var% )")) { const Token *numTok = tok->tokAt(1); if (numTok && numTok->str() != "0") { comparisonOfBoolWithIntError(numTok, tok->strAt(4)); } } } } //--------------------------------------------------------------------------- // switch (x) // { // case 2: // y = a; // break; // break; // <- Redundant break // case 3: // y = b; // } //--------------------------------------------------------------------------- void CheckOther::checkDuplicateBreak() { if (!_settings->isEnabled("style")) return; const char breakPattern[] = "break|continue ; break|continue ;"; // Find consecutive break or continue statements. e.g.: // break; break; const Token *tok = Token::findmatch(_tokenizer->tokens(), breakPattern); while (tok) { duplicateBreakError(tok); tok = Token::findmatch(tok->next(), breakPattern); } } void CheckOther::sizeofForNumericParameterError(const Token *tok) { reportError(tok, Severity::error, "sizeofwithnumericparameter", "Using sizeof with a numeric constant as function " "argument might not be what you intended.\n" "It is unusual to use constant value with sizeof. For example, this code:\n" " int f() {\n" " return sizeof(10);\n" " }\n" " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10." ); } void CheckOther::sizeofForArrayParameterError(const Token *tok) { reportError(tok, Severity::error, "sizeofwithsilentarraypointer", "Using sizeof for array given as function argument " "returns the size of pointer.\n" "Giving array as function parameter and then using sizeof-operator for the array " "argument. In this case the sizeof-operator returns the size of pointer (in the " "system). It does not return the size of the whole array in bytes as might be " "expected. For example, this code:\n" " int f(char a[100]) {\n" " return sizeof(a);\n" " }\n" " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the " "size of the array in bytes)." ); } void CheckOther::invalidScanfError(const Token *tok) { reportError(tok, Severity::warning, "invalidscanf", "scanf without field width limits can crash with huge input data\n" "scanf without field width limits can crash with huge input data. To fix this error " "message add a field width specifier:\n" " %s => %20s\n" " %i => %3i\n" "\n" "Sample program that can crash:\n" "\n" "#include \n" "int main()\n" "{\n" " int a;\n" " scanf(\"%i\", &a);\n" " return 0;\n" "}\n" "\n" "To make it crash:\n" "perl -e 'print \"5\"x2100000' | ./a.out"); } //--------------------------------------------------------------------------- // Check for unsigned divisions //--------------------------------------------------------------------------- void CheckOther::checkUnsignedDivision() { if (!_settings->isEnabled("style")) return; // Check for "ivar / uvar" and "uvar / ivar" std::map varsign; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "[{};(,] %type% %var% [;=,)]")) { if (tok->tokAt(1)->isUnsigned()) varsign[tok->tokAt(2)->varId()] = 'u'; else varsign[tok->tokAt(2)->varId()] = 's'; } else if (!Token::Match(tok, "[).]") && Token::Match(tok->next(), "%var% / %num%")) { if (tok->strAt(3)[0] == '-') { char sign1 = varsign[tok->tokAt(1)->varId()]; if (sign1 == 'u') { udivError(tok->next()); } } } else if (Token::Match(tok, "(|[|=|%op% %num% / %var%")) { if (tok->strAt(1)[0] == '-') { char sign2 = varsign[tok->tokAt(3)->varId()]; if (sign2 == 'u') { udivError(tok->next()); } } } } } //--------------------------------------------------------------------------- // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted //--------------------------------------------------------------------------- void CheckOther::checkMemsetZeroBytes() { const Token *tok = _tokenizer->tokens(); while (tok && ((tok = Token::findmatch(tok, "memset ( %var% , %num% , 0 )")) != NULL)) { memsetZeroBytesError(tok, tok->strAt(2)); tok = tok->tokAt(8); } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Usage of function variables //--------------------------------------------------------------------------- /** * @brief This class is used to capture the control flow within a function. */ class ScopeInfo { public: ScopeInfo() : _token(NULL), _parent(NULL) { } ScopeInfo(const Token *token, ScopeInfo *parent_) : _token(token), _parent(parent_) { } ~ScopeInfo(); ScopeInfo *parent() { return _parent; } ScopeInfo *addChild(const Token *token); void remove(ScopeInfo *scope); private: const Token *_token; ScopeInfo *_parent; std::list _children; }; ScopeInfo::~ScopeInfo() { while (!_children.empty()) { delete *_children.begin(); _children.pop_front(); } } ScopeInfo *ScopeInfo::addChild(const Token *token) { ScopeInfo *temp = new ScopeInfo(token, this); _children.push_back(temp); return temp; } void ScopeInfo::remove(ScopeInfo *scope) { std::list::iterator it; for (it = _children.begin(); it != _children.end(); ++it) { if (*it == scope) { delete *it; _children.erase(it); break; } } } /** * @brief This class is used create a list of variables within a function. */ class Variables { public: enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer }; /** Store information about variable usage */ class VariableUsage { public: VariableUsage(const Token *name = 0, VariableType type = standard, ScopeInfo *scope = NULL, bool read = false, bool write = false, bool modified = false, bool allocateMemory = false) : _name(name), _type(type), _scope(scope), _read(read), _write(write), _modified(modified), _allocateMemory(allocateMemory) { } /** variable is used.. set both read+write */ void use() { _read = true; _write = true; } /** is variable unused? */ bool unused() const { return (!_read && !_write); } const Token *_name; VariableType _type; ScopeInfo *_scope; bool _read; bool _write; bool _modified; // read/modify/write bool _allocateMemory; std::set _aliases; std::set _assignments; }; typedef std::map VariableMap; void clear() { _varUsage.clear(); } VariableMap &varUsage() { return _varUsage; } void addVar(const Token *name, VariableType type, ScopeInfo *scope, bool write_); void allocateMemory(unsigned int varid); void read(unsigned int varid); void readAliases(unsigned int varid); void readAll(unsigned int varid); void write(unsigned int varid); void writeAliases(unsigned int varid); void writeAll(unsigned int varid); void use(unsigned int varid); void modified(unsigned int varid); VariableUsage *find(unsigned int varid); void alias(unsigned int varid1, unsigned int varid2, bool replace); void erase(unsigned int varid) { _varUsage.erase(varid); } void eraseAliases(unsigned int varid); void eraseAll(unsigned int varid); void clearAliases(unsigned int varid); private: VariableMap _varUsage; }; /** * Alias the 2 given variables. Either replace the existing aliases if * they exist or merge them. You would replace an existing alias when this * assignment is in the same scope as the previous assignment. You might * merge the aliases when this assignment is in a different scope from the * previous assignment depending on the relationship of the 2 scopes. */ void Variables::alias(unsigned int varid1, unsigned int varid2, bool replace) { VariableUsage *var1 = find(varid1); VariableUsage *var2 = find(varid2); // alias to self if (varid1 == varid2) { if (var1) var1->use(); return; } std::set::iterator i; if (replace) { // remove var1 from all aliases for (i = var1->_aliases.begin(); i != var1->_aliases.end(); ++i) { VariableUsage *temp = find(*i); if (temp) temp->_aliases.erase(var1->_name->varId()); } // remove all aliases from var1 var1->_aliases.clear(); } // var1 gets all var2s aliases for (i = var2->_aliases.begin(); i != var2->_aliases.end(); ++i) { if (*i != varid1) var1->_aliases.insert(*i); } // var2 is an alias of var1 var2->_aliases.insert(varid1); var1->_aliases.insert(varid2); if (var2->_type == Variables::pointer) var2->_read = true; } void Variables::clearAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { // remove usage from all aliases std::set::iterator i; for (i = usage->_aliases.begin(); i != usage->_aliases.end(); ++i) { VariableUsage *temp = find(*i); if (temp) temp->_aliases.erase(usage->_name->varId()); } // remove all aliases from usage usage->_aliases.clear(); } } void Variables::eraseAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) erase(*aliases); } } void Variables::eraseAll(unsigned int varid) { eraseAliases(varid); erase(varid); } void Variables::addVar(const Token *name, VariableType type, ScopeInfo *scope, bool write_) { if (name->varId() > 0) _varUsage.insert(std::make_pair(name->varId(), VariableUsage(name, type, scope, false, write_, false))); } void Variables::allocateMemory(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) usage->_allocateMemory = true; } void Variables::read(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) usage->_read = true; } void Variables::readAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_read = true; } } } void Variables::readAll(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { usage->_read = true; std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_read = true; } } } void Variables::write(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) usage->_write = true; } void Variables::writeAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_write = true; } } } void Variables::writeAll(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { usage->_write = true; std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_write = true; } } } void Variables::use(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { usage->use(); std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->use(); } } } void Variables::modified(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { usage->_modified = true; std::set::iterator aliases; for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) aliased->_modified = true; } } } Variables::VariableUsage *Variables::find(unsigned int varid) { if (varid) { VariableMap::iterator i = _varUsage.find(varid); if (i != _varUsage.end()) return &i->second; } return 0; } static int doAssignment(Variables &variables, const Token *tok, bool dereference, ScopeInfo *scope) { int next = 0; // a = a + b; if (Token::Match(tok, "%var% = %var% !!;") && tok->str() == tok->strAt(2)) { return 2; } // check for aliased variable const unsigned int varid1 = tok->varId(); Variables::VariableUsage *var1 = variables.find(varid1); if (var1) { int start = 1; // search for '=' while (tok->tokAt(start)->str() != "=") start++; start++; if (Token::Match(tok->tokAt(start), "&| %var%") || Token::Match(tok->tokAt(start), "( const| struct|union| %type% *| ) &| %var%") || Token::Match(tok->tokAt(start), "( const| struct|union| %type% *| ) ( &| %var%") || Token::Match(tok->tokAt(start), "%any% < const| struct|union| %type% *| > ( &| %var%")) { unsigned char offset = 0; unsigned int varid2; bool addressOf = false; if (Token::Match(tok->tokAt(start), "%var% .")) variables.use(tok->tokAt(start)->varId()); // use = read + write // check for C style cast if (tok->tokAt(start)->str() == "(") { if (tok->tokAt(start + 1)->str() == "const") offset++; if (Token::Match(tok->tokAt(start + 1 + offset), "struct|union")) offset++; if (tok->tokAt(start + 2 + offset)->str() == "*") offset++; if (tok->tokAt(start + 3 + offset)->str() == "&") { addressOf = true; next = start + 4 + offset; } else if (tok->tokAt(start + 3 + offset)->str() == "(") { if (tok->tokAt(start + 4 + offset)->str() == "&") { addressOf = true; next = start + 5 + offset; } else next = start + 4 + offset; } else next = start + 3 + offset; } // check for C++ style cast else if (tok->tokAt(start)->str().find("cast") != std::string::npos && tok->tokAt(start + 1)->str() == "<") { if (tok->tokAt(start + 2)->str() == "const") offset++; if (Token::Match(tok->tokAt(start + 2 + offset), "struct|union")) offset++; if (tok->tokAt(start + 3 + offset)->str() == "*") offset++; if (tok->tokAt(start + 5 + offset)->str() == "&") { addressOf = true; next = start + 6 + offset; } else next = start + 5 + offset; } // check for var ? ... else if (Token::Match(tok->tokAt(start), "%var% ?")) { next = start; } // no cast else { if (tok->tokAt(start)->str() == "&") { addressOf = true; next = start + 1; } else if (tok->tokAt(start)->str() == "new") return 0; else next = start; } // check if variable is local varid2 = tok->tokAt(next)->varId(); Variables::VariableUsage *var2 = variables.find(varid2); if (var2) { // local variable (alias or read it) if (var1->_type == Variables::pointer) { if (dereference) variables.read(varid2); else { if (addressOf || var2->_type == Variables::array || var2->_type == Variables::pointer) { bool replace = true; // check if variable declared in same scope if (scope == var1->_scope) replace = true; // not in same scope as declaration else { std::set::iterator assignment; // check for an assignment in this scope assignment = var1->_assignments.find(scope); // no other assignment in this scope if (assignment == var1->_assignments.end()) { // nothing to replace if (var1->_assignments.empty()) replace = false; // this variable has previous assignments else { /** * @todo determine if existing aliases should be replaced or merged */ replace = false; } } // assignment in this scope else { // replace when only one other assignment if (var1->_assignments.size() == 1) replace = true; // otherwise, merge them else replace = false; } } variables.alias(varid1, varid2, replace); } else if (tok->tokAt(next + 1)->str() == "?") { if (var2->_type == Variables::reference) variables.readAliases(varid2); else variables.read(varid2); } } } else if (var1->_type == Variables::reference) { variables.alias(varid1, varid2, true); } else { if (var2->_type == Variables::pointer && tok->tokAt(next + 1)->str() == "[") variables.readAliases(varid2); variables.read(varid2); } } else { // not a local variable (or an unsupported local variable) if (var1->_type == Variables::pointer && !dereference) { // check if variable declaration is in this scope if (var1->_scope == scope) variables.clearAliases(varid1); else { std::set::iterator assignment; // check for an assignment in this scope assignment = var1->_assignments.find(scope); // no other assignment in this scope if (assignment == var1->_assignments.end()) { /** * @todo determine if existing aliases should be discarded */ } // this assignment replaces the last assignment in this scope else { // aliased variables in a larger scope are not supported // remove all aliases variables.clearAliases(varid1); } } } } } var1->_assignments.insert(scope); } // check for alias to struct member // char c[10]; a.b = c; else if (Token::Match(tok->tokAt(-2), "%var% .")) { if (Token::Match(tok->tokAt(2), "%var%")) { unsigned int varid2 = tok->tokAt(2)->varId(); Variables::VariableUsage *var2 = variables.find(varid2); // struct member aliased to local variable if (var2 && (var2->_type == Variables::array || var2->_type == Variables::pointer)) { // erase aliased variable and all variables that alias it // to prevent false positives variables.eraseAll(varid2); } } } return next; } static bool nextIsStandardType(const Token *tok) { tok = tok->next(); if (tok->str() == "static") tok = tok->next(); return tok->isStandardType(); } static bool nextIsStandardTypeOrVoid(const Token *tok) { tok = tok->next(); if (tok->str() == "static") tok = tok->next(); if (tok->str() == "const") tok = tok->next(); return tok->isStandardType() || tok->str() == "void"; } bool CheckOther::isRecordTypeWithoutSideEffects(const Token *tok) { const Variable * var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(tok->varId()); // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check base class for side effects */ /** @todo false negative: check constructors for side effects */ if (var && var->type() && var->type()->numConstructors == 0 && (var->type()->varlist.empty() || var->type()->needInitialization == Scope::True) && var->type()->derivedFrom.empty()) return true; return false; } void CheckOther::functionVariableUsage() { if (!_settings->isEnabled("style")) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // First token for the current scope.. const Token *const tok1 = scope->classStart; // varId, usage {read, write, modified} Variables variables; // scopes ScopeInfo scopes; ScopeInfo *info = &scopes; unsigned int indentlevel = 0; for (const Token *tok = tok1; tok; tok = tok->next()) { if (tok->str() == "{") { // replace the head node when found if (indentlevel == 0) scopes = ScopeInfo(tok, NULL); // add the new scope else info = info->addChild(tok); ++indentlevel; } else if (tok->str() == "}") { --indentlevel; info = info->parent(); if (indentlevel == 0) break; } else if (Token::Match(tok, "struct|union|class {") || Token::Match(tok, "struct|union|class %type% {|:")) { while (tok->str() != "{") tok = tok->next(); tok = tok->link(); if (! tok) break; } if (Token::Match(tok, "[;{}] asm ( ) ;")) { variables.clear(); break; } // standard type declaration with possible initialization // int i; int j = 0; static int k; if (Token::Match(tok, "[;{}] static| %type% %var% ;|=") && !Token::Match(tok->next(), "return|throw")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->isStandardType() || isRecordTypeWithoutSideEffects(tok->next())) { variables.addVar(tok->next(), Variables::standard, info, tok->tokAt(2)->str() == "=" || isStatic); } tok = tok->next(); } // standard const type declaration // const int i = x; else if (Token::Match(tok, "[;{}] const %type% %var% =")) { tok = tok->next()->next(); if (tok->isStandardType() || isRecordTypeWithoutSideEffects(tok->next())) variables.addVar(tok->next(), Variables::standard, info, true); tok = tok->next(); } // std::string declaration with possible initialization // std::string s; std::string s = "string"; else if (Token::Match(tok, "[;{}] static| std :: string %var% ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); tok = tok->tokAt(3); variables.addVar(tok, Variables::standard, info, tok->next()->str() == "=" || isStatic); } // standard struct type declaration with possible initialization // struct S s; struct S s = { 0 }; static struct S s; else if (Token::Match(tok, "[;{}] static| struct %type% %var% ;|=") && (isRecordTypeWithoutSideEffects(tok->strAt(1) == "static" ? tok->tokAt(4) : tok->tokAt(3)))) { tok = tok->next(); bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); tok = tok->next(); variables.addVar(tok->next(), Variables::standard, info, tok->tokAt(2)->str() == "=" || isStatic); tok = tok->next(); } // standard type declaration and initialization using constructor // int i(0); static int j(0); else if (Token::Match(tok, "[;{}] static| %type% %var% ( %any% ) ;") && nextIsStandardType(tok)) { tok = tok->next(); if (tok->str() == "static") tok = tok->next(); variables.addVar(tok->next(), Variables::standard, info, true); // check if a local variable is used to initialize this variable if (tok->tokAt(3)->varId() > 0) variables.readAll(tok->tokAt(3)->varId()); tok = tok->tokAt(4); } // standard type declaration of array of with possible initialization // int i[10]; int j[2] = { 0, 1 }; static int k[2] = { 2, 3 }; else if (Token::Match(tok, "[;{}] static| const| %type% *| %var% [ %any% ] ;|=") && nextIsStandardType(tok)) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->str() != "return" && tok->str() != "throw") { bool isPointer = bool(tok->strAt(1) == "*"); const Token * const nametok = tok->tokAt(isPointer ? 2 : 1); variables.addVar(nametok, isPointer ? Variables::pointerArray : Variables::array, info, nametok->tokAt(4)->str() == "=" || isStatic); // check for reading array size from local variable if (nametok->tokAt(2)->varId() != 0) variables.read(nametok->tokAt(2)->varId()); // look at initializers if (Token::simpleMatch(nametok->tokAt(4), "= {")) { tok = nametok->tokAt(6); while (tok->str() != "}") { if (Token::Match(tok, "%var%")) variables.read(tok->varId()); tok = tok->next(); } } else tok = nametok->tokAt(3); } } // pointer or reference declaration with possible initialization // int * i; int * j = 0; static int * k = 0; else if (Token::Match(tok, "[;{}] static| const| %type% *|& %var% ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->strAt(1) == "::") tok = tok->tokAt(2); if (tok->str() != "return" && tok->str() != "throw") { Variables::VariableType type; if (tok->next()->str() == "*") type = Variables::pointer; else type = Variables::reference; bool written = tok->tokAt(3)->str() == "="; variables.addVar(tok->tokAt(2), type, info, written || isStatic); int offset = 0; // check for assignment if (written) offset = doAssignment(variables, tok->tokAt(2), false, info); tok = tok->tokAt(2 + offset); } } // pointer to pointer declaration with possible initialization // int ** i; int ** j = 0; static int ** k = 0; else if (Token::Match(tok, "[;{}] static| const| %type% * * %var% ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->str() != "return") { bool written = tok->tokAt(4)->str() == "="; variables.addVar(tok->tokAt(3), Variables::pointerPointer, info, written || isStatic); int offset = 0; // check for assignment if (written) offset = doAssignment(variables, tok->tokAt(3), false, info); tok = tok->tokAt(3 + offset); } } // pointer or reference of struct or union declaration with possible initialization // struct s * i; struct s * j = 0; static struct s * k = 0; else if (Token::Match(tok, "[;{}] static| const| struct|union %type% *|& %var% ;|=")) { Variables::VariableType type; tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->strAt(2) == "*") type = Variables::pointer; else type = Variables::reference; const bool written = tok->strAt(4) == "="; variables.addVar(tok->tokAt(3), type, info, written || isStatic); int offset = 0; // check for assignment if (written) offset = doAssignment(variables, tok->tokAt(3), false, info); tok = tok->tokAt(3 + offset); } // pointer or reference declaration with initialization using constructor // int * i(j); int * k(i); static int * l(i); else if (Token::Match(tok, "[;{}] static| const| %type% &|* %var% ( %any% ) ;") && nextIsStandardTypeOrVoid(tok)) { Variables::VariableType type; tok = tok->next(); if (tok->str() == "static") tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->next()->str() == "*") type = Variables::pointer; else type = Variables::reference; unsigned int varid = 0; // check for aliased variable if (Token::Match(tok->tokAt(4), "%var%")) varid = tok->tokAt(4)->varId(); variables.addVar(tok->tokAt(2), type, info, true); // check if a local variable is used to initialize this variable if (varid > 0) { Variables::VariableUsage *var = variables.find(varid); if (type == Variables::pointer) { variables.use(tok->tokAt(4)->varId()); if (var && (var->_type == Variables::array || var->_type == Variables::pointer)) var->_aliases.insert(tok->varId()); } else { variables.readAll(tok->tokAt(4)->varId()); if (var) var->_aliases.insert(tok->varId()); } } tok = tok->tokAt(5); } // array of pointer or reference declaration with possible initialization // int * p[10]; int * q[10] = { 0 }; static int * * r[10] = { 0 }; else if (Token::Match(tok, "[;{}] static| const| %type% *|& %var% [ %any% ] ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (tok->str() != "return") { variables.addVar(tok->tokAt(2), tok->next()->str() == "*" ? Variables::pointerArray : Variables::referenceArray, info, tok->tokAt(6)->str() == "=" || isStatic); // check for reading array size from local variable if (tok->tokAt(4)->varId() != 0) variables.read(tok->tokAt(4)->varId()); tok = tok->tokAt(5); } } // array of pointer or reference of struct or union declaration with possible initialization // struct S * p[10]; struct T * q[10] = { 0 }; static struct S * r[10] = { 0 }; else if (Token::Match(tok, "[;{}] static| const| struct|union %type% *|& %var% [ %any% ] ;|=")) { tok = tok->next(); const bool isStatic = tok->str() == "static"; if (isStatic) tok = tok->next(); if (tok->str() == "const") tok = tok->next(); variables.addVar(tok->tokAt(3), tok->tokAt(2)->str() == "*" ? Variables::pointerArray : Variables::referenceArray, info, tok->tokAt(7)->str() == "=" || isStatic); // check for reading array size from local variable if (tok->tokAt(5)->varId() != 0) variables.read(tok->tokAt(5)->varId()); tok = tok->tokAt(6); } // Freeing memory (not considered "using" the pointer if it was also allocated in this function) else if (Token::Match(tok, "free|g_free|kfree|vfree ( %var% )") || Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")) { unsigned int varid = 0; if (tok->str() != "delete") { varid = tok->tokAt(2)->varId(); tok = tok->tokAt(3); } else if (tok->strAt(1) == "[") { varid = tok->tokAt(3)->varId(); tok = tok->tokAt(4); } else { varid = tok->next()->varId(); tok = tok->tokAt(2); } Variables::VariableUsage *var = variables.find(varid); if (var && !var->_allocateMemory) { variables.readAll(varid); } } else if (Token::Match(tok, "return|throw %var%")) variables.readAll(tok->next()->varId()); // assignment else if (Token::Match(tok, "*| (| ++|--| %var% ++|--| )| =") || Token::Match(tok, "*| ( const| %type% *| ) %var% =")) { bool dereference = false; bool pre = false; bool post = false; if (tok->str() == "*") { dereference = true; tok = tok->next(); } if (Token::Match(tok, "( const| %type% *| ) %var% =")) tok = tok->link()->next(); else if (tok->str() == "(") tok = tok->next(); if (Token::Match(tok, "++|--")) { pre = true; tok = tok->next(); } if (Token::Match(tok->next(), "++|--")) post = true; const unsigned int varid1 = tok->varId(); const Token *start = tok; tok = tok->tokAt(doAssignment(variables, tok, dereference, info)); if (pre || post) variables.use(varid1); if (dereference) { Variables::VariableUsage *var = variables.find(varid1); if (var && var->_type == Variables::array) variables.write(varid1); variables.writeAliases(varid1); variables.read(varid1); } else { Variables::VariableUsage *var = variables.find(varid1); if (var && var->_type == Variables::reference) { variables.writeAliases(varid1); variables.read(varid1); } // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable else if (var && var->_type == Variables::pointer && Token::Match(start, "%var% = new|malloc|calloc|g_malloc|kmalloc|vmalloc")) { bool allocate = true; if (start->strAt(2) == "new") { // is it a user defined type? if (!start->tokAt(3)->isStandardType()) { if (!isRecordTypeWithoutSideEffects(start)) allocate = false; } } if (allocate) variables.allocateMemory(varid1); else variables.write(varid1); } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) { variables.use(varid1); } else { variables.write(varid1); } Variables::VariableUsage *var2 = variables.find(tok->varId()); if (var2) { if (var2->_type == Variables::reference) { variables.writeAliases(tok->varId()); variables.read(tok->varId()); } else if (tok->varId() != varid1 && Token::Match(tok, "%var% .")) variables.read(tok->varId()); else if (tok->varId() != varid1 && var2->_type == Variables::standard && tok->strAt(-1) != "&") variables.use(tok->varId()); } } const Token *equal = tok->next(); if (Token::Match(tok->next(), "[ %any% ]")) equal = tok->tokAt(4); // checked for chained assignments if (tok != start && equal->str() == "=") { Variables::VariableUsage *var = variables.find(tok->varId()); if (var && var->_type != Variables::reference) var->_read = true; tok = tok->previous(); } } // assignment else if (Token::Match(tok, "%var% [") && Token::simpleMatch(tok->next()->link(), "] =")) { unsigned int varid = tok->varId(); const Variables::VariableUsage *var = variables.find(varid); if (var) { // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable if (var->_type == Variables::pointer && Token::Match(tok->next()->link(), "] = new|malloc|calloc|g_malloc|kmalloc|vmalloc")) { variables.allocateMemory(varid); } else if (var->_type == Variables::pointer || var->_type == Variables::reference) { variables.read(varid); variables.writeAliases(varid); } else variables.writeAll(varid); } } else if (Token::Match(tok, ">>|& %var%")) variables.use(tok->next()->varId()); // use = read + write else if (Token::Match(tok, "[;{}] %var% >>")) variables.use(tok->next()->varId()); // use = read + write // function parameter else if (Token::Match(tok, "[(,] %var% [")) variables.use(tok->next()->varId()); // use = read + write else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") variables.use(tok->next()->varId()); // use = read + write else if (Token::Match(tok, "[(,] (") && Token::Match(tok->next()->link(), ") %var% [,)]")) variables.use(tok->next()->link()->next()->varId()); // use = read + write // function else if (Token::Match(tok, "%var% (")) { variables.read(tok->varId()); if (Token::Match(tok->tokAt(2), "%var% =")) variables.read(tok->tokAt(2)->varId()); } else if (Token::Match(tok, "[{,] %var% [,}]")) variables.read(tok->next()->varId()); else if (Token::Match(tok, "%var% .")) variables.use(tok->varId()); // use = read + write else if ((Token::Match(tok, "[(=&!]") || tok->isExtendedOp()) && (Token::Match(tok->next(), "%var%") && !Token::Match(tok->next(), "true|false|new"))) variables.readAll(tok->next()->varId()); else if (Token::Match(tok, "%var%") && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) variables.readAll(tok->varId()); else if (Token::Match(tok, "; %var% ;")) variables.readAll(tok->next()->varId()); if (Token::Match(tok, "++|-- %var%")) { if (tok->strAt(-1) != ";") variables.use(tok->next()->varId()); else variables.modified(tok->next()->varId()); } else if (Token::Match(tok, "%var% ++|--")) { if (tok->strAt(-1) != ";") variables.use(tok->varId()); else variables.modified(tok->varId()); } else if (tok->isAssignmentOp()) { for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId()) { variables.read(tok2->varId()); if (tok2->next()->isAssignmentOp()) variables.write(tok2->varId()); } } } } // Check usage of all variables in the current scope.. Variables::VariableMap::const_iterator it; for (it = variables.varUsage().begin(); it != variables.varUsage().end(); ++it) { const Variables::VariableUsage &usage = it->second; const std::string &varname = usage._name->str(); // variable has been marked as unused so ignore it if (usage._name->isUnused()) continue; // skip things that are only partially implemented to prevent false positives if (usage._type == Variables::pointerPointer || usage._type == Variables::pointerArray || usage._type == Variables::referenceArray) continue; // variable has had memory allocated for it, but hasn't done // anything with that memory other than, perhaps, freeing it if (usage.unused() && !usage._modified && usage._allocateMemory) allocatedButUnusedVariableError(usage._name, varname); // variable has not been written, read, or modified else if (usage.unused() && !usage._modified) unusedVariableError(usage._name, varname); // variable has not been written but has been modified else if (usage._modified & !usage._write) unassignedVariableError(usage._name, varname); // variable has been written but not read else if (!usage._read && !usage._modified) unreadVariableError(usage._name, varname); // variable has been read but not written else if (!usage._write && !usage._allocateMemory) unassignedVariableError(usage._name, varname); } } } void CheckOther::unusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedVariable", "Unused variable: " + varname); } void CheckOther::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedAllocatedMemory", "Variable '" + varname + "' is allocated memory that is never used"); } void CheckOther::unreadVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' is assigned a value that is never used"); } void CheckOther::unassignedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unassignedVariable", "Variable '" + varname + "' is not assigned a value"); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Check scope of variables.. //--------------------------------------------------------------------------- void CheckOther::checkVariableScope() { if (!_settings->isEnabled("information")) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // Walk through all tokens.. int indentlevel = 0; for (const Token *tok = scope->classStart; tok; tok = tok->next()) { // Skip function local class and struct declarations.. if ((tok->str() == "class") || (tok->str() == "struct") || (tok->str() == "union")) { for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { tok = tok2->link(); break; } if (Token::Match(tok2, "[,);]")) { break; } } if (! tok) break; } else if (tok->str() == "{") { ++indentlevel; } else if (tok->str() == "}") { --indentlevel; if (indentlevel == 0) break; } if (indentlevel > 0 && Token::Match(tok, "[{};]")) { // First token of statement.. const Token *tok1 = tok->next(); if (! tok1) continue; if ((tok1->str() == "return") || (tok1->str() == "throw") || (tok1->str() == "delete") || (tok1->str() == "goto") || (tok1->str() == "else")) continue; // Variable declaration? if (Token::Match(tok1, "%type% %var% ; %var% = %num% ;")) { // Tokenizer modify "int i = 0;" to "int i; i = 0;", // so to handle this situation we just skip // initialization (see ticket #272). const unsigned int firstVarId = tok1->next()->varId(); const unsigned int secondVarId = tok1->tokAt(3)->varId(); if (firstVarId > 0 && firstVarId == secondVarId) { lookupVar(tok1->tokAt(6), tok1->strAt(1)); } } else if (tok1->isStandardType() && Token::Match(tok1, "%type% %var% [;=]")) { lookupVar(tok1, tok1->strAt(1)); } } } } } //--------------------------------------------------------------------------- void CheckOther::lookupVar(const Token *tok1, const std::string &varname) { const Token *tok = tok1; // Skip the variable declaration.. while (tok && tok->str() != ";") tok = tok->next(); // Check if the variable is used in this indentlevel.. bool used1 = false; // used in one sub-scope -> reducable bool used2 = false; // used in more sub-scopes -> not reducable int indentlevel = 0; int parlevel = 0; bool for_or_while = false; // is sub-scope a "for/while/etc". anything that is not "if" while (tok) { if (tok->str() == "{") { if (tok->strAt(-1) == "=") { if (Token::findmatch(tok, varname.c_str(), tok->link())) { return; } tok = tok->link(); } else ++indentlevel; } else if (tok->str() == "}") { if (indentlevel == 0) break; --indentlevel; if (indentlevel == 0) { if (for_or_while && used2) return; used2 |= used1; used1 = false; } } else if (tok->str() == "(") { ++parlevel; } else if (tok->str() == ")") { --parlevel; } // Bail out if references are used else if (Token::simpleMatch(tok, (std::string("& ") + varname).c_str())) { return; } else if (tok->str() == varname) { if (indentlevel == 0) return; used1 = true; if (for_or_while && !Token::simpleMatch(tok->next(), "=")) used2 = true; if (used1 && used2) return; } else if (indentlevel == 0) { // %unknown% ( %any% ) { // If %unknown% is anything except if, we assume // that it is a for or while loop or a macro hiding either one if (Token::simpleMatch(tok->next(), "(") && Token::simpleMatch(tok->next()->link(), ") {")) { if (tok->str() != "if") for_or_while = true; } else if (Token::simpleMatch(tok, "do {")) for_or_while = true; // possible unexpanded macro hiding for/while.. else if (tok->str() != "else" && Token::Match(tok->previous(), "[;{}] %type% {")) { for_or_while = true; } if (parlevel == 0 && (tok->str() == ";")) for_or_while = false; } tok = tok->next(); } // Warning if this variable: // * not used in this indentlevel // * used in lower indentlevel if (used1 || used2) variableScopeError(tok1, varname); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Check for constant function parameters //--------------------------------------------------------------------------- void CheckOther::checkConstantFunctionParameter() { if (!_settings->isEnabled("style")) return; const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // TODO: False negatives. This pattern only checks for string. // Investigate if there are other classes in the std // namespace and add them to the pattern. There are // streams for example (however it seems strange with // const stream parameter). if (Token::Match(tok, "[,(] const std :: string %var% [,)]")) { passedByValueError(tok, tok->strAt(5)); } else if (Token::Match(tok, "[,(] const std :: %type% < %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(8)); } else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(10)); } else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% , std :: %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(14)); } else if (Token::Match(tok, "[,(] const std :: %type% < %type% , std :: %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(12)); } else if (Token::Match(tok, "[,(] const std :: %type% < std :: %type% , %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(12)); } else if (Token::Match(tok, "[,(] const std :: %type% < %type% , %type% > %var% [,)]")) { passedByValueError(tok, tok->strAt(10)); } else if (Token::Match(tok, "[,(] const %type% %var% [,)]")) { // Check if type is a struct or class. if (symbolDatabase->isClassOrStruct(tok->strAt(2))) { passedByValueError(tok, tok->strAt(3)); } } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Check that all struct members are used //--------------------------------------------------------------------------- void CheckOther::checkStructMemberUsage() { if (!_settings->isEnabled("style")) return; std::string structname; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->fileIndex() != 0) continue; if (Token::Match(tok, "struct|union %type% {")) { structname.clear(); if (Token::simpleMatch(tok->previous(), "extern")) continue; if ((!tok->previous() || Token::simpleMatch(tok->previous(), ";")) && Token::Match(tok->tokAt(2)->link(), ("} ; " + tok->strAt(1) + " %var% ;").c_str())) continue; structname = tok->strAt(1); // Bail out if struct/union contain any functions for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") { structname.clear(); break; } if (tok2->str() == "}") break; } // bail out if struct is inherited if (!structname.empty() && Token::findmatch(tok, (",|private|protected|public " + structname).c_str())) structname.clear(); // Bail out if some data is casted to struct.. const std::string s("( struct| " + tok->next()->str() + " * ) & %var% ["); if (Token::findmatch(tok, s.c_str())) structname.clear(); // Try to prevent false positives when struct members are not used directly. if (Token::findmatch(tok, (structname + " *").c_str())) structname.clear(); else if (Token::findmatch(tok, (structname + " %type% *").c_str())) structname = ""; } if (tok->str() == "}") structname.clear(); if (!structname.empty() && Token::Match(tok, "[{;]")) { // Declaring struct variable.. std::string varname; // declaring a POD variable? if (!tok->next()->isStandardType()) continue; if (Token::Match(tok->next(), "%type% %var% [;[]")) varname = tok->strAt(2); else if (Token::Match(tok->next(), "%type% %type% %var% [;[]")) varname = tok->strAt(3); else if (Token::Match(tok->next(), "%type% * %var% [;[]")) varname = tok->strAt(3); else if (Token::Match(tok->next(), "%type% %type% * %var% [;[]")) varname = tok->strAt(4); else continue; // Check if the struct variable is used anywhere in the file const std::string usagePattern(". " + varname); bool used = false; for (const Token *tok2 = _tokenizer->tokens(); tok2; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, usagePattern.c_str())) { used = true; break; } } if (! used) { unusedStructMemberError(tok->next(), structname, varname); } } } } //--------------------------------------------------------------------------- // Check usage of char variables.. //--------------------------------------------------------------------------- void CheckOther::checkCharVariable() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Declaring the variable.. if (Token::Match(tok, "[{};(,] const| char *| %var% [;=,)]") || Token::Match(tok, "[{};(,] const| char %var% [")) { // goto 'char' token tok = tok->next(); if (tok->str() == "const") tok = tok->next(); // Check for unsigned char if (tok->isUnsigned()) continue; // Set tok to point to the variable name tok = tok->next(); const bool isPointer(tok->str() == "*" || tok->strAt(1) == "["); if (tok->str() == "*") tok = tok->next(); // Check usage of char variable.. int indentlevel = 0; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { --indentlevel; if (indentlevel <= 0) break; } if (!isPointer) { std::string temp = "%var% [ " + tok->str() + " ]"; if ((tok2->str() != ".") && Token::Match(tok2->next(), temp.c_str())) { charArrayIndexError(tok2->next()); break; } } if (Token::Match(tok2, "[;{}] %var% = %any% [&|] %any% ;")) { // is the char variable used in the calculation? if (tok2->tokAt(3)->varId() != tok->varId() && tok2->tokAt(5)->varId() != tok->varId()) continue; // it's ok with a bitwise and where the other operand is 0xff or less.. if (tok2->strAt(4) == "&") { if (tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3))) continue; if (tok2->tokAt(5)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(5))) continue; } // is the result stored in a short|int|long? if (!Token::findmatch(_tokenizer->tokens(), "short|int|long %varid%", tok2->next()->varId())) continue; // This is an error.. charBitOpError(tok2); break; } if (isPointer && Token::Match(tok2, "[;{}] %var% = %any% [&|] ( * %varid% ) ;", tok->varId())) { // it's ok with a bitwise and where the other operand is 0xff or less.. if (tok2->strAt(4) == "&" && tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3))) continue; // is the result stored in a short|int|long? if (!Token::findmatch(_tokenizer->tokens(), "short|int|long %varid%", tok2->next()->varId())) continue; // This is an error.. charBitOpError(tok2); break; } } } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Incomplete statement.. //--------------------------------------------------------------------------- void CheckOther::checkIncompleteStatement() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "(") { tok = tok->link(); if (Token::simpleMatch(tok, ") {") && Token::simpleMatch(tok->next()->link(), "} ;")) tok = tok->next()->link(); } else if (Token::simpleMatch(tok, "= {")) tok = tok->next()->link(); else if (tok->str() == "{" && Token::Match(tok->tokAt(-2), "%type% %var%")) tok = tok->link(); else if (Token::Match(tok, "[;{}] %str%") || Token::Match(tok, "[;{}] %num%")) { // bailout if there is a "? :" in this statement bool bailout = false; for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "?") bailout = true; else if (tok2->str() == ";") break; } if (bailout) continue; constStatementError(tok->next(), tok->next()->isNumber() ? "numeric" : "string"); } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // str plus char //--------------------------------------------------------------------------- void CheckOther::strPlusChar() { // Don't use this check for Java and C# programs.. if (_tokenizer->isJavaOrCSharp()) { return; } bool charVars[10000] = {0}; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Declaring char variable.. if (Token::Match(tok, "char|int|short %var% [;=]")) { unsigned int varid = tok->next()->varId(); if (varid > 0 && varid < 10000) charVars[varid] = true; } // else if (Token::Match(tok, "[=(] %str% + %any%")) { // char constant.. const std::string s = tok->strAt(3); if (s[0] == '\'') strPlusChar(tok->next()); // char variable.. unsigned int varid = tok->tokAt(3)->varId(); if (varid > 0 && varid < 10000 && charVars[varid]) strPlusChar(tok->next()); } } } void CheckOther::checkZeroDivision() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "/ %num%") && MathLib::isInt(tok->next()->str()) && MathLib::toLongNumber(tok->next()->str()) == 0L) { zerodivError(tok); } else if (Token::Match(tok, "div|ldiv|lldiv|imaxdiv ( %num% , %num% )") && MathLib::isInt(tok->tokAt(4)->str()) && MathLib::toLongNumber(tok->tokAt(4)->str()) == 0L) { zerodivError(tok); } } } void CheckOther::checkMathFunctions() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // case log(-2) if (tok->varId() == 0 && Token::Match(tok, "log|log10 ( %num% )") && MathLib::isNegative(tok->tokAt(2)->str()) && MathLib::isInt(tok->tokAt(2)->str()) && MathLib::toLongNumber(tok->tokAt(2)->str()) <= 0) { mathfunctionCallError(tok); } // case log(-2.0) else if (tok->varId() == 0 && Token::Match(tok, "log|log10 ( %num% )") && MathLib::isNegative(tok->tokAt(2)->str()) && MathLib::isFloat(tok->tokAt(2)->str()) && MathLib::toDoubleNumber(tok->tokAt(2)->str()) <= 0.) { mathfunctionCallError(tok); } // case log(0.0) else if (tok->varId() == 0 && Token::Match(tok, "log|log10 ( %num% )") && !MathLib::isNegative(tok->tokAt(2)->str()) && MathLib::isFloat(tok->tokAt(2)->str()) && MathLib::toDoubleNumber(tok->tokAt(2)->str()) <= 0.) { mathfunctionCallError(tok); } // case log(0) else if (tok->varId() == 0 && Token::Match(tok, "log|log10 ( %num% )") && !MathLib::isNegative(tok->tokAt(2)->str()) && MathLib::isInt(tok->tokAt(2)->str()) && MathLib::toLongNumber(tok->tokAt(2)->str()) <= 0) { mathfunctionCallError(tok); } // acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond else if (tok->varId() == 0 && Token::Match(tok, "acos|asin ( %num% )") && std::fabs(MathLib::toDoubleNumber(tok->tokAt(2)->str())) > 1.0) { mathfunctionCallError(tok); } // sqrt( x ): if x is negative the result is undefined else if (tok->varId() == 0 && Token::Match(tok, "sqrt|sqrtf|sqrtl ( %num% )") && MathLib::isNegative(tok->tokAt(2)->str())) { mathfunctionCallError(tok); } // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined else if (tok->varId() == 0 && Token::Match(tok, "atan2 ( %num% , %num% )") && MathLib::isNullValue(tok->tokAt(2)->str()) && MathLib::isNullValue(tok->tokAt(4)->str())) { mathfunctionCallError(tok, 2); } // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). else if (tok->varId() == 0 && Token::Match(tok, "fmod ( %num% , %num% )") && MathLib::isNullValue(tok->tokAt(4)->str())) { mathfunctionCallError(tok, 2); } // pow ( x , y) If x is zero, and y is negative --> division by zero else if (tok->varId() == 0 && Token::Match(tok, "pow ( %num% , %num% )") && MathLib::isNullValue(tok->tokAt(2)->str()) && MathLib::isNegative(tok->tokAt(4)->str())) { mathfunctionCallError(tok, 2); } } } /** Is there a function with given name? */ static bool isFunction(const std::string &name, const Token *startToken) { const std::string pattern1(name + " ("); for (const Token *tok = startToken; tok; tok = tok->next()) { // skip executable scopes etc if (tok->str() == "(") { tok = tok->link(); if (Token::simpleMatch(tok, ") {")) tok = tok->next()->link(); else if (Token::simpleMatch(tok, ") const {")) tok = tok->tokAt(2)->link(); } // function declaration/implementation found if ((tok->str() == "*" || (tok->isName() && tok->str().find(":") ==std::string::npos)) && Token::simpleMatch(tok->next(), pattern1.c_str())) return true; } return false; } void CheckOther::checkMisusedScopedObject() { // Skip this check for .c files { const std::string fname = _tokenizer->getFiles()->at(0); size_t position = fname.rfind("."); if (position != std::string::npos) { const std::string ext = fname.substr(position); if (ext == ".c" || ext == ".C") return; } } const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; unsigned int depth = 0; for (const Token *tok = scope->classStart; tok; tok = tok->next()) { if (tok->str() == "{") { ++depth; } else if (tok->str() == "}") { --depth; if (depth == 0) break; } if (Token::Match(tok, "[;{}] %var% (") && Token::simpleMatch(tok->tokAt(2)->link(), ") ;") && symbolDatabase->isClassOrStruct(tok->next()->str()) && !isFunction(tok->next()->str(), _tokenizer->tokens())) { tok = tok->next(); misusedScopeObjectError(tok, tok->str()); tok = tok->next(); } } } } void CheckOther::checkIncorrectStringCompare() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, ". substr ( %any% , %num% ) ==|!= %str%")) { size_t clen = MathLib::toLongNumber(tok->tokAt(5)->str()); size_t slen = Token::getStrLength(tok->tokAt(8)); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", tok->tokAt(8)->str(), tok->tokAt(5)->str()); } } if (Token::Match(tok, "%str% ==|!= %var% . substr ( %any% , %num% )")) { size_t clen = MathLib::toLongNumber(tok->tokAt(8)->str()); size_t slen = Token::getStrLength(tok); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", tok->str(), tok->tokAt(8)->str()); } } } } //----------------------------------------------------------------------------- // check for duplicate expressions in if statements // if (a) { } else if (a) { } //----------------------------------------------------------------------------- static const std::string stringifyTokens(const Token *start, const Token *end) { const Token *tok = start; std::string stringified; if (tok->isUnsigned()) stringified.append("unsigned "); else if (tok->isSigned()) stringified.append("signed "); if (tok->isLong()) stringified.append("long "); stringified.append(tok->str()); while (tok && tok->next() && tok != end) { if (tok->isUnsigned()) stringified.append("unsigned "); else if (tok->isSigned()) stringified.append("signed "); if (tok->isLong()) stringified.append("long "); tok = tok->next(); stringified.append(" "); stringified.append(tok->str()); } return stringified; } static bool expressionHasSideEffects(const Token *first, const Token *last) { for (const Token *tok = first; tok != last->next(); tok = tok->next()) { // check for assignment if (tok->isAssignmentOp()) return true; // check for inc/dec else if (Token::Match(tok, "++|--")) return true; // check for function call else if (Token::Match(tok, "%var% (") && !(Token::Match(tok, "c_str|string") || tok->isStandardType())) return true; } return false; } void CheckOther::checkDuplicateIf() { if (!_settings->isEnabled("style")) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // check all the code in the function for if (...) and else if (...) statements for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { if (Token::simpleMatch(tok, "if (") && tok->strAt(-1) != "else" && Token::simpleMatch(tok->next()->link(), ") {")) { std::map expressionMap; // get the expression from the token stream std::string expression = stringifyTokens(tok->tokAt(2), tok->next()->link()->previous()); // save the expression and its location expressionMap.insert(std::make_pair(expression, tok)); // find the next else if (...) statement const Token *tok1 = tok->next()->link()->next()->link(); // check all the else if (...) statements while (Token::simpleMatch(tok1, "} else if (") && Token::simpleMatch(tok1->tokAt(3)->link(), ") {")) { // get the expression from the token stream expression = stringifyTokens(tok1->tokAt(4), tok1->tokAt(3)->link()->previous()); // try to look up the expression to check for duplicates std::map::iterator it = expressionMap.find(expression); // found a duplicate if (it != expressionMap.end()) { // check for expressions that have side effects and ignore them if (!expressionHasSideEffects(tok1->tokAt(4), tok1->tokAt(3)->link()->previous())) duplicateIfError(it->second, tok1->next()); } // not a duplicate expression so save it and its location else expressionMap.insert(std::make_pair(expression, tok1->next())); // find the next else if (...) statement tok1 = tok1->tokAt(3)->link()->next()->link(); } tok = tok->next()->link()->next(); } } } } void CheckOther::duplicateIfError(const Token *tok1, const Token *tok2) { std::list toks; toks.push_back(tok2); toks.push_back(tok1); reportError(toks, Severity::style, "duplicateIf", "Found duplicate if expressions.\n" "Finding the same expression more than once is suspicious and might indicate " "a cut and paste or logic error. Please examine this code carefully to determine " "if it is correct."); } //----------------------------------------------------------------------------- // check for duplicate code in if and else branches // if (a) { b = true; } else { b = true; } //----------------------------------------------------------------------------- void CheckOther::checkDuplicateBranch() { if (!_settings->isEnabled("style")) return; if (!_settings->inconclusive) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // check all the code in the function for if (..) else for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { if (Token::simpleMatch(tok, "if (") && tok->strAt(-1) != "else" && Token::simpleMatch(tok->next()->link(), ") {") && Token::simpleMatch(tok->next()->link()->next()->link(), "} else {")) { // save if branch code std::string branch1 = stringifyTokens(tok->next()->link()->tokAt(2), tok->next()->link()->next()->link()->previous()); // find else branch const Token *tok1 = tok->next()->link()->next()->link(); // save else branch code std::string branch2 = stringifyTokens(tok1->tokAt(3), tok1->tokAt(2)->link()->previous()); // check for duplicates if (branch1 == branch2) duplicateBranchError(tok, tok1->tokAt(2)); tok = tok->next()->link()->next(); } } } } void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2) { std::list toks; toks.push_back(tok2); toks.push_back(tok1); reportError(toks, Severity::style, "duplicateBranch", "Found duplicate branches for if and else.\n" "Finding the same code for an if branch and an else branch is suspicious and " "might indicate a cut and paste or logic error. Please examine this code " "carefully to determine if it is correct."); } //--------------------------------------------------------------------------- // check for the same expression on both sides of an operator // (x == x), (x && x), (x || x) // (x.y == x.y), (x.y && x.y), (x.y || x.y) //--------------------------------------------------------------------------- void CheckOther::checkDuplicateExpression() { if (!_settings->isEnabled("style")) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { if (Token::Match(tok, "(|&&|%oror% %var% &&|%oror%|==|!=|<=|>=|<|>|-|%or% %var% )|&&|%oror%") && tok->strAt(1) == tok->strAt(3)) { // float == float and float != float are valid NaN checks if (Token::Match(tok->tokAt(2), "==|!=") && tok->next()->varId()) { const Variable * var = symbolDatabase->getVariableFromVarId(tok->next()->varId()); if (var && var->typeStartToken() == var->typeEndToken()) { if (Token::Match(var->typeStartToken(), "float|double")) continue; } } duplicateExpressionError(tok->next(), tok->tokAt(3), tok->strAt(2)); } else if (Token::Match(tok, "(|&&|%oror% %var% . %var% &&|%oror%|==|!=|<=|>=|<|>|-|%or% %var% . %var% )|&&|%oror%") && tok->strAt(1) == tok->strAt(5) && tok->strAt(3) == tok->strAt(7)) { duplicateExpressionError(tok->next(), tok->tokAt(6), tok->strAt(4)); } } } } void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const std::string &op) { std::list toks; toks.push_back(tok2); toks.push_back(tok1); reportError(toks, Severity::style, "duplicateExpression", "Same expression on both sides of \'" + op + "\'.\n" "Finding the same expression on both sides of an operator is suspicious and might " "indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct."); } //--------------------------------------------------------------------------- // Check for string comparison involving two static strings. // if(strcmp("00FF00","00FF00")==0) // <- statement is always true //--------------------------------------------------------------------------- void CheckOther::checkAlwaysTrueOrFalseStringCompare() { if (!_settings->isEnabled("style")) return; const char pattern1[] = "strcmp|stricmp|strcmpi|strcasecmp|wcscmp ( %str% , %str% )"; const char pattern2[] = "QString :: compare ( %str% , %str% )"; const Token *tok = _tokenizer->tokens(); while (tok && (tok = Token::findmatch(tok, pattern1)) != NULL) { alwaysTrueFalseStringCompare(tok, tok->strAt(2), tok->strAt(4)); tok = tok->tokAt(5); } tok = _tokenizer->tokens(); while (tok && (tok = Token::findmatch(tok, pattern2)) != NULL) { alwaysTrueFalseStringCompare(tok, tok->strAt(4), tok->strAt(6)); tok = tok->tokAt(7); } } void CheckOther::alwaysTrueFalseStringCompare(const Token *tok, const std::string& str1, const std::string& str2) { const size_t stringLen = 10; const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + ".."); const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + ".."); if (str1 == str2) { reportError(tok, Severity::warning, "staticStringCompare", "Comparison of always identical static strings.\n" "The compared strings, '" + string1 + "' and '" + string2 + "', are always identical. " "If the purpose is to compare these two strings, the comparison is unnecessary. " "If the strings are supposed to be different, then there is a bug somewhere."); } else { reportError(tok, Severity::performance, "staticStringCompare", "Unnecessary comparison of static strings.\n" "The compared strings, '" + string1 + "' and '" + string2 + "', are static and always different. " "If the purpose is to compare these two strings, the comparison is unnecessary."); } } //----------------------------------------------------------------------------- void CheckOther::cstyleCastError(const Token *tok) { reportError(tok, Severity::style, "cstyleCast", "C-style pointer casting"); } void CheckOther::dangerousUsageStrtolError(const Token *tok) { reportError(tok, Severity::error, "dangerousUsageStrtol", "Invalid radix in call to strtol or strtoul. Must be 0 or 2-36"); } void CheckOther::sprintfOverlappingDataError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "sprintfOverlappingData", "Undefined behavior: variable is used as parameter and destination in s[n]printf().\n" "The variable '" + varname + "' is used both as a parameter and as a destination in " "s[n]printf(). The origin and destination buffers overlap. Quote from glibc (C-library) " "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): " "'If copying takes place between objects that overlap as a result of a call " "to sprintf() or snprintf(), the results are undefined.'"); } void CheckOther::udivError(const Token *tok) { reportError(tok, Severity::error, "udivError", "Unsigned division. The result will be wrong."); } void CheckOther::unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname) { reportError(tok, Severity::style, "unusedStructMember", "struct or union member '" + structname + "::" + varname + "' is never used"); } void CheckOther::passedByValueError(const Token *tok, const std::string &parname) { reportError(tok, Severity::performance, "passedByValue", "Function parameter '" + parname + "' should be passed by reference.\n" "Parameter '" + parname + "' is passed as a value. It could be passed " "as a (const) reference which is usually faster and recommended in C++."); } void CheckOther::constStatementError(const Token *tok, const std::string &type) { reportError(tok, Severity::warning, "constStatement", "Redundant code: Found a statement that begins with " + type + " constant"); } void CheckOther::charArrayIndexError(const Token *tok) { reportError(tok, Severity::warning, "charArrayIndex", "Using char type as array index\n" "Using signed char type as array index. If the value " "can be greater than 127 there will be a buffer overflow " "(because of sign extension)."); } void CheckOther::charBitOpError(const Token *tok) { reportError(tok, Severity::warning, "charBitOp", "When using char variables in bit operations, sign extension can generate unexpected results.\n" "When using char variables in bit operations, sign extension can generate unexpected results. For example:\n" " char c = 0x80;\n" " int i = 0 | c;\n" " if (i & 0x8000)\n" " printf(\"not expected\");\n" "The 'not expected' will be printed on the screen."); } void CheckOther::variableScopeError(const Token *tok, const std::string &varname) { reportError(tok, Severity::information, "variableScope", "The scope of the variable '" + varname + "' can be reduced\n" "The scope of the variable '" + varname + "' can be reduced. Warning: It can be unsafe " "to fix this message. Be careful. Especially when there are inner loops. Here is an " "example where cppcheck will write that the scope for 'i' can be reduced:\n" "void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {\n" " // it's safe to move 'int i = 0' here\n" " for (int n = 0; n < 10; ++n) {\n" " // it is possible but not safe to move 'int i = 0' here\n" " do_something(&i);\n" " }\n" " }\n" "}\n" "When you see this message it is always safe to reduce the variable scope 1 level."); } void CheckOther::conditionAlwaysTrueFalse(const Token *tok, const std::string &truefalse) { reportError(tok, Severity::style, "conditionAlwaysTrueFalse", "Condition is always " + truefalse); } void CheckOther::strPlusChar(const Token *tok) { reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic"); } void CheckOther::zerodivError(const Token *tok) { reportError(tok, Severity::error, "zerodiv", "Division by zero"); } void CheckOther::mathfunctionCallError(const Token *tok, const unsigned int numParam) { if (tok) { if (numParam == 1) reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok->tokAt(2)->str() + " to " + tok->str() + "() leads to undefined result"); else if (numParam == 2) reportError(tok, Severity::error, "wrongmathcall", "Passing value " + tok->tokAt(2)->str() + " and " + tok->tokAt(4)->str() + " to " + tok->str() + "() leads to undefined result"); } else reportError(tok, Severity::error, "wrongmathcall", "Passing value " " to " "() leads to undefined result"); } void CheckOther::fflushOnInputStreamError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "fflushOnInputStream", "fflush() called on input stream \"" + varname + "\" may result in undefined behaviour"); } void CheckOther::sizeofsizeof() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof (| sizeof")) { sizeofsizeofError(tok); tok = tok->next(); } } } void CheckOther::sizeofsizeofError(const Token *tok) { reportError(tok, Severity::warning, "sizeofsizeof", "Calling sizeof for 'sizeof'.\n" "Calling sizeof for 'sizeof looks like a suspicious code and " "most likely there should be just one 'sizeof'. The current " "code is equivalent to 'sizeof(size_t)'"); } void CheckOther::sizeofCalculation() { if (!_settings->isEnabled("style")) return; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { unsigned int parlevel = 0; for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") ++parlevel; else if (tok2->str() == ")") { if (parlevel <= 1) break; --parlevel; } else if (Token::Match(tok2, "+|/")) { sizeofCalculationError(tok2); break; } } } } } void CheckOther::sizeofCalculationError(const Token *tok) { reportError(tok, Severity::warning, "sizeofCalculation", "Found calculation inside sizeof()"); } void CheckOther::redundantAssignmentInSwitchError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "redundantAssignInSwitch", "Redundant assignment of \"" + varname + "\" in switch"); } void CheckOther::switchCaseFallThrough(const Token *tok) { reportError(tok, Severity::style, "switchCaseFallThrough", "Switch falls through case without comment"); } void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "selfAssignment", "Redundant assignment of \"" + varname + "\" to itself"); } void CheckOther::assignmentInAssertError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "assignmentInAssert", "Assert statement modifies '" + varname + "'.\n" "Variable '" + varname + "' is modified inside assert statement. " "Assert statements are removed from release builds so the code inside " "assert statement is not run. If the code is needed also in release " "builds this is a bug."); } void CheckOther::incorrectLogicOperatorError(const Token *tok, bool always) { if (always) reportError(tok, Severity::warning, "incorrectLogicOperator", "Mutual exclusion over || always evaluates to true. Did you intend to use && instead?"); else reportError(tok, Severity::warning, "incorrectLogicOperator", "Expression always evaluates to false. Did you intend to use || instead?"); } void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "unusedScopedObject", "instance of \"" + varname + "\" object destroyed immediately"); } void CheckOther::catchExceptionByValueError(const Token *tok) { reportError(tok, Severity::style, "catchExceptionByValue", "Exception should be caught by reference.\n" "The exception is caught as a value. It could be caught " "as a (const) reference which is usually recommended in C++."); } void CheckOther::memsetZeroBytesError(const Token *tok, const std::string &varname) { const std::string summary("memset() called to fill 0 bytes of \'" + varname + "\'"); const std::string verbose(summary + ". Second and third arguments might be inverted."); reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose); } void CheckOther::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string, const std::string &len) { reportError(tok, Severity::warning, "incorrectStringCompare", "String literal " + string + " doesn't match length argument for " + func + "(" + len + ")."); } void CheckOther::comparisonOfBoolWithIntError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "comparisonOfBoolWithInt", "Comparison of a boolean with a non-zero integer\n" "The expression \"!" + varname + "\" is of type 'bool' but is compared against a non-zero 'int'."); } void CheckOther::duplicateBreakError(const Token *tok) { reportError(tok, Severity::style, "duplicateBreak", "Consecutive break or continue statements are unnecessary\n" "The second of the two statements can never be executed, and so should be removed\n"); } void CheckOther::checkAssignBoolToPointer() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %var% = %bool% ;")) { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const Variable *var1(symbolDatabase->getVariableFromVarId(tok->next()->varId())); // Is variable a pointer? if (var1 && var1->nameToken()->strAt(-1) == "*") assignBoolToPointerError(tok->next()); } } } void CheckOther::assignBoolToPointerError(const Token *tok) { reportError(tok, Severity::error, "assignBoolToPointer", "Assigning bool value to pointer (converting bool value to address)"); } //--------------------------------------------------------------------------- // Check testing sign of unsigned variables. //--------------------------------------------------------------------------- void CheckOther::checkSignOfUnsignedVariable() { if (!_settings->isEnabled("style")) return; const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); std::list::const_iterator scope; for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) { // only check functions if (scope->type != Scope::eFunction) continue; // check all the code in the function for (const Token *tok = scope->classStart; tok && tok != scope->classStart->link(); tok = tok->next()) { if (Token::Match(tok, "( %var% <|<= 0 )") && tok->next()->varId()) { const Variable * var = symbolDatabase->getVariableFromVarId(tok->next()->varId()); if (var && var->typeEndToken()->isUnsigned()) unsignedLessThanZero(tok->next(), tok->next()->str()); } else if (Token::Match(tok, "( 0 > %var% )") && tok->tokAt(3)->varId()) { const Variable * var = symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId()); if (var && var->typeEndToken()->isUnsigned()) unsignedLessThanZero(tok->tokAt(3), tok->strAt(3)); } else if (Token::Match(tok, "( 0 <= %var% )") && tok->tokAt(3)->varId()) { const Variable * var = symbolDatabase->getVariableFromVarId(tok->tokAt(3)->varId()); if (var && var->typeEndToken()->isUnsigned()) unsignedPositive(tok->tokAt(3), tok->strAt(3)); } } } } void CheckOther::unsignedLessThanZero(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unsignedLessThanZero", "Checking if unsigned variable '" + varname + "' is less than zero.\n" "An unsigned variable will never be negative so it is either pointless or " "an error to check if it is."); } void CheckOther::unsignedPositive(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unsignedPositive", "Checking if unsigned variable '" + varname + "' is positive is always true.\n" "An unsigned variable can't be negative so it is either pointless or " "an error to check if it is."); } cppcheck-1.90/gui/test/data/files/000077500000000000000000000000001357737443600170245ustar00rootroot00000000000000cppcheck-1.90/gui/test/data/files/bar1000066400000000000000000000000211357737443600175650ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/bar1.foo000066400000000000000000000000211357737443600203470ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/dir1/000077500000000000000000000000001357737443600176635ustar00rootroot00000000000000cppcheck-1.90/gui/test/data/files/dir1/dir11/000077500000000000000000000000001357737443600206035ustar00rootroot00000000000000cppcheck-1.90/gui/test/data/files/dir1/dir11/foo11.cpp000066400000000000000000000000211357737443600222250ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/dir1/foo1.cpp000066400000000000000000000000211357737443600212240ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/dir2/000077500000000000000000000000001357737443600176645ustar00rootroot00000000000000cppcheck-1.90/gui/test/data/files/dir2/foo1.cpp000066400000000000000000000000211357737443600212250ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/foo1.cpp000066400000000000000000000000211357737443600203650ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/foo2.cxx000066400000000000000000000000211357737443600204060ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/foo3.cc000066400000000000000000000000211357737443600201720ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/foo4.c000066400000000000000000000000211357737443600200300ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/foo5.c++000066400000000000000000000000211357737443600201570ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/foo6.txx000066400000000000000000000000211357737443600204330ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/files/foo7.tpp000066400000000000000000000000211357737443600204140ustar00rootroot00000000000000Dummy test file. cppcheck-1.90/gui/test/data/projectfiles/000077500000000000000000000000001357737443600204135ustar00rootroot00000000000000cppcheck-1.90/gui/test/data/projectfiles/simple.cppcheck000066400000000000000000000006031357737443600234050ustar00rootroot00000000000000 cppcheck-1.90/gui/test/data/projectfiles/simple_ignore.cppcheck000066400000000000000000000006011357737443600247460ustar00rootroot00000000000000 cppcheck-1.90/gui/test/data/projectfiles/simple_noroot.cppcheck000066400000000000000000000005501357737443600250060ustar00rootroot00000000000000 cppcheck-1.90/gui/test/data/xmlfiles/000077500000000000000000000000001357737443600175455ustar00rootroot00000000000000cppcheck-1.90/gui/test/data/xmlfiles/xmlreport_v2.xml000066400000000000000000000040141357737443600227310ustar00rootroot00000000000000 cppcheck-1.90/gui/test/filelist/000077500000000000000000000000001357737443600166245ustar00rootroot00000000000000cppcheck-1.90/gui/test/filelist/filelist.pro000066400000000000000000000010331357737443600211560ustar00rootroot00000000000000TEMPLATE = app TARGET = test-filelist DEPENDPATH += . INCLUDEPATH += . ../../../externals/simplecpp OBJECTS_DIR = ../build MOC_DIR = ../build QT += testlib include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testfilelist.cpp \ ../../filelist.cpp \ ../../../lib/pathmatch.cpp \ ../../../lib/path.cpp \ ../../../externals/simplecpp/simplecpp.cpp HEADERS += testfilelist.h \ ../../filelist.h \ ../../../lib/pathmatch.h \ ../../../lib/path.h \ ../../../externals/simplecpp/simplecpp.h cppcheck-1.90/gui/test/filelist/testfilelist.cpp000066400000000000000000000142171357737443600220500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "testfilelist.h" #include "filelist.h" void TestFileList::addFile() { // Accepted extensions: *.cpp, *.cxx, *.cc, *.c, *.c++, *.txx, *.tpp" FileList list; list.addFile(QString(SRCDIR) + "/../data/files/foo1.cpp"); list.addFile(QString(SRCDIR) + "/../data/files/foo2.cxx"); list.addFile(QString(SRCDIR) + "/../data/files/foo3.cc"); list.addFile(QString(SRCDIR) + "/../data/files/foo4.c"); list.addFile(QString(SRCDIR) + "/../data/files/foo5.c++"); list.addFile(QString(SRCDIR) + "/../data/files/foo6.txx"); list.addFile(QString(SRCDIR) + "/../data/files/foo7.tpp"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 7); } void TestFileList::addPathList() { // Accepted extensions: *.cpp, *.cxx, *.cc, *.c, *.c++, *.txx, *.tpp" QStringList paths; paths << QString(SRCDIR) + "/../data/files/foo1.cpp"; paths << QString(SRCDIR) + "/../data/files/foo2.cxx"; paths << QString(SRCDIR) + "/../data/files/foo3.cc"; paths << QString(SRCDIR) + "/../data/files/foo4.c"; paths << QString(SRCDIR) + "/../data/files/foo5.c++"; paths << QString(SRCDIR) + "/../data/files/foo6.txx"; paths << QString(SRCDIR) + "/../data/files/foo7.tpp"; FileList list; list.addPathList(paths); QStringList files = list.getFileList(); QCOMPARE(files.size(), 7); } void TestFileList::addFile_notexist() { FileList list; list.addFile(QString(SRCDIR) + "/../data/files/bar1.cpp"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 0); } void TestFileList::addFile_unknown() { FileList list; list.addFile(QString(SRCDIR) + "/../data/files/bar1"); list.addFile(QString(SRCDIR) + "/../data/files/bar1.foo"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 0); } void TestFileList::addDirectory() { FileList list; list.addDirectory(QString(SRCDIR) + "/../data/files"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 7); } void TestFileList::addDirectory_recursive() { FileList list; list.addDirectory(QString(SRCDIR) + "/../data/files", true); QStringList files = list.getFileList(); QCOMPARE(files.size(), 10); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(files.contains(base + "/dir1/foo1.cpp")); QVERIFY(files.contains(base + "/dir1/dir11/foo11.cpp")); QVERIFY(files.contains(base + "/dir2/foo1.cpp")); } void TestFileList::filterFiles() { FileList list; QStringList filters; filters << "foo1.cpp" << "foo3.cc"; list.addExcludeList(filters); list.addFile(QString(SRCDIR) + "/../data/files/foo1.cpp"); list.addFile(QString(SRCDIR) + "/../data/files/foo2.cxx"); list.addFile(QString(SRCDIR) + "/../data/files/foo3.cc"); list.addFile(QString(SRCDIR) + "/../data/files/foo4.c"); list.addFile(QString(SRCDIR) + "/../data/files/foo5.c++"); list.addFile(QString(SRCDIR) + "/../data/files/foo6.txx"); list.addFile(QString(SRCDIR) + "/../data/files/foo7.tpp"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 5); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(! files.contains(base + "/foo1.cpp")); QVERIFY(! files.contains(base + "/foo3.cpp")); } void TestFileList::filterFiles2() { FileList list; QStringList filters; filters << "foo1.cpp" << "foo3.cc"; list.addExcludeList(filters); list.addDirectory(QString(SRCDIR) + "/../data/files"); QStringList files = list.getFileList(); QCOMPARE(files.size(), 5); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(! files.contains(base + "/foo1.cpp")); QVERIFY(! files.contains(base + "/foo3.cpp")); } void TestFileList::filterFiles3() { FileList list; QStringList filters; filters << "foo1.cpp" << "foo3.cc"; list.addExcludeList(filters); list.addDirectory(QString(SRCDIR) + "/../data/files", true); QStringList files = list.getFileList(); QCOMPARE(files.size(), 6); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(! files.contains(base + "/foo1.cpp")); QVERIFY(! files.contains(base + "/foo3.cpp")); QVERIFY(! files.contains(base + "/dir1/foo1.cpp")); QVERIFY(! files.contains(base + "/dir2/foo1.cpp")); } void TestFileList::filterFiles4() { FileList list; QStringList filters; filters << "dir1/"; list.addExcludeList(filters); list.addDirectory(QString(SRCDIR) + "/../data/files", true); QStringList files = list.getFileList(); QCOMPARE(files.size(), 8); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(! files.contains(base + "/dir1/foo1.cpp")); QVERIFY(! files.contains(base + "/dir1/dir11/foo11.cpp")); } /* void TestFileList::filterFiles5() { FileList list; QStringList filters; filters << QDir(QString(SRCDIR) + "/../data/files/dir1/").absolutePath() + "/"; list.addExcludeList(filters); list.addDirectory(QString(SRCDIR) + "/../data/files", true); QStringList files = list.getFileList(); QCOMPARE(files.size(), 8); QDir dir(QString(SRCDIR) + "/../data/files"); QString base = dir.canonicalPath(); QVERIFY(! files.contains(base + "/dir1/foo1.cpp")); QVERIFY(! files.contains(base + "/dir1/dir11/foo11.cpp")); } */ QTEST_MAIN(TestFileList) cppcheck-1.90/gui/test/filelist/testfilelist.h000066400000000000000000000021451357737443600215120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include class TestFileList: public QObject { Q_OBJECT private slots: void addFile(); void addPathList(); void addFile_notexist(); void addFile_unknown(); void addDirectory(); void addDirectory_recursive(); void filterFiles(); void filterFiles2(); void filterFiles3(); void filterFiles4(); }; cppcheck-1.90/gui/test/projectfile/000077500000000000000000000000001357737443600173175ustar00rootroot00000000000000cppcheck-1.90/gui/test/projectfile/projectfile.pro000066400000000000000000000010031357737443600223410ustar00rootroot00000000000000TEMPLATE = app TARGET = test-projectfile DEPENDPATH += . INCLUDEPATH += . ../../../externals/simplecpp OBJECTS_DIR = ../build MOC_DIR = ../build QT -= gui QT += core CONFIG += console include(../common.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testprojectfile.cpp \ ../../projectfile.cpp \ ../../../lib/path.cpp \ ../../../externals/simplecpp/simplecpp.cpp HEADERS += testprojectfile.h \ ../../projectfile.h \ ../../../lib/path.h \ ../../../externals/simplecpp/simplecpp.h cppcheck-1.90/gui/test/projectfile/testprojectfile.cpp000066400000000000000000000076711357737443600232440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "testprojectfile.h" #include "projectfile.h" #include "settings.h" // Mock... const char Settings::SafeChecks::XmlRootName[] = "safe-checks"; const char Settings::SafeChecks::XmlClasses[] = "class-public"; const char Settings::SafeChecks::XmlExternalFunctions[] = "external-functions"; const char Settings::SafeChecks::XmlInternalFunctions[] = "internal-functions"; const char Settings::SafeChecks::XmlExternalVariables[] = "external-variables"; void TestProjectFile::loadInexisting() { const QString filepath(QString(SRCDIR) + "/../data/projectfiles/foo.cppcheck"); ProjectFile pfile(filepath); QCOMPARE(pfile.read(), false); } void TestProjectFile::loadSimple() { const QString filepath(QString(SRCDIR) + "/../data/projectfiles/simple.cppcheck"); ProjectFile pfile(filepath); QVERIFY(pfile.read()); QCOMPARE(pfile.getRootPath(), QString("../..")); QStringList includes = pfile.getIncludeDirs(); QCOMPARE(includes.size(), 2); QCOMPARE(includes[0], QString("lib/")); QCOMPARE(includes[1], QString("cli/")); QStringList paths = pfile.getCheckPaths(); QCOMPARE(paths.size(), 2); QCOMPARE(paths[0], QString("gui/")); QCOMPARE(paths[1], QString("test/")); QStringList excludes = pfile.getExcludedPaths(); QCOMPARE(excludes.size(), 1); QCOMPARE(excludes[0], QString("gui/temp/")); QStringList defines = pfile.getDefines(); QCOMPARE(defines.size(), 1); QCOMPARE(defines[0], QString("FOO")); } // Test that project file with old 'ignore' element works void TestProjectFile::loadSimpleWithIgnore() { const QString filepath(QString(SRCDIR) + "/../data/projectfiles/simple_ignore.cppcheck"); ProjectFile pfile(filepath); QVERIFY(pfile.read()); QCOMPARE(pfile.getRootPath(), QString("../..")); QStringList includes = pfile.getIncludeDirs(); QCOMPARE(includes.size(), 2); QCOMPARE(includes[0], QString("lib/")); QCOMPARE(includes[1], QString("cli/")); QStringList paths = pfile.getCheckPaths(); QCOMPARE(paths.size(), 2); QCOMPARE(paths[0], QString("gui/")); QCOMPARE(paths[1], QString("test/")); QStringList excludes = pfile.getExcludedPaths(); QCOMPARE(excludes.size(), 1); QCOMPARE(excludes[0], QString("gui/temp/")); QStringList defines = pfile.getDefines(); QCOMPARE(defines.size(), 1); QCOMPARE(defines[0], QString("FOO")); } void TestProjectFile::loadSimpleNoroot() { const QString filepath(QString(SRCDIR) + "/../data/projectfiles/simple_noroot.cppcheck"); ProjectFile pfile(filepath); QVERIFY(pfile.read()); QCOMPARE(pfile.getRootPath(), QString()); QStringList includes = pfile.getIncludeDirs(); QCOMPARE(includes.size(), 2); QCOMPARE(includes[0], QString("lib/")); QCOMPARE(includes[1], QString("cli/")); QStringList paths = pfile.getCheckPaths(); QCOMPARE(paths.size(), 2); QCOMPARE(paths[0], QString("gui/")); QCOMPARE(paths[1], QString("test/")); QStringList excludes = pfile.getExcludedPaths(); QCOMPARE(excludes.size(), 1); QCOMPARE(excludes[0], QString("gui/temp/")); QStringList defines = pfile.getDefines(); QCOMPARE(defines.size(), 1); QCOMPARE(defines[0], QString("FOO")); } QTEST_MAIN(TestProjectFile) cppcheck-1.90/gui/test/projectfile/testprojectfile.h000066400000000000000000000017241357737443600227020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include class TestProjectFile: public QObject { Q_OBJECT private slots: void loadInexisting(); void loadSimple(); void loadSimpleWithIgnore(); void loadSimpleNoroot(); }; cppcheck-1.90/gui/test/readme.txt000066400000000000000000000013071357737443600170100ustar00rootroot00000000000000GUI tests + benchmark tests =========================== As the GUI uses Qt framework, the GUI tests also use Qt's Testlib. This is totally different test framework than lib/cli is using. By principle each testcase is compiled as an own runnable binary. Compiling --------- To compile all the tests run in root directory of tests: - qmake ; make You can also (re)compile single test by CD:ing to the directory where test (source) resides and running: - qmake ; make Running ------- As each test is compiled as single executable binary you can run the test just by running the executable. You can get from http://bitbucket.org/kimmov/testrun a script which runs all the tests and collects the results. cppcheck-1.90/gui/test/test.pro000066400000000000000000000002661357737443600165160ustar00rootroot00000000000000#lessThan(QT_MAJOR_VERSION, 5): error(requires >= Qt 5 (You used: $$QT_VERSION)) CONFIG += ordered TEMPLATE = subdirs SUBDIRS = \ filelist \ projectfile \ xmlreportv2 cppcheck-1.90/gui/test/translationhandler/000077500000000000000000000000001357737443600207055ustar00rootroot00000000000000cppcheck-1.90/gui/test/translationhandler/testtranslationhandler.cpp000066400000000000000000000020761357737443600262120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "testtranslationhandler.h" #include "translationhandler.h" void TestTranslationHandler::construct() { TranslationHandler handler; QCOMPARE(handler.getNames().size(), 13); // 12 translations + english QCOMPARE(handler.getCurrentLanguage(), QString("en")); } QTEST_MAIN(TestTranslationHandler) cppcheck-1.90/gui/test/translationhandler/testtranslationhandler.h000066400000000000000000000016011357737443600256500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include class TestTranslationHandler: public QObject { Q_OBJECT private slots: void construct(); }; cppcheck-1.90/gui/test/translationhandler/translationhandler.pro000066400000000000000000000003651357737443600253270ustar00rootroot00000000000000TEMPLATE = app TARGET = test-translationhandler DEPENDPATH += . INCLUDEPATH += . OBJECTS_DIR = ../build MOC_DIR = ../build QT += widgets include(../common.pri) # tests SOURCES += testtranslationhandler.cpp HEADERS += testtranslationhandler.h cppcheck-1.90/gui/test/xmlreportv2/000077500000000000000000000000001357737443600173155ustar00rootroot00000000000000cppcheck-1.90/gui/test/xmlreportv2/testxmlreportv2.cpp000066400000000000000000000043521357737443600232310ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "testxmlreportv2.h" #include "xmlreportv2.h" #include "erroritem.h" #include "errorlogger.h" void TestXmlReportV2::readXml() { const QString filepath(QString(SRCDIR) + "/../data/xmlfiles/xmlreport_v2.xml"); XmlReportV2 report(filepath); QVERIFY(report.open()); QList errors = report.read(); QCOMPARE(errors.size(), 6); const ErrorItem &item = errors[0]; QCOMPARE(item.errorPath.size(), 1); QCOMPARE(item.errorPath[0].file, QString("test.cxx")); QCOMPARE(item.errorPath[0].line, (unsigned int)11); QCOMPARE(item.errorId, QString("unreadVariable")); QCOMPARE(GuiSeverity::toString(item.severity), QString("style")); QCOMPARE(item.summary, QString("Variable 'a' is assigned a value that is never used")); QCOMPARE(item.message, QString("Variable 'a' is assigned a value that is never used")); const ErrorItem &item2 = errors[3]; QCOMPARE(item2.errorPath.size(), 2); QCOMPARE(item2.errorPath[0].file, QString("test.cxx")); QCOMPARE(item2.errorPath[0].line, (unsigned int)16); QCOMPARE(item2.errorPath[1].file, QString("test.cxx")); QCOMPARE(item2.errorPath[1].line, (unsigned int)32); QCOMPARE(item2.errorId, QString("mismatchAllocDealloc")); QCOMPARE(GuiSeverity::toString(item2.severity), QString("error")); QCOMPARE(item2.summary, QString("Mismatching allocation and deallocation: k")); QCOMPARE(item2.message, QString("Mismatching allocation and deallocation: k")); } QTEST_MAIN(TestXmlReportV2) cppcheck-1.90/gui/test/xmlreportv2/testxmlreportv2.h000066400000000000000000000015701357737443600226750ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include class TestXmlReportV2: public QObject { Q_OBJECT private slots: void readXml(); }; cppcheck-1.90/gui/test/xmlreportv2/xmlreportv2.pro000066400000000000000000000010211357737443600223350ustar00rootroot00000000000000TEMPLATE = app TARGET = test-xmlreportv2 DEPENDPATH += . INCLUDEPATH += . ../../../externals/simplecpp ../../../externals/tinyxml OBJECTS_DIR = ../build MOC_DIR = ../build include(../common.pri) include(../../../lib/lib.pri) DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testxmlreportv2.cpp \ ../../erroritem.cpp \ ../../report.cpp \ ../../xmlreport.cpp \ ../../xmlreportv2.cpp HEADERS += testxmlreportv2.h \ ../../erroritem.h \ ../../report.h \ ../../xmlreport.cpp \ ../../xmlreportv2.h cppcheck-1.90/gui/threadhandler.cpp000066400000000000000000000162401357737443600173460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "common.h" #include "settings.h" #include "checkthread.h" #include "threadhandler.h" #include "resultsview.h" ThreadHandler::ThreadHandler(QObject *parent) : QObject(parent), mScanDuration(0), mRunningThreadCount(0), mAnalyseWholeProgram(false) { setThreadCount(1); } ThreadHandler::~ThreadHandler() { removeThreads(); } void ThreadHandler::clearFiles() { mLastFiles.clear(); mResults.clearFiles(); mAnalyseWholeProgram = false; mAddonsAndTools.clear(); mSuppressions.clear(); } void ThreadHandler::setFiles(const QStringList &files) { mResults.setFiles(files); mLastFiles = files; } void ThreadHandler::setProject(const ImportProject &prj) { mResults.setProject(prj); mLastFiles.clear(); } void ThreadHandler::setCheckFiles(bool all) { if (mRunningThreadCount == 0) { mResults.setFiles(getReCheckFiles(all)); } } void ThreadHandler::setCheckFiles(QStringList files) { if (mRunningThreadCount == 0) { mResults.setFiles(files); } } void ThreadHandler::check(const Settings &settings) { if (mResults.getFileCount() == 0 || mRunningThreadCount > 0 || settings.jobs == 0) { qDebug() << "Can't start checking if there's no files to check or if check is in progress."; emit done(); return; } setThreadCount(settings.jobs); mRunningThreadCount = mThreads.size(); if (mResults.getFileCount() < mRunningThreadCount) { mRunningThreadCount = mResults.getFileCount(); } for (int i = 0; i < mRunningThreadCount; i++) { mThreads[i]->setAddonsAndTools(mAddonsAndTools); mThreads[i]->setMisraFile(mMisraFile); mThreads[i]->setSuppressions(mSuppressions); mThreads[i]->setClangIncludePaths(mClangIncludePaths); mThreads[i]->setDataDir(mDataDir); mThreads[i]->check(settings); } // Date and time when checking starts.. mCheckStartTime = QDateTime::currentDateTime(); mAnalyseWholeProgram = true; mTime.start(); } bool ThreadHandler::isChecking() const { return mRunningThreadCount > 0; } void ThreadHandler::setThreadCount(const int count) { if (mRunningThreadCount > 0 || count == mThreads.size() || count <= 0) { return; } //Remove unused old threads removeThreads(); //Create new threads for (int i = mThreads.size(); i < count; i++) { mThreads << new CheckThread(mResults); connect(mThreads.last(), &CheckThread::done, this, &ThreadHandler::threadDone); connect(mThreads.last(), &CheckThread::fileChecked, &mResults, &ThreadResult::fileChecked); } } void ThreadHandler::removeThreads() { for (int i = 0; i < mThreads.size(); i++) { mThreads[i]->terminate(); disconnect(mThreads[i], &CheckThread::done, this, &ThreadHandler::threadDone); disconnect(mThreads[i], &CheckThread::fileChecked, &mResults, &ThreadResult::fileChecked); delete mThreads[i]; } mThreads.clear(); mAnalyseWholeProgram = false; } void ThreadHandler::threadDone() { if (mRunningThreadCount == 1 && mAnalyseWholeProgram) { mThreads[0]->analyseWholeProgram(mLastFiles); mAnalyseWholeProgram = false; return; } mRunningThreadCount--; if (mRunningThreadCount == 0) { emit done(); mScanDuration = mTime.elapsed(); // Set date/time used by the recheck if (!mCheckStartTime.isNull()) { mLastCheckTime = mCheckStartTime; mCheckStartTime = QDateTime(); } } } void ThreadHandler::stop() { mCheckStartTime = QDateTime(); mAnalyseWholeProgram = false; for (int i = 0; i < mThreads.size(); i++) { mThreads[i]->stop(); } } void ThreadHandler::initialize(ResultsView *view) { connect(&mResults, &ThreadResult::progress, view, &ResultsView::progress); connect(&mResults, &ThreadResult::error, view, &ResultsView::error); connect(&mResults, &ThreadResult::log, this, &ThreadHandler::log); connect(&mResults, &ThreadResult::debugError, this, &ThreadHandler::debugError); } void ThreadHandler::loadSettings(QSettings &settings) { setThreadCount(settings.value(SETTINGS_CHECK_THREADS, 1).toInt()); } void ThreadHandler::saveSettings(QSettings &settings) const { settings.setValue(SETTINGS_CHECK_THREADS, mThreads.size()); } bool ThreadHandler::hasPreviousFiles() const { return !mLastFiles.isEmpty(); } int ThreadHandler::getPreviousFilesCount() const { return mLastFiles.size(); } int ThreadHandler::getPreviousScanDuration() const { return mScanDuration; } QStringList ThreadHandler::getReCheckFiles(bool all) const { if (mLastCheckTime.isNull() || all) return mLastFiles; std::set modified; std::set unmodified; QStringList files; for (int i = 0; i < mLastFiles.size(); ++i) { if (needsReCheck(mLastFiles[i], modified, unmodified)) files.push_back(mLastFiles[i]); } return files; } bool ThreadHandler::needsReCheck(const QString &filename, std::set &modified, std::set &unmodified) const { if (modified.find(filename) != modified.end()) return true; if (unmodified.find(filename) != unmodified.end()) return false; if (QFileInfo(filename).lastModified() > mLastCheckTime) { return true; } // Parse included files recursively QFile f(filename); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return false; // prevent recursion.. unmodified.insert(filename); QTextStream in(&f); while (!in.atEnd()) { QString line = in.readLine(); if (line.startsWith("#include \"")) { line.remove(0,10); int i = line.indexOf("\""); if (i > 0) { line.remove(i,line.length()); line = QFileInfo(filename).absolutePath() + "/" + line; if (needsReCheck(line, modified, unmodified)) { modified.insert(line); return true; } } } } return false; } QDateTime ThreadHandler::getCheckStartTime() const { return mCheckStartTime; } void ThreadHandler::setCheckStartTime(QDateTime checkStartTime) { mCheckStartTime = checkStartTime; } cppcheck-1.90/gui/threadhandler.h000066400000000000000000000137631357737443600170220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef THREADHANDLER_H #define THREADHANDLER_H #include #include #include #include #include "threadresult.h" #include "importproject.h" #include "suppressions.h" class ResultsView; class CheckThread; class QSettings; class Settings; /// @addtogroup GUI /// @{ /** * @brief This class handles creating threadresult and starting threads * */ class ThreadHandler : public QObject { Q_OBJECT public: explicit ThreadHandler(QObject *parent = nullptr); virtual ~ThreadHandler(); /** * @brief Set the number of threads to use * @param count The number of threads to use */ void setThreadCount(const int count); /** * @brief Initialize the threads (connect all signals to resultsview's slots) * * @param view View to show error results */ void initialize(ResultsView *view); /** * @brief Load settings * @param settings QSettings to load settings from */ void loadSettings(QSettings &settings); /** * @brief Save settings * @param settings QSettings to save settings to */ void saveSettings(QSettings &settings) const; void setAddonsAndTools(const QStringList &addonsAndTools, const QString &misraFile) { mAddonsAndTools = addonsAndTools; mMisraFile = misraFile; } void setSuppressions(const QList &s) { mSuppressions = s; } void setClangIncludePaths(const QStringList &s) { mClangIncludePaths = s; } void setDataDir(const QString &dataDir) { mDataDir = dataDir; } /** * @brief Clear all files from cppcheck * */ void clearFiles(); /** * @brief Set files to check * * @param files files to check */ void setFiles(const QStringList &files); /** * @brief Set project to check * * @param prj project to check */ void setProject(const ImportProject &prj); /** * @brief Start the threads to check the files * * @param settings Settings for checking */ void check(const Settings &settings); /** * @brief Set files to check * * @param all true if all files, false if modified files */ void setCheckFiles(bool all); /** * @brief Set selected files to check * * @param files list of files to be checked */ void setCheckFiles(QStringList files); /** * @brief Is checking running? * * @return true if check is running, false otherwise. */ bool isChecking() const; /** * @brief Have we checked files already? * * @return true check has been previously run and recheck can be done */ bool hasPreviousFiles() const; /** * @brief Return count of files we checked last time. * * @return count of files that were checked last time. */ int getPreviousFilesCount() const; /** * @brief Return the time elapsed while scanning the previous time. * * @return the time elapsed in milliseconds. */ int getPreviousScanDuration() const; /** * @brief Get files that should be rechecked because they have been * changed. */ QStringList getReCheckFiles(bool all) const; /** * @brief Get start time of last check * * @return start time of last check */ QDateTime getCheckStartTime() const; /** * @brief Set start time of check * * @param checkStartTime saved start time of the last check */ void setCheckStartTime(QDateTime checkStartTime); signals: /** * @brief Signal that all threads are done * */ void done(); void log(const QString &msg); void debugError(const ErrorItem &item); public slots: /** * @brief Slot to stop all threads * */ void stop(); protected slots: /** * @brief Slot that a single thread is done * */ void threadDone(); protected: /** * @brief List of files checked last time (used when rechecking) * */ QStringList mLastFiles; /** @brief date and time when current checking started */ QDateTime mCheckStartTime; /** * @brief when was the files checked the last time (used when rechecking) */ QDateTime mLastCheckTime; /** * @brief Timer used for measuring scan duration * */ QTime mTime; /** * @brief The previous scan duration in milliseconds. * */ int mScanDuration; /** * @brief Function to delete all threads * */ void removeThreads(); /** * @brief Thread results are stored here * */ ThreadResult mResults; /** * @brief List of threads currently in use * */ QList mThreads; /** * @brief The amount of threads currently running * */ int mRunningThreadCount; bool mAnalyseWholeProgram; QStringList mAddonsAndTools; QList mSuppressions; QStringList mClangIncludePaths; QString mDataDir; QString mMisraFile; private: /** * @brief Check if a file needs to be rechecked. Recursively checks * included headers. Used by GetReCheckFiles() */ bool needsReCheck(const QString &filename, std::set &modified, std::set &unmodified) const; }; /// @} #endif // THREADHANDLER_H cppcheck-1.90/gui/threadresult.cpp000066400000000000000000000071231357737443600172470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "common.h" #include "erroritem.h" #include "errorlogger.h" #include "threadresult.h" ThreadResult::ThreadResult() : mMaxProgress(0), mProgress(0), mFilesChecked(0), mTotalFiles(0) { //ctor } ThreadResult::~ThreadResult() { //dtor } void ThreadResult::reportOut(const std::string &outmsg) { emit log(QString::fromStdString(outmsg)); } void ThreadResult::fileChecked(const QString &file) { QMutexLocker locker(&mutex); mProgress += QFile(file).size(); mFilesChecked ++; if (mMaxProgress > 0) { const int value = static_cast(PROGRESS_MAX * mProgress / mMaxProgress); const QString description = tr("%1 of %2 files checked").arg(mFilesChecked).arg(mTotalFiles); emit progress(value, description); } } void ThreadResult::reportErr(const ErrorLogger::ErrorMessage &msg) { QMutexLocker locker(&mutex); const ErrorItem item(msg); if (msg.severity != Severity::debug) emit error(item); else emit debugError(item); } QString ThreadResult::getNextFile() { QMutexLocker locker(&mutex); if (mFiles.isEmpty()) { return QString(); } return mFiles.takeFirst(); } ImportProject::FileSettings ThreadResult::getNextFileSettings() { QMutexLocker locker(&mutex); if (mFileSettings.empty()) { return ImportProject::FileSettings(); } const ImportProject::FileSettings fs = mFileSettings.front(); mFileSettings.pop_front(); return fs; } void ThreadResult::setFiles(const QStringList &files) { QMutexLocker locker(&mutex); mFiles = files; mProgress = 0; mFilesChecked = 0; mTotalFiles = files.size(); // Determine the total size of all of the files to check, so that we can // show an accurate progress estimate quint64 sizeOfFiles = 0; foreach (const QString& file, files) { sizeOfFiles += QFile(file).size(); } mMaxProgress = sizeOfFiles; } void ThreadResult::setProject(const ImportProject &prj) { QMutexLocker locker(&mutex); mFiles.clear(); mFileSettings = prj.fileSettings; mProgress = 0; mFilesChecked = 0; mTotalFiles = prj.fileSettings.size(); // Determine the total size of all of the files to check, so that we can // show an accurate progress estimate quint64 sizeOfFiles = 0; foreach (const ImportProject::FileSettings& fs, prj.fileSettings) { sizeOfFiles += QFile(QString::fromStdString(fs.filename)).size(); } mMaxProgress = sizeOfFiles; } void ThreadResult::clearFiles() { QMutexLocker locker(&mutex); mFiles.clear(); mFileSettings.clear(); mFilesChecked = 0; mTotalFiles = 0; } int ThreadResult::getFileCount() const { QMutexLocker locker(&mutex); return mFiles.size() + mFileSettings.size(); } cppcheck-1.90/gui/threadresult.h000066400000000000000000000062711357737443600167170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef THREADRESULT_H #define THREADRESULT_H #include #include #include #include "errorlogger.h" #include "importproject.h" class ErrorItem; /// @addtogroup GUI /// @{ /** * @brief Threads use this class to obtain new files to process and to publish results * */ class ThreadResult : public QObject, public ErrorLogger { Q_OBJECT public: ThreadResult(); virtual ~ThreadResult(); /** * @brief Get next unprocessed file * @return File path */ QString getNextFile(); ImportProject::FileSettings getNextFileSettings(); /** * @brief Set list of files to check * @param files List of files to check */ void setFiles(const QStringList &files); void setProject(const ImportProject &prj); /** * @brief Clear files to check * */ void clearFiles(); /** * @brief Get the number of files to check * */ int getFileCount() const; /** * ErrorLogger methods */ void reportOut(const std::string &outmsg) override; void reportErr(const ErrorLogger::ErrorMessage &msg) override; public slots: /** * @brief Slot threads use to signal this class that a specific file is checked * @param file File that is checked */ void fileChecked(const QString &file); signals: /** * @brief Progress signal * @param value Current progress * @param description Description of the current stage */ void progress(int value, const QString& description); /** * @brief Signal of a new error * * @param item Error data */ void error(const ErrorItem &item); /** * @brief Signal of a new log message * * @param logline Log line */ void log(const QString &logline); /** * @brief Signal of a debug error * * @param item Error data */ void debugError(const ErrorItem &item); protected: /** * @brief Mutex * */ mutable QMutex mutex; /** * @brief List of files to check * */ QStringList mFiles; std::list mFileSettings; /** * @brief Max progress * */ quint64 mMaxProgress; /** * @brief Current progress * */ quint64 mProgress; /** * @brief Current number of files checked * */ unsigned long mFilesChecked; /** * @brief Total number of files * */ unsigned long mTotalFiles; }; /// @} #endif // THREADRESULT_H cppcheck-1.90/gui/translationhandler.cpp000066400000000000000000000154731357737443600204440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "translationhandler.h" // Provide own translations for standard buttons. This (garbage) code is needed to enforce them to appear in .ts files even after "lupdate gui.pro" static void unused() { // NOTE: Keeping semi-colons at end of macro for style preference #if ((QT_VERSION >= 0x040000)&&(QT_VERSION < 0x050000)) Q_UNUSED(QT_TRANSLATE_NOOP("QDialogButtonBox", "OK")); Q_UNUSED(QT_TRANSLATE_NOOP("QDialogButtonBox", "Cancel")); Q_UNUSED(QT_TRANSLATE_NOOP("QDialogButtonBox", "Close")); Q_UNUSED(QT_TRANSLATE_NOOP("QDialogButtonBox", "Save")); #elif ((QT_VERSION >= 0x050000)&&(QT_VERSION < 0x060000)) Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "OK")); Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel")); Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Close")); Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Save")); #else #error Unsupported Qt version. #endif } TranslationHandler::TranslationHandler(QObject *parent) : QObject(parent), mCurrentLanguage("en"), mTranslator(nullptr) { // Add our available languages // Keep this list sorted addTranslation("Chinese (Simplified)", "cppcheck_zh_CN"); addTranslation("Dutch", "cppcheck_nl"); addTranslation("English", "cppcheck_en"); addTranslation("Finnish", "cppcheck_fi"); addTranslation("French", "cppcheck_fr"); addTranslation("German", "cppcheck_de"); addTranslation("Italian", "cppcheck_it"); addTranslation("Japanese", "cppcheck_ja"); addTranslation("Korean", "cppcheck_ko"); addTranslation("Russian", "cppcheck_ru"); addTranslation("Serbian", "cppcheck_sr"); addTranslation("Spanish", "cppcheck_es"); addTranslation("Swedish", "cppcheck_sv"); } TranslationHandler::~TranslationHandler() { } const QStringList TranslationHandler::getNames() const { QStringList names; foreach (TranslationInfo translation, mTranslations) { names.append(translation.mName); } return names; } bool TranslationHandler::setLanguage(const QString &code) { bool failure = false; QString error; //If English is the language if (code == "en") { //Just remove all extra translators if (mTranslator) { qApp->removeTranslator(mTranslator); delete mTranslator; mTranslator = nullptr; } mCurrentLanguage = code; return true; } //Make sure the translator is otherwise valid int index = getLanguageIndexByCode(code); if (index == -1) { error = QObject::tr("Unknown language specified!"); failure = true; } else { // Make sure there is a translator if (!mTranslator && !failure) mTranslator = new QTranslator(this); //Load the new language const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); QSettings settings; QString datadir = settings.value("DATADIR").toString(); if (datadir.isEmpty()) datadir = appPath; QString translationFile; if (QFile::exists(datadir + "/lang/" + mTranslations[index].mFilename + ".qm")) translationFile = datadir + "/lang/" + mTranslations[index].mFilename + ".qm"; else if (QFile::exists(datadir + "/" + mTranslations[index].mFilename + ".qm")) translationFile = datadir + "/" + mTranslations[index].mFilename + ".qm"; else translationFile = appPath + "/" + mTranslations[index].mFilename + ".qm"; if (!mTranslator->load(translationFile) && !failure) { //If it failed, lets check if the default file exists if (!QFile::exists(translationFile)) { error = QObject::tr("Language file %1 not found!"); error = error.arg(translationFile); failure = true; } //If file exists, there's something wrong with it error = QObject::tr("Failed to load translation for language %1 from file %2"); error = error.arg(mTranslations[index].mName); error = error.arg(translationFile); } } if (failure) { const QString msg(tr("Failed to change the user interface language:" "\n\n%1\n\n" "The user interface language has been reset to English. Open " "the Preferences-dialog to select any of the available " "languages.").arg(error)); QMessageBox msgBox(QMessageBox::Warning, tr("Cppcheck"), msg, QMessageBox::Ok); msgBox.exec(); return false; } qApp->installTranslator(mTranslator); mCurrentLanguage = code; return true; } QString TranslationHandler::getCurrentLanguage() const { return mCurrentLanguage; } QString TranslationHandler::suggestLanguage() const { //Get language from system locale's name ie sv_SE or zh_CN QString language = QLocale::system().name(); //qDebug()<<"Your language is"<. */ #ifndef TRANSLATIONHANDLER_H #define TRANSLATIONHANDLER_H #include #include #include #include /// @addtogroup GUI /// @{ /** * @brief Information for one translation. * */ struct TranslationInfo { /** * @brief Readable name for the translation (e.g. "English"). * */ QString mName; /** * @brief Filename for the translation. * */ QString mFilename; /** * @brief ISO 639 language code for the translation (e.g. "en"). * */ QString mCode; }; /** * @brief A class handling the available translations. * * This class contains a list of available translations. The class also keeps * track which translation is the currently active translation. * */ class TranslationHandler : QObject { Q_OBJECT public: explicit TranslationHandler(QObject *parent = nullptr); virtual ~TranslationHandler(); /** * @brief Get a list of available translation names. * @return List of available translation names. * */ const QStringList getNames() const; /** * @brief Get a list of available translations. * @return List of available translations. * */ QList getTranslations() const { return mTranslations; } /** * @brief Set active translation. * @param code ISO 639 language code for new selected translation. * @return true if succeeds, false otherwise. * */ bool setLanguage(const QString &code); /** * @brief Get currently selected translation. * @return ISO 639 language code for current translation. * */ QString getCurrentLanguage() const; /** * @brief Get translation suggestion for the system. * This function checks the current system locale and determines which of * the available translations is best as current translation. If none of * the available translations is good then it returns English ("en"). * @return Suggested translation ISO 639 language code. * */ QString suggestLanguage() const; protected: /** * @brief Add new translation to list of available translations. * @param name Name of the translation ("English"). * @param filename Filename of the translation. * */ void addTranslation(const char *name, const char *filename); /** * @brief Find language in the list and return its index. * @param code ISO 639 language code. * @return Index at list, or -1 if not found. * */ int getLanguageIndexByCode(const QString &code) const; private: /** * @brief ISO 639 language code of the currently selected translation. * */ QString mCurrentLanguage; /** * @brief List of available translations. * */ QList mTranslations; /** * @brief Translator class instance. * */ QTranslator *mTranslator; }; /// @} #endif // TRANSLATIONHANDLER_H cppcheck-1.90/gui/txtreport.cpp000066400000000000000000000040071357737443600166120ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "txtreport.h" TxtReport::TxtReport(const QString &filename) : Report(filename) { } TxtReport::~TxtReport() { } bool TxtReport::create() { if (Report::create()) { mTxtWriter.setDevice(Report::getFile()); return true; } return false; } void TxtReport::writeHeader() { // No header for txt report } void TxtReport::writeFooter() { // No footer for txt report } void TxtReport::writeError(const ErrorItem &error) { /* Error example from the core program in text [gui/test.cpp:23] -> [gui/test.cpp:14]: (error) Mismatching allocation and deallocation: k */ QString line; for (int i = 0; i < error.errorPath.size(); i++) { const QString file = QDir::toNativeSeparators(error.errorPath[i].file); line += QString("[%1:%2]").arg(file).arg(error.errorPath[i].line); if (i < error.errorPath.size() - 1) { line += " -> "; } if (i == error.errorPath.size() - 1) { line += ": "; } } QString temp = "(%1"; if (error.inconclusive) { temp += ", "; temp += tr("inconclusive"); } temp += ") "; line += temp.arg(GuiSeverity::toString(error.severity)); line += error.summary; mTxtWriter << line << endl; } cppcheck-1.90/gui/txtreport.h000066400000000000000000000033361357737443600162630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef TXT_REPORT_H #define TXT_REPORT_H #include #include #include "report.h" /// @addtogroup GUI /// @{ /** * @brief Text file report. * This report mimics the output of the command line cppcheck. */ class TxtReport : public Report { Q_OBJECT public: explicit TxtReport(const QString &filename); virtual ~TxtReport(); /** * @brief Create the report (file). * @return true if succeeded, false if file could not be created. */ virtual bool create() override; /** * @brief Write report header. */ virtual void writeHeader() override; /** * @brief Write report footer. */ virtual void writeFooter() override; /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error) override; private: /** * @brief Text stream writer for writing the report in text format. */ QTextStream mTxtWriter; }; /// @} #endif // TXT_REPORT_H cppcheck-1.90/gui/xmlreport.cpp000066400000000000000000000056541357737443600166040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "report.h" #include "xmlreport.h" static const char ResultElementName[] = "results"; static const char VersionAttribute[] = "version"; XmlReport::XmlReport(const QString &filename) : Report(filename) { } QString XmlReport::quoteMessage(const QString &message) { QString quotedMessage(message); quotedMessage.replace("&", "&"); quotedMessage.replace("\"", """); quotedMessage.replace("'", "'"); quotedMessage.replace("<", "<"); quotedMessage.replace(">", ">"); return quotedMessage; } QString XmlReport::unquoteMessage(const QString &message) { QString quotedMessage(message); quotedMessage.replace("&", "&"); quotedMessage.replace(""", "\""); quotedMessage.replace("'", "'"); quotedMessage.replace("<", "<"); quotedMessage.replace(">", ">"); return quotedMessage; } int XmlReport::determineVersion(const QString &filename) { QFile file; file.setFileName(filename); bool succeed = file.open(QIODevice::ReadOnly | QIODevice::Text); if (!succeed) return 0; QXmlStreamReader reader(&file); while (!reader.atEnd()) { switch (reader.readNext()) { case QXmlStreamReader::StartElement: if (reader.name() == ResultElementName) { QXmlStreamAttributes attribs = reader.attributes(); if (attribs.hasAttribute(QString(VersionAttribute))) { int ver = attribs.value(QString(), VersionAttribute).toString().toInt(); return ver; } else return 1; } break; // Not handled case QXmlStreamReader::EndElement: case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } return 0; } cppcheck-1.90/gui/xmlreport.h000066400000000000000000000034301357737443600162370ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2017 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef XML_REPORT_H #define XML_REPORT_H #include #include #include "report.h" #include "erroritem.h" class QObject; /// @addtogroup GUI /// @{ /** * @brief Base class for XML report classes. */ class XmlReport : public Report { public: explicit XmlReport(const QString &filename); /** * @brief Read contents of the report file. */ virtual QList read() = 0; /** * @brief Quote the message. * @param message Message to quote. * @return quoted message. */ static QString quoteMessage(const QString &message); /** * @brief Unquote the message. * @param message Message to quote. * @return quoted message. */ static QString unquoteMessage(const QString &message); /** * @brief Get the XML report format version from the file. * @param filename Filename of the report file. * @return XML report format version or 0 if error happened. */ static int determineVersion(const QString &filename); }; /// @} #endif // XML_REPORT_H cppcheck-1.90/gui/xmlreportv2.cpp000066400000000000000000000240171357737443600170460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "report.h" #include "erroritem.h" #include "xmlreport.h" #include "xmlreportv2.h" #include "cppcheck.h" static const QString ResultElementName = "results"; static const QString CppcheckElementName = "cppcheck"; static const QString ErrorElementName = "error"; static const QString ErrorsElementName = "errors"; static const QString LocationElementName = "location"; static const QString CWEAttribute = "cwe"; static const QString SinceDateAttribute = "sinceDate"; static const QString TagsAttribute = "tag"; static const QString FilenameAttribute = "file"; static const QString IncludedFromFilenameAttribute = "file0"; static const QString InconclusiveAttribute = "inconclusive"; static const QString InfoAttribute = "info"; static const QString LineAttribute = "line"; static const QString ColumnAttribute = "column"; static const QString IdAttribute = "id"; static const QString SeverityAttribute = "severity"; static const QString MsgAttribute = "msg"; static const QString VersionAttribute = "version"; static const QString VerboseAttribute = "verbose"; XmlReportV2::XmlReportV2(const QString &filename) : XmlReport(filename), mXmlReader(nullptr), mXmlWriter(nullptr) { } XmlReportV2::~XmlReportV2() { delete mXmlReader; delete mXmlWriter; } bool XmlReportV2::create() { if (Report::create()) { mXmlWriter = new QXmlStreamWriter(Report::getFile()); return true; } return false; } bool XmlReportV2::open() { if (Report::open()) { mXmlReader = new QXmlStreamReader(Report::getFile()); return true; } return false; } void XmlReportV2::writeHeader() { mXmlWriter->setAutoFormatting(true); mXmlWriter->writeStartDocument(); mXmlWriter->writeStartElement(ResultElementName); mXmlWriter->writeAttribute(VersionAttribute, QString::number(2)); mXmlWriter->writeStartElement(CppcheckElementName); mXmlWriter->writeAttribute(VersionAttribute, QString(CppCheck::version())); mXmlWriter->writeEndElement(); mXmlWriter->writeStartElement(ErrorsElementName); } void XmlReportV2::writeFooter() { mXmlWriter->writeEndElement(); // errors mXmlWriter->writeEndElement(); // results mXmlWriter->writeEndDocument(); } void XmlReportV2::writeError(const ErrorItem &error) { /* Error example from the core program in xml */ mXmlWriter->writeStartElement(ErrorElementName); mXmlWriter->writeAttribute(IdAttribute, error.errorId); // Don't localize severity so we can read these files mXmlWriter->writeAttribute(SeverityAttribute, GuiSeverity::toString(error.severity)); const QString summary = XmlReport::quoteMessage(error.summary); mXmlWriter->writeAttribute(MsgAttribute, summary); const QString message = XmlReport::quoteMessage(error.message); mXmlWriter->writeAttribute(VerboseAttribute, message); if (error.inconclusive) mXmlWriter->writeAttribute(InconclusiveAttribute, "true"); if (error.cwe > 0) mXmlWriter->writeAttribute(CWEAttribute, QString::number(error.cwe)); if (!error.sinceDate.isEmpty()) mXmlWriter->writeAttribute(SinceDateAttribute, error.sinceDate); if (!error.tags.isEmpty()) mXmlWriter->writeAttribute(TagsAttribute, error.tags); for (int i = error.errorPath.count() - 1; i >= 0; i--) { mXmlWriter->writeStartElement(LocationElementName); QString file = QDir::toNativeSeparators(error.errorPath[i].file); if (!error.file0.isEmpty() && file != error.file0) { mXmlWriter->writeAttribute(IncludedFromFilenameAttribute, quoteMessage(error.file0)); } mXmlWriter->writeAttribute(FilenameAttribute, XmlReport::quoteMessage(file)); mXmlWriter->writeAttribute(LineAttribute, QString::number(error.errorPath[i].line)); if (error.errorPath[i].column > 0) mXmlWriter->writeAttribute(ColumnAttribute, QString::number(error.errorPath[i].column)); if (error.errorPath.count() > 1) mXmlWriter->writeAttribute(InfoAttribute, XmlReport::quoteMessage(error.errorPath[i].info)); mXmlWriter->writeEndElement(); } mXmlWriter->writeEndElement(); } QList XmlReportV2::read() { QList errors; bool insideResults = false; if (!mXmlReader) { qDebug() << "You must Open() the file before reading it!"; return errors; } while (!mXmlReader->atEnd()) { switch (mXmlReader->readNext()) { case QXmlStreamReader::StartElement: if (mXmlReader->name() == ResultElementName) insideResults = true; // Read error element from inside result element if (insideResults && mXmlReader->name() == ErrorElementName) { ErrorItem item = readError(mXmlReader); errors.append(item); } break; case QXmlStreamReader::EndElement: if (mXmlReader->name() == ResultElementName) insideResults = false; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } return errors; } ErrorItem XmlReportV2::readError(QXmlStreamReader *reader) { /* Error example from the core program in xml */ ErrorItem item; // Read error element from inside errors element if (mXmlReader->name() == ErrorElementName) { QXmlStreamAttributes attribs = reader->attributes(); item.errorId = attribs.value(QString(), IdAttribute).toString(); item.severity = GuiSeverity::fromString(attribs.value(QString(), SeverityAttribute).toString()); const QString summary = attribs.value(QString(), MsgAttribute).toString(); item.summary = XmlReport::unquoteMessage(summary); const QString message = attribs.value(QString(), VerboseAttribute).toString(); item.message = XmlReport::unquoteMessage(message); if (attribs.hasAttribute(QString(), InconclusiveAttribute)) item.inconclusive = true; if (attribs.hasAttribute(QString(), CWEAttribute)) item.cwe = attribs.value(QString(), CWEAttribute).toString().toInt(); if (attribs.hasAttribute(QString(), SinceDateAttribute)) item.sinceDate = attribs.value(QString(), SinceDateAttribute).toString(); if (attribs.hasAttribute(QString(), TagsAttribute)) item.tags = attribs.value(QString(), TagsAttribute).toString(); } bool errorRead = false; while (!errorRead && !mXmlReader->atEnd()) { switch (mXmlReader->readNext()) { case QXmlStreamReader::StartElement: // Read location element from inside error element if (mXmlReader->name() == LocationElementName) { QXmlStreamAttributes attribs = mXmlReader->attributes(); QString file0 = attribs.value(QString(), IncludedFromFilenameAttribute).toString(); if (!file0.isEmpty()) item.file0 = XmlReport::unquoteMessage(file0); QErrorPathItem loc; loc.file = XmlReport::unquoteMessage(attribs.value(QString(), FilenameAttribute).toString()); loc.line = attribs.value(QString(), LineAttribute).toString().toUInt(); if (attribs.hasAttribute(QString(), ColumnAttribute)) loc.column = attribs.value(QString(), ColumnAttribute).toString().toInt(); if (attribs.hasAttribute(QString(), InfoAttribute)) loc.info = XmlReport::unquoteMessage(attribs.value(QString(), InfoAttribute).toString()); item.errorPath.push_front(loc); } break; case QXmlStreamReader::EndElement: if (mXmlReader->name() == ErrorElementName) errorRead = true; break; // Not handled case QXmlStreamReader::NoToken: case QXmlStreamReader::Invalid: case QXmlStreamReader::StartDocument: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Characters: case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EntityReference: case QXmlStreamReader::ProcessingInstruction: break; } } if (item.errorPath.size() == 1 && item.errorPath[0].info.isEmpty()) item.errorPath[0].info = item.message; return item; } cppcheck-1.90/gui/xmlreportv2.h000066400000000000000000000045011357737443600165070ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef XML_REPORTV2_H #define XML_REPORTV2_H #include #include #include #include #include "xmlreport.h" /// @addtogroup GUI /// @{ /** * @brief XML file report version 2. * This report outputs XML-formatted report. The XML format must match command * line version's XML output. */ class XmlReportV2 : public XmlReport { public: explicit XmlReportV2(const QString &filename); virtual ~XmlReportV2(); /** * @brief Create the report (file). * @return true if succeeded, false if file could not be created. */ virtual bool create() override; /** * @brief Open existing report file. */ virtual bool open() override; /** * @brief Write report header. */ virtual void writeHeader() override; /** * @brief Write report footer. */ virtual void writeFooter() override; /** * @brief Write error to report. * @param error Error data. */ virtual void writeError(const ErrorItem &error) override; /** * @brief Read contents of the report file. */ virtual QList read() override; protected: /** * @brief Read and parse error item from XML stream. * @param reader XML stream reader to use. */ ErrorItem readError(QXmlStreamReader *reader); private: /** * @brief XML stream reader for reading the report in XML format. */ QXmlStreamReader *mXmlReader; /** * @brief XML stream writer for writing the report in XML format. */ QXmlStreamWriter *mXmlWriter; }; /// @} #endif // XML_REPORTV2_H cppcheck-1.90/htmlreport/000077500000000000000000000000001357737443600154465ustar00rootroot00000000000000cppcheck-1.90/htmlreport/README.txt000066400000000000000000000006611357737443600171470ustar00rootroot00000000000000cppcheck-htmlreport This is a little utility to generate a html report of a XML file produced by cppcheck. The utility is implemented in Python (2.7+) and requires the pygments module to generate syntax highlighted source code. If you are using a Debian based Linux system, the pygments package can be installed by following command: $ sudo apt-get install python-pygments For more information run './cppcheck-htmlreport --help' cppcheck-1.90/htmlreport/check.sh000077500000000000000000000043601357737443600170650ustar00rootroot00000000000000#!/bin/bash -ex # Command for checking HTML syntax with HTML Tidy, see http://www.html-tidy.org/ # newer tidy (5.6.0) command, if using this it is not necessary to ignore warnings: #tidy_cmd='tidy -o /dev/null -eq --drop-empty-elements no' # older tidy from 2009 (Ubuntu 16.04 Xenial comes with this old version): tidy_cmd='tidy -o /dev/null -eq' function validate_html { set +e ${tidy_cmd} $1 tidy_status=$? set -e if [ $tidy_status -eq 2 ]; then echo "HTML does not validate!" exit 1 fi } ./cppcheck-htmlreport --file ../gui/test/data/xmlfiles/xmlreport_v2.xml --title "xml2 test" --report-dir . --source-dir ../test/ echo -e "\n" # Check HTML syntax validate_html index.html validate_html stats.html ../cppcheck ../gui/test --enable=all --inconclusive --xml-version=2 2> gui_test.xml xmllint --noout gui_test.xml ./cppcheck-htmlreport --file ./gui_test.xml --title "xml2 + inconclusive test" --report-dir . echo "" # Check HTML syntax validate_html index.html validate_html stats.html ../cppcheck ../gui/test --enable=all --inconclusive --verbose --xml-version=2 2> gui_test.xml xmllint --noout gui_test.xml ./cppcheck-htmlreport --file ./gui_test.xml --title "xml2 + inconclusive + verbose test" --report-dir . echo -e "\n" # Check HTML syntax validate_html index.html validate_html stats.html ../cppcheck --errorlist --inconclusive --xml-version=2 > errorlist.xml xmllint --noout errorlist.xml ./cppcheck-htmlreport --file ./errorlist.xml --title "errorlist" --report-dir . # Check HTML syntax validate_html index.html validate_html stats.html ../cppcheck ../samples/memleak/good.c ../samples/resourceLeak/good.c --xml-version=2 --enable=information --suppressions-list=test_suppressions.txt --xml 2> unmatchedSuppr.xml xmllint --noout unmatchedSuppr.xml ./cppcheck-htmlreport --file ./unmatchedSuppr.xml --title "unmatched Suppressions" --report-dir=. grep "unmatchedSuppression<.*>information<.*>Unmatched suppression: variableScope*<" index.html grep ">unmatchedSuppressioninformation<.*>Unmatched suppression: uninitstring<" index.html grep "notexisting" index.html grep ">unmatchedSuppression<.*>information<.*>Unmatched suppression: \*<" index.html # Check HTML syntax validate_html index.html validate_html stats.html cppcheck-1.90/htmlreport/cppcheck-htmlreport000077500000000000000000000622131357737443600213560ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import unicode_literals import io import sys import optparse import os import operator from collections import Counter from pygments import highlight from pygments.lexers import guess_lexer, guess_lexer_for_filename from pygments.formatters import HtmlFormatter # pylint: disable=no-name-in-module from pygments.util import ClassNotFound from xml.sax import parse as xml_parse from xml.sax import SAXParseException as XmlParseException from xml.sax.handler import ContentHandler as XmlContentHandler from xml.sax.saxutils import escape """ Turns a cppcheck xml file into a browsable html report along with syntax highlighted source code. """ STYLE_FILE = """ body { font: 13px Arial, Verdana, Sans-Serif; margin: 0; width: auto; } h1 { margin: 10px; } #footer > p { margin: 4px; } .error { background-color: #ffb7b7; } .error2 { background-color: #faa; border: 1px dotted black; display: inline-block; margin-left: 4px; } .inconclusive { background-color: #B6B6B4; } .inconclusive2 { background-color: #B6B6B4; border: 1px dotted black; display: inline-block; margin-left: 4px; } div.verbose { display: inline-block; vertical-align: top; cursor: help; } div.verbose div.content { display: none; position: absolute; padding: 10px; margin: 4px; max-width: 40%; white-space: pre-wrap; border: 1px solid black; background-color: #FFFFCC; cursor: auto; } .highlight .hll { padding: 1px; } #header { border-bottom: thin solid #aaa; } #menu { float: left; margin-top: 5px; text-align: left; width: 150px; /*height: 75%;*/ position: fixed; overflow: auto; z-index: 1; } #menu_index { float: left; margin-top: 5px; padding-left: 5px; text-align: left; width: 300px; /*height: 75%;*/ position: fixed; overflow: auto; z-index: 1; } #menu > a { display: block; margin-left: 10px; font: 12px; z-index: 1; } #filename { margin-left: 10px; font: 12px; z-index: 1; } .highlighttable { background-color:white; z-index: 10; position: relative; margin: -10 px; } #content { background-color: white; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; float: left; margin: 5px; margin-left: 10px; padding: 0 10px 10px 10px; width: 80%; padding-left: 150px; } #content_index { background-color: white; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; float: left; margin: 5px; margin-left: 10px; padding: 0 10px 10px 10px; width: 80%; padding-left: 300px; } .linenos { border-right: thin solid #aaa; color: lightgray; padding-right: 6px; } #footer { border-top: thin solid #aaa; clear: both; font-size: 90%; margin-top: 5px; } #footer ul { list-style-type: none; padding-left: 0; } """ HTML_HEAD = """ Cppcheck - HTML report - %s
""" HTML_FOOTER = """
""" HTML_ERROR = "<--- %s\n" HTML_INCONCLUSIVE = "<--- %s\n" HTML_EXPANDABLE_ERROR = "\n""" HTML_EXPANDABLE_INCONCLUSIVE = "\n""" # escape() and unescape() takes care of &, < and >. html_escape_table = { '"': """, "'": "'" } html_unescape_table = {v: k for k, v in html_escape_table.items()} def html_escape(text): return escape(text, html_escape_table) class AnnotateCodeFormatter(HtmlFormatter): errors = [] def wrap(self, source, outfile): line_no = 1 for i, t in HtmlFormatter.wrap(self, source, outfile): # If this is a source code line we want to add a span tag at the # end. if i == 1: for error in self.errors: if error['line'] == line_no: try: if error['inconclusive'] == 'true': # only print verbose msg if it really differs # from actual message if error.get('verbose') and (error['verbose'] != error['msg']): index = t.rfind('\n') t = t[:index] + HTML_EXPANDABLE_INCONCLUSIVE % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] else: t = t.replace('\n', HTML_INCONCLUSIVE % error['msg']) except KeyError: if error.get('verbose') and (error['verbose'] != error['msg']): index = t.rfind('\n') t = t[:index] + HTML_EXPANDABLE_ERROR % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] else: t = t.replace('\n', HTML_ERROR % error['msg']) line_no = line_no + 1 yield i, t class CppCheckHandler(XmlContentHandler): """Parses the cppcheck xml file and produces a list of all its errors.""" def __init__(self): XmlContentHandler.__init__(self) self.errors = [] self.version = '1' self.versionCppcheck = '' def startElement(self, name, attributes): if name == 'results': self.version = attributes.get('version', self.version) if self.version == '1': self.handleVersion1(name, attributes) else: self.handleVersion2(name, attributes) def handleVersion1(self, name, attributes): if name != 'error': return self.errors.append({ 'file': attributes.get('file', ''), 'line': int(attributes.get('line', 0)), 'locations': [{ 'file': attributes.get('file', ''), 'line': int(attributes.get('line', 0)), }], 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'] }) def handleVersion2(self, name, attributes): if name == 'cppcheck': self.versionCppcheck = attributes['version'] if name == 'error': error = { 'locations': [], 'file': '', 'line': 0, 'id': attributes['id'], 'severity': attributes['severity'], 'msg': attributes['msg'], 'verbose': attributes.get('verbose') } if 'inconclusive' in attributes: error['inconclusive'] = attributes['inconclusive'] if 'cwe' in attributes: error['cwe'] = attributes['cwe'] self.errors.append(error) elif name == 'location': assert self.errors error = self.errors[-1] locations = error['locations'] file = attributes['file'] line = int(attributes['line']) if not locations: error['file'] = file error['line'] = line locations.append({ 'file': file, 'line': line, 'info': attributes.get('info') }) if __name__ == '__main__': # Configure all the options this little utility is using. parser = optparse.OptionParser() parser.add_option('--title', dest='title', help='The title of the project.', default='[project name]') parser.add_option('--file', dest='file', help='The cppcheck xml output file to read defects ' 'from. Default is reading from stdin.') parser.add_option('--report-dir', dest='report_dir', help='The directory where the HTML report content is ' 'written.') parser.add_option('--source-dir', dest='source_dir', help='Base directory where source code files can be ' 'found.') parser.add_option('--source-encoding', dest='source_encoding', help='Encoding of source code.', default='utf-8') # Parse options and make sure that we have an output directory set. options, args = parser.parse_args() try: sys.argv[1] except IndexError: # no arguments give, print --help parser.print_help() quit() if not options.report_dir: parser.error('No report directory set.') # Get the directory where source code files are located. source_dir = os.getcwd() if options.source_dir: source_dir = options.source_dir # Get the stream that we read cppcheck errors from. input_file = sys.stdin if options.file: if not os.path.exists(options.file): parser.error('cppcheck xml file: %s not found.' % options.file) input_file = io.open(options.file, 'r') else: parser.error('No cppcheck xml file specified. (--file=)') # Parse the xml file and produce a simple list of errors. print('Parsing xml report.') try: contentHandler = CppCheckHandler() xml_parse(input_file, contentHandler) except XmlParseException as msg: print('Failed to parse cppcheck xml file: %s' % msg) sys.exit(1) # We have a list of errors. But now we want to group them on # each source code file. Lets create a files dictionary that # will contain a list of all the errors in that file. For each # file we will also generate a HTML filename to use. files = {} file_no = 0 for error in contentHandler.errors: filename = error['file'] if filename not in files.keys(): files[filename] = { 'errors': [], 'htmlfile': str(file_no) + '.html'} file_no = file_no + 1 files[filename]['errors'].append(error) # Make sure that the report directory is created if it doesn't exist. print('Creating %s directory' % options.report_dir) if not os.path.exists(options.report_dir): os.makedirs(options.report_dir) # Generate a HTML file with syntax highlighted source code for each # file that contains one or more errors. print('Processing errors') decode_errors = [] for filename, data in sorted(files.items()): htmlfile = data['htmlfile'] errors = [] for error in data['errors']: for location in error['locations']: if filename == location['file']: newError = dict(error) del newError['locations'] newError['line'] = location['line'] if location.get('info'): newError['msg'] = location['info'] newError['severity'] = 'information' del newError['verbose'] errors.append(newError) lines = [] for error in errors: lines.append(error['line']) if filename == '': continue source_filename = os.path.join(source_dir, filename) try: with io.open(source_filename, 'r', encoding=options.source_encoding) as input_file: content = input_file.read() except IOError: if (error['id'] == 'unmatchedSuppression'): continue # file not found, bail out else: sys.stderr.write("ERROR: Source file '%s' not found.\n" % source_filename) continue except UnicodeDecodeError: sys.stderr.write("WARNING: Unicode decode error in '%s'.\n" % source_filename) decode_errors.append(source_filename[2:]) # "[2:]" gets rid of "./" at beginning continue htmlFormatter = AnnotateCodeFormatter(linenos=True, style='colorful', hl_lines=lines, lineanchors='line', encoding=options.source_encoding) htmlFormatter.errors = errors with io.open(os.path.join(options.report_dir, htmlfile), 'w', encoding='utf-8') as output_file: output_file.write(HTML_HEAD % (options.title, htmlFormatter.get_style_defs('.highlight'), options.title, filename, filename.split('/')[-1])) for error in sorted(errors, key=lambda k: k['line']): output_file.write(" %s %s" % (data['htmlfile'], error['line'], error['id'], error['line'])) output_file.write(HTML_HEAD_END) try: lexer = guess_lexer_for_filename(source_filename, '') except ClassNotFound: try: lexer = guess_lexer(content) except ClassNotFound: sys.stderr.write("ERROR: Couldn't determine lexer for the file' " + source_filename + " '. Won't be able to syntax highlight this file.") output_file.write("\n Could not generated content because pygments failed to retrieve the determine code type.") output_file.write("\n Sorry about this.") continue if options.source_encoding: lexer.encoding = options.source_encoding output_file.write( highlight(content, lexer, htmlFormatter).decode( options.source_encoding)) output_file.write(HTML_FOOTER % contentHandler.versionCppcheck) print(' ' + filename) # Generate a master index.html file that will contain a list of # all the errors created. print('Creating index.html') with io.open(os.path.join(options.report_dir, 'index.html'), 'w') as output_file: stats_count = 0 stats = [] for filename, data in sorted(files.items()): for error in data['errors']: stats.append(error['id']) # get the stats stats_count += 1 counter = Counter(stats) stat_html = [] # the following lines sort the stat primary by value (occurrences), # but if two IDs occur equally often, then we sort them alphabetically by warning ID try: cnt_max = counter.most_common()[0][1] except IndexError: cnt_max = 0 try: cnt_min = counter.most_common()[-1][1] except IndexError: cnt_min = 0 stat_fmt = "\n {}{}" for occurrences in reversed(range(cnt_min, cnt_max + 1)): for _id in [k for k, v in sorted(counter.items()) if v == occurrences]: stat_html.append(stat_fmt.format(_id, _id, dict(counter.most_common())[_id], _id)) output_file.write(HTML_HEAD.replace('id="menu" dir="rtl"', 'id="menu_index"', 1).replace("Defects:", "Defect summary;", 1) % (options.title, '', options.title, '', '')) output_file.write('\n ') output_file.write('\n ') output_file.write('\n ') output_file.write(''.join(stat_html)) output_file.write('\n ') output_file.write('\n
Show#Defect ID
' + str(stats_count) + 'total
') output_file.write('\n

Statistics

') output_file.write(HTML_HEAD_END.replace("content", "content_index", 1)) output_file.write('\n ') output_file.write('\n ') for filename, data in sorted(files.items()): if filename in decode_errors: # don't print a link but a note output_file.write("\n " % (filename)) output_file.write("\n ") else: if filename.endswith('*'): # assume unmatched suppression output_file.write( "\n " % (filename)) else: output_file.write( "\n " % (data['htmlfile'], filename)) for error in sorted(data['errors'], key=lambda k: k['line']): error_class = '' try: if error['inconclusive'] == 'true': error_class = 'class="inconclusive"' error['severity'] += ", inconcl." except KeyError: pass try: if error['cwe']: cwe_url = "" + error['cwe'] + "" except KeyError: cwe_url = "" if error['severity'] == 'error': error_class = 'class="error"' if error['id'] == 'missingInclude': output_file.write( '\n ' % (error['id'], error['id'], error['severity'], html_escape(error['msg']))) elif (error['id'] == 'unmatchedSuppression') and filename.endswith('*'): output_file.write( '\n ' % (error['id'], error['id'], error['severity'], error_class, html_escape(error['msg']))) else: output_file.write( '\n ' % (error['id'], data['htmlfile'], error['line'], error['line'], error['id'], cwe_url, error['severity'], error_class, html_escape(error['msg']))) output_file.write('\n
LineIdCWESeverityMessage
%s
Could not generated due to UnicodeDecodeError
%s
%s
%s%s%s
%s%s%s
%d%s%s%s%s
') output_file.write(HTML_FOOTER % contentHandler.versionCppcheck) if (decode_errors): sys.stderr.write("\nGenerating html failed for the following files: " + ' '.join(decode_errors)) sys.stderr.write("\nConsider changing source-encoding (for example: \"htmlreport ... --source-encoding=\"iso8859-1\"\"\n") print('Creating style.css file') with io.open(os.path.join(options.report_dir, 'style.css'), 'w') as css_file: css_file.write(STYLE_FILE) print("Creating stats.html (statistics)\n") stats_countlist = {} for filename, data in sorted(files.items()): if (filename == ''): continue stats_tmplist = [] for error in sorted(data['errors'], key=lambda k: k['line']): stats_tmplist.append(error['severity']) stats_countlist[filename] = dict(Counter(stats_tmplist)) # get top ten for each severity SEVERITIES = "error", "warning", "portability", "performance", "style", "unusedFunction", "information", "missingInclude", "internal" with io.open(os.path.join(options.report_dir, 'stats.html'), 'w') as stats_file: stats_file.write(HTML_HEAD.replace('id="menu" dir="rtl"', 'id="menu_index"', 1).replace("Defects:", "Back to summary", 1) % (options.title, '', options.title, 'Statistics', '')) stats_file.write(HTML_HEAD_END.replace("content", "content_index", 1)) for sev in SEVERITIES: _sum = 0 stats_templist = {} # if the we have an style warning but we are checking for # portability, we have to skip it to prevent KeyError try: for filename in stats_countlist: try: # also bail out if we have a file with no sev-results _sum += stats_countlist[filename][sev] stats_templist[filename] = (int)(stats_countlist[filename][sev]) # file : amount, except KeyError: continue # don't print "0 style" etc, if no style warnings were found if (_sum == 0): continue except KeyError: continue stats_file.write("

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

\n") stats_file.write(HTML_FOOTER % contentHandler.versionCppcheck) print("\nOpen '" + options.report_dir + "/index.html' to see the results.") cppcheck-1.90/htmlreport/example.cc000066400000000000000000000000711357737443600174060ustar00rootroot00000000000000#include "missing.h" int main() { int x; x++; } cppcheck-1.90/htmlreport/example.xml000066400000000000000000000007531357737443600176300ustar00rootroot00000000000000 Checking example.cc... Checking usage of global functions.. cppcheck-1.90/htmlreport/requirements.txt000066400000000000000000000000111357737443600207220ustar00rootroot00000000000000Pygments cppcheck-1.90/htmlreport/setup.py000077500000000000000000000010301357737443600171550ustar00rootroot00000000000000#!/usr/bin/env python from setuptools import setup with open('README.txt') as f: readme = f.read() setup( name="cppcheck", description='Python script to parse the XML (version 2) output of ' + 'cppcheck and generate an HTML report using Pygments for syntax ' + 'highlighting.', long_description=readme, author='Henrik Nilsson', url='https://github.com/danmar/cppcheck', license='GPL', scripts=[ "cppcheck-htmlreport", ], install_requires=['Pygments'] ) cppcheck-1.90/htmlreport/test_htmlreport.py000077500000000000000000000067751357737443600213010ustar00rootroot00000000000000#!/usr/bin/env python """Test cppcheck-htmlreport.""" import os import contextlib import shutil import subprocess import sys import tempfile if sys.version_info < (2, 7): # For TestCase.assertIn(). import unittest2 as unittest else: import unittest ROOT_DIR = os.path.split(os.path.abspath(os.path.dirname(__file__)))[0] CPPCHECK_BIN = os.path.join(ROOT_DIR, 'cppcheck') HTML_REPORT_BIN = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'cppcheck-htmlreport') class TestHTMLReport(unittest.TestCase): def testReportError(self): for xml_version in ['2']: self.checkReportError(xml_version) def checkReportError(self, xml_version): with runCheck( os.path.join(ROOT_DIR, 'samples', 'memleak', 'bad.c'), xml_version=xml_version ) as (report, output_directory): self.assertIn('. */ #include "analyzerinfo.h" #include "path.h" #include "utils.h" #include #include #include #include AnalyzerInformation::~AnalyzerInformation() { close(); } static std::string getFilename(const std::string &fullpath) { std::string::size_type pos1 = fullpath.find_last_of("/\\"); pos1 = (pos1 == std::string::npos) ? 0U : (pos1 + 1U); std::string::size_type pos2 = fullpath.rfind('.'); if (pos2 < pos1) pos2 = std::string::npos; if (pos2 != std::string::npos) pos2 = pos2 - pos1; return fullpath.substr(pos1,pos2); } void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::list &fileSettings) { std::map fileCount; const std::string filesTxt(buildDir + "/files.txt"); std::ofstream fout(filesTxt); for (const std::string &f : sourcefiles) { const std::string afile = getFilename(f); fout << afile << ".a" << (++fileCount[afile]) << "::" << Path::simplifyPath(Path::fromNativeSeparators(f)) << '\n'; } for (const ImportProject::FileSettings &fs : fileSettings) { const std::string afile = getFilename(fs.filename); fout << afile << ".a" << (++fileCount[afile]) << ":" << fs.cfg << ":" << Path::simplifyPath(Path::fromNativeSeparators(fs.filename)) << std::endl; } } void AnalyzerInformation::close() { mAnalyzerInfoFile.clear(); if (mOutputStream.is_open()) { mOutputStream << "\n"; mOutputStream.close(); } } static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long checksum, std::list *errors) { tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(analyzerInfoFile.c_str()); if (error != tinyxml2::XML_SUCCESS) return false; const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); if (rootNode == nullptr) return false; const char *attr = rootNode->Attribute("checksum"); if (!attr || attr != std::to_string(checksum)) return false; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "error") == 0) errors->emplace_back(e); } return true; } std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg) { const std::string files(buildDir + "/files.txt"); std::ifstream fin(files); if (fin.is_open()) { std::string line; const std::string end(':' + cfg + ':' + sourcefile); while (std::getline(fin,line)) { if (line.size() <= end.size() + 2U) continue; if (!endsWith(line, end.c_str(), end.size())) continue; std::ostringstream ostr; ostr << buildDir << '/' << line.substr(0,line.find(':')); return ostr.str(); } } std::string filename = Path::fromNativeSeparators(buildDir); if (!endsWith(filename, '/')) filename += '/'; const std::string::size_type pos = sourcefile.rfind('/'); if (pos == std::string::npos) filename += sourcefile; else filename += sourcefile.substr(pos+1); filename += ".analyzerinfo"; return filename; } bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, unsigned long long checksum, std::list *errors) { if (buildDir.empty() || sourcefile.empty()) return true; close(); mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg); if (skipAnalysis(mAnalyzerInfoFile, checksum, errors)) return false; mOutputStream.open(mAnalyzerInfoFile); if (mOutputStream.is_open()) { mOutputStream << "\n"; mOutputStream << "\n"; } else { mAnalyzerInfoFile.clear(); } return true; } void AnalyzerInformation::reportErr(const ErrorLogger::ErrorMessage &msg, bool /*verbose*/) { if (mOutputStream.is_open()) mOutputStream << msg.toXML() << '\n'; } void AnalyzerInformation::setFileInfo(const std::string &check, const std::string &fileInfo) { if (mOutputStream.is_open() && !fileInfo.empty()) mOutputStream << " \n" << fileInfo << " \n"; } cppcheck-1.90/lib/analyzerinfo.h000066400000000000000000000045661357737443600167010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef analyzerinfoH #define analyzerinfoH //--------------------------------------------------------------------------- #include "config.h" #include "errorlogger.h" #include "importproject.h" #include #include #include /// @addtogroup Core /// @{ /** * @brief Analyzer information * * Store various analysis information: * - checksum * - error messages * - whole program analysis data * * The information can be used for various purposes. It allows: * - 'make' - only analyze TUs that are changed and generate full report * - should be possible to add distributed analysis later * - multi-threaded whole program analysis */ class CPPCHECKLIB AnalyzerInformation { public: ~AnalyzerInformation(); static void writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::list &fileSettings); /** Close current TU.analyzerinfo file */ void close(); bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, unsigned long long checksum, std::list *errors); void reportErr(const ErrorLogger::ErrorMessage &msg, bool verbose); void setFileInfo(const std::string &check, const std::string &fileInfo); static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg); private: std::ofstream mOutputStream; std::string mAnalyzerInfoFile; }; /// @} //--------------------------------------------------------------------------- #endif // analyzerinfoH cppcheck-1.90/lib/astutils.cpp000066400000000000000000002327261357737443600164040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "astutils.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "valueflow.h" #include #include #include void visitAstNodes(const Token *ast, std::function visitor) { std::stack tokens; tokens.push(ast); while (!tokens.empty()) { const Token *tok = tokens.top(); tokens.pop(); if (!tok) continue; ChildrenToVisit c = visitor(tok); if (c == ChildrenToVisit::done) break; if (c == ChildrenToVisit::op1 || c == ChildrenToVisit::op1_and_op2) tokens.push(tok->astOperand1()); if (c == ChildrenToVisit::op2 || c == ChildrenToVisit::op1_and_op2) tokens.push(tok->astOperand2()); } } static void astFlattenRecursive(const Token *tok, std::vector *result, const char* op, nonneg int depth = 0) { ++depth; if (!tok || depth >= 100) return; if (tok->str() == op) { astFlattenRecursive(tok->astOperand1(), result, op, depth); astFlattenRecursive(tok->astOperand2(), result, op, depth); } else { result->push_back(tok); } } std::vector astFlatten(const Token* tok, const char* op) { std::vector result; astFlattenRecursive(tok, &result, op); return result; } bool astHasToken(const Token* root, const Token * tok) { if (!root) return false; if (root == tok) return true; return astHasToken(root->astOperand1(), tok) || astHasToken(root->astOperand2(), tok); } bool astHasVar(const Token * tok, nonneg int varid) { if (!tok) return false; if (tok->varId() == varid) return true; return astHasVar(tok->astOperand1(), varid) || astHasVar(tok->astOperand2(), varid); } static bool astIsCharWithSign(const Token *tok, ValueType::Sign sign) { if (!tok) return false; const ValueType *valueType = tok->valueType(); if (!valueType) return false; return valueType->type == ValueType::Type::CHAR && valueType->pointer == 0U && valueType->sign == sign; } bool astIsSignedChar(const Token *tok) { return astIsCharWithSign(tok, ValueType::Sign::SIGNED); } bool astIsUnknownSignChar(const Token *tok) { return astIsCharWithSign(tok, ValueType::Sign::UNKNOWN_SIGN); } bool astIsIntegral(const Token *tok, bool unknown) { const ValueType *vt = tok ? tok->valueType() : nullptr; if (!vt) return unknown; return vt->isIntegral() && vt->pointer == 0U; } bool astIsFloat(const Token *tok, bool unknown) { const ValueType *vt = tok ? tok->valueType() : nullptr; if (!vt) return unknown; return vt->type >= ValueType::Type::FLOAT && vt->pointer == 0U; } bool astIsBool(const Token *tok) { return tok && (tok->isBoolean() || (tok->valueType() && tok->valueType()->type == ValueType::Type::BOOL && !tok->valueType()->pointer)); } bool astIsPointer(const Token *tok) { return tok && tok->valueType() && tok->valueType()->pointer; } bool astIsSmartPointer(const Token* tok) { return tok && tok->valueType() && tok->valueType()->smartPointerTypeToken; } bool astIsIterator(const Token *tok) { return tok && tok->valueType() && tok->valueType()->type == ValueType::Type::ITERATOR; } bool astIsContainer(const Token *tok) { return getLibraryContainer(tok) != nullptr && tok->valueType()->type != ValueType::Type::ITERATOR; } std::string astCanonicalType(const Token *expr) { if (!expr) return ""; if (expr->variable()) { const Variable *var = expr->variable(); std::string ret; for (const Token *type = var->typeStartToken(); Token::Match(type,"%name%|::") && type != var->nameToken(); type = type->next()) { if (!Token::Match(type, "const|static")) ret += type->str(); } return ret; } // TODO: handle expressions return ""; } static bool match(const Token *tok, const std::string &rhs) { if (tok->str() == rhs) return true; if (!tok->varId() && tok->hasKnownIntValue() && MathLib::toString(tok->values().front().intvalue) == rhs) return true; return false; } const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok) { if (!tok) return nullptr; const Token *ret = nullptr; if (tok->isComparisonOp()) { if (tok->astOperand1() && match(tok->astOperand1(), rhs)) { // Invert comparator std::string s = tok->str(); if (s[0] == '>') s[0] = '<'; else if (s[0] == '<') s[0] = '>'; if (s == comp) { ret = tok->astOperand2(); } } else if (tok->str() == comp && tok->astOperand2() && match(tok->astOperand2(), rhs)) { ret = tok->astOperand1(); } } else if (comp == "!=" && rhs == std::string("0")) { ret = tok; } else if (comp == "==" && rhs == std::string("0")) { if (tok->str() == "!") { ret = tok->astOperand1(); // handle (!(x!=0)) as (x==0) astIsVariableComparison(ret, "!=", "0", &ret); } } while (ret && ret->str() == ".") ret = ret->astOperand2(); if (ret && ret->str() == "=" && ret->astOperand1() && ret->astOperand1()->varId()) ret = ret->astOperand1(); else if (ret && ret->varId() == 0U) ret = nullptr; if (vartok) *vartok = ret; return ret; } bool isTemporary(bool cpp, const Token* tok, const Library* library) { if (!tok) return false; if (Token::simpleMatch(tok, ".")) return (tok->originalName() != "->" && isTemporary(cpp, tok->astOperand1(), library)) || isTemporary(cpp, tok->astOperand2(), library); if (Token::Match(tok, ",|::")) return isTemporary(cpp, tok->astOperand2(), library); if (tok->isCast() || (cpp && isCPPCast(tok))) return isTemporary(cpp, tok->astOperand2(), library); if (Token::Match(tok, "?|.|[|++|--|%name%|%assign%")) return false; if (tok->isUnaryOp("*")) return false; if (Token::Match(tok, "&|<<|>>") && isLikelyStream(cpp, tok->astOperand1())) return false; if (Token::Match(tok->previous(), ">|%name% (")) { const Token* ftok = nullptr; if (tok->previous()->link()) ftok = tok->previous()->link()->previous(); else ftok = tok->previous(); if (!ftok) return false; if (const Function * f = ftok->function()) { return !Function::returnsReference(f, true); } else if (library) { std::string returnType = library->returnValueType(ftok); return !returnType.empty() && returnType.back() != '&'; } else { return false; } } return true; } static bool isFunctionCall(const Token* tok) { if (Token::Match(tok, "%name% (")) return true; if (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->next()->link(), "> (")) return true; if (Token::Match(tok, "%name% ::")) return isFunctionCall(tok->tokAt(2)); return false; } static bool hasToken(const Token * startTok, const Token * stopTok, const Token * tok) { for (const Token * tok2 = startTok; tok2 != stopTok; tok2 = tok2->next()) { if (tok2 == tok) return true; } return false; } const Token * nextAfterAstRightmostLeaf(const Token * tok) { const Token * rightmostLeaf = tok; if (!rightmostLeaf || !rightmostLeaf->astOperand1()) return nullptr; do { if (rightmostLeaf->astOperand2()) rightmostLeaf = rightmostLeaf->astOperand2(); else rightmostLeaf = rightmostLeaf->astOperand1(); } while (rightmostLeaf->astOperand1()); while (Token::Match(rightmostLeaf->next(), "]|)") && !hasToken(rightmostLeaf->next()->link(), rightmostLeaf->next(), tok)) rightmostLeaf = rightmostLeaf->next(); if (rightmostLeaf->str() == "{" && rightmostLeaf->link()) rightmostLeaf = rightmostLeaf->link(); return rightmostLeaf->next(); } const Token* astParentSkipParens(const Token* tok) { return astParentSkipParens(const_cast(tok)); } Token* astParentSkipParens(Token* tok) { if (!tok) return nullptr; Token * parent = tok->astParent(); if (!Token::simpleMatch(parent, "(")) return parent; if (parent->link() != nextAfterAstRightmostLeaf(tok)) return parent; return astParentSkipParens(parent); } const Token* getParentMember(const Token * tok) { if (!tok) return tok; const Token * parent = tok->astParent(); if (!Token::simpleMatch(parent, ".")) return tok; if (tok == parent->astOperand2()) return parent->astOperand1(); const Token * gparent = parent->astParent(); if (!Token::simpleMatch(gparent, ".") || gparent->astOperand2() != parent) return tok; if (gparent->astOperand1()) return gparent->astOperand1(); return tok; } static const Token * getVariableInitExpression(const Variable * var) { if (!var || !var->declEndToken()) return nullptr; if (Token::Match(var->declEndToken(), "; %varid% =", var->declarationId())) return var->declEndToken()->tokAt(2)->astOperand2(); return var->declEndToken()->astOperand2(); } static bool isInLoopCondition(const Token * tok) { return Token::Match(tok->astTop()->previous(), "for|while ("); } /// If tok2 comes after tok1 bool precedes(const Token * tok1, const Token * tok2) { if (!tok1) return false; if (!tok2) return false; return tok1->index() < tok2->index(); } bool isAliasOf(const Token *tok, nonneg int varid) { if (tok->varId() == varid) return false; if (tok->varId() == 0) return false; for (const ValueFlow::Value &val : tok->values()) { if (!val.isLocalLifetimeValue()) continue; if (val.isInconclusive()) continue; if (val.tokvalue->varId() == varid) return true; } return false; } static bool isAliased(const Token *startTok, const Token *endTok, nonneg int varid) { if (!precedes(startTok, endTok)) return false; for (const Token *tok = startTok; tok != endTok; tok = tok->next()) { if (Token::Match(tok, "= & %varid% ;", varid)) return true; if (isAliasOf(tok, varid)) return true; } return false; } bool isAliased(const Variable *var) { if (!var) return false; if (!var->scope()) return false; const Token *start = var->declEndToken(); if (!start) return false; return isAliased(start, var->scope()->bodyEnd, var->declarationId()); } bool exprDependsOnThis(const Token* expr, nonneg int depth) { if (!expr) return false; if (depth >= 1000) // Abort recursion to avoid stack overflow return true; ++depth; // calling nonstatic method? if (Token::Match(expr->previous(), "!!:: %name% (") && expr->function() && expr->function()->nestedIn && expr->function()->nestedIn->isClassOrStruct()) { // is it a method of this? const Scope *nestedIn = expr->scope()->functionOf; if (nestedIn && nestedIn->function) nestedIn = nestedIn->function->token->scope(); while (nestedIn && nestedIn != expr->function()->nestedIn) { nestedIn = nestedIn->nestedIn; } return nestedIn == expr->function()->nestedIn; } else if (Token::Match(expr, "%var%") && expr->variable()) { const Variable* var = expr->variable(); return (var->isPrivate() || var->isPublic() || var->isProtected()); } if (Token::simpleMatch(expr, ".")) return exprDependsOnThis(expr->astOperand1(), depth); return exprDependsOnThis(expr->astOperand1(), depth) || exprDependsOnThis(expr->astOperand2(), depth); } /// This takes a token that refers to a variable and it will return the token /// to the expression that the variable is assigned to. If its not valid to /// make such substitution then it will return the original token. static const Token * followVariableExpression(const Token * tok, bool cpp, const Token * end = nullptr) { if (!tok) return tok; // Skip following variables that is across multiple files if (end && end->fileIndex() != tok->fileIndex()) return tok; // Skip array access if (Token::Match(tok, "%var% [")) return tok; // Skip pointer indirection if (tok->astParent() && tok->isUnaryOp("*")) return tok; // Skip following variables if it is used in an assignment if (Token::Match(tok->next(), "%assign%")) return tok; const Variable * var = tok->variable(); const Token * varTok = getVariableInitExpression(var); if (!varTok) return tok; // Bailout. If variable value depends on value of "this". if (exprDependsOnThis(varTok)) return tok; // Skip array access if (Token::simpleMatch(varTok, "[")) return tok; if (var->isVolatile()) return tok; if (!var->isLocal() && !var->isConst()) return tok; if (var->isStatic() && !var->isConst()) return tok; if (var->isArgument()) return tok; const Token * lastTok = precedes(tok, end) ? end : tok; // If this is in a loop then check if variables are modified in the entire scope const Token * endToken = (isInLoopCondition(tok) || isInLoopCondition(varTok) || var->scope() != tok->scope()) ? var->scope()->bodyEnd : lastTok; if (!var->isConst() && (!precedes(varTok, endToken) || isVariableChanged(varTok, endToken, tok->varId(), false, nullptr, cpp))) return tok; if (precedes(varTok, endToken) && isAliased(varTok, endToken, tok->varId())) return tok; // Start at beginning of initialization const Token * startToken = varTok; while (Token::Match(startToken, "%op%|.|(|{") && startToken->astOperand1()) startToken = startToken->astOperand1(); // Skip if the variable its referring to is modified for (const Token * tok2 = startToken; tok2 != endToken; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, ";")) break; if (tok2->astParent() && tok2->isUnaryOp("*")) return tok; if (tok2->tokType() == Token::eIncDecOp || tok2->isAssignmentOp() || Token::Match(tok2, "%name% .|[|++|--|%assign%")) { return tok; } if (const Variable * var2 = tok2->variable()) { if (!var2->scope()) return tok; const Token * endToken2 = var2->scope() != tok->scope() ? var2->scope()->bodyEnd : endToken; if (!var2->isLocal() && !var2->isConst() && !var2->isArgument()) return tok; if (var2->isStatic() && !var2->isConst()) return tok; if (!var2->isConst() && (!precedes(tok2, endToken2) || isVariableChanged(tok2, endToken2, tok2->varId(), false, nullptr, cpp))) return tok; if (precedes(tok2, endToken2) && isAliased(tok2, endToken2, tok2->varId())) return tok; // Recognized as a variable but the declaration is unknown } else if (tok2->varId() > 0) { return tok; } else if (tok2->tokType() == Token::eName && !Token::Match(tok2, "sizeof|decltype|typeof") && !tok2->function()) { return tok; } } return varTok; } static void followVariableExpressionError(const Token *tok1, const Token *tok2, ErrorPath* errors) { if (!errors) return; if (!tok1) return; if (!tok2) return; ErrorPathItem item = std::make_pair(tok2, "'" + tok1->str() + "' is assigned value '" + tok2->expressionString() + "' here."); if (std::find(errors->begin(), errors->end(), item) != errors->end()) return; errors->push_back(item); } template static void findTokenValue(const Token* const tok, Predicate pred, F f) { auto x = std::find_if(tok->values().begin(), tok->values().end(), pred); if (x != tok->values().end()) f(*x); } bool isEqualKnownValue(const Token * const tok1, const Token * const tok2) { bool result = false; findTokenValue(tok1, std::mem_fn(&ValueFlow::Value::isKnown), [&](const ValueFlow::Value& v1) { findTokenValue(tok2, std::mem_fn(&ValueFlow::Value::isKnown), [&](const ValueFlow::Value& v2) { result = v1.equalValue(v2); }); }); return result; } bool isDifferentKnownValues(const Token * const tok1, const Token * const tok2) { bool result = false; findTokenValue(tok1, std::mem_fn(&ValueFlow::Value::isKnown), [&](const ValueFlow::Value& v1) { findTokenValue(tok2, std::mem_fn(&ValueFlow::Value::isKnown), [&](const ValueFlow::Value& v2) { result = !v1.equalValue(v2); }); }); return result; } bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors) { if (tok1 == nullptr && tok2 == nullptr) return true; if (tok1 == nullptr || tok2 == nullptr) return false; if (cpp) { if (tok1->str() == "." && tok1->astOperand1() && tok1->astOperand1()->str() == "this") tok1 = tok1->astOperand2(); if (tok2->str() == "." && tok2->astOperand1() && tok2->astOperand1()->str() == "this") tok2 = tok2->astOperand2(); } // Skip double not if (Token::simpleMatch(tok1, "!") && Token::simpleMatch(tok1->astOperand1(), "!") && !Token::simpleMatch(tok1->astParent(), "=")) { return isSameExpression(cpp, macro, tok1->astOperand1()->astOperand1(), tok2, library, pure, followVar, errors); } if (Token::simpleMatch(tok2, "!") && Token::simpleMatch(tok2->astOperand1(), "!") && !Token::simpleMatch(tok2->astParent(), "=")) { return isSameExpression(cpp, macro, tok1, tok2->astOperand1()->astOperand1(), library, pure, followVar, errors); } if (tok1->str() != tok2->str() && isDifferentKnownValues(tok1, tok2)) return false; // Follow variable if (followVar && tok1->str() != tok2->str() && (Token::Match(tok1, "%var%") || Token::Match(tok2, "%var%"))) { const Token * varTok1 = followVariableExpression(tok1, cpp, tok2); if (varTok1->str() == tok2->str()) { followVariableExpressionError(tok1, varTok1, errors); return isSameExpression(cpp, macro, varTok1, tok2, library, true, followVar, errors); } const Token * varTok2 = followVariableExpression(tok2, cpp, tok1); if (tok1->str() == varTok2->str()) { followVariableExpressionError(tok2, varTok2, errors); return isSameExpression(cpp, macro, tok1, varTok2, library, true, followVar, errors); } if (varTok1->str() == varTok2->str()) { followVariableExpressionError(tok1, varTok1, errors); followVariableExpressionError(tok2, varTok2, errors); return isSameExpression(cpp, macro, varTok1, varTok2, library, true, followVar, errors); } } if (tok1->varId() != tok2->varId() || tok1->str() != tok2->str() || tok1->originalName() != tok2->originalName()) { if ((Token::Match(tok1,"<|>") && Token::Match(tok2,"<|>")) || (Token::Match(tok1,"<=|>=") && Token::Match(tok2,"<=|>="))) { return isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure, followVar, errors) && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure, followVar, errors); } return false; } if (macro && (tok1->isExpandedMacro() || tok2->isExpandedMacro() || tok1->isTemplateArg() || tok2->isTemplateArg())) return false; if (tok1->isComplex() != tok2->isComplex()) return false; if (tok1->isLong() != tok2->isLong()) return false; if (tok1->isUnsigned() != tok2->isUnsigned()) return false; if (tok1->isSigned() != tok2->isSigned()) return false; if (pure && tok1->isName() && tok1->next()->str() == "(" && tok1->str() != "sizeof") { if (!tok1->function()) { if (Token::simpleMatch(tok1->previous(), ".")) { const Token *lhs = tok1->previous(); while (Token::Match(lhs, "(|.|[")) lhs = lhs->astOperand1(); const bool lhsIsConst = (lhs->variable() && lhs->variable()->isConst()) || (lhs->valueType() && lhs->valueType()->constness > 0) || (Token::Match(lhs, "%var% . %name% (") && library.isFunctionConst(lhs->tokAt(2))); if (!lhsIsConst) return false; } else { const Token * ftok = tok1; if (Token::simpleMatch(tok1->previous(), "::")) ftok = tok1->previous(); if (!library.isFunctionConst(ftok) && !ftok->isAttributeConst() && !ftok->isAttributePure()) return false; } } else { if (tok1->function() && !tok1->function()->isConst() && !tok1->function()->isAttributeConst() && !tok1->function()->isAttributePure()) return false; } } // templates/casts if ((Token::Match(tok1, "%name% <") && tok1->next()->link()) || (Token::Match(tok2, "%name% <") && tok2->next()->link())) { // non-const template function that is not a dynamic_cast => return false if (pure && Token::simpleMatch(tok1->next()->link(), "> (") && !(tok1->function() && tok1->function()->isConst()) && tok1->str() != "dynamic_cast") return false; // some template/cast stuff.. check that the template arguments are same const Token *t1 = tok1->next(); const Token *t2 = tok2->next(); const Token *end1 = t1->link(); const Token *end2 = t2->link(); while (t1 && t2 && t1 != end1 && t2 != end2) { if (t1->str() != t2->str()) return false; t1 = t1->next(); t2 = t2->next(); } if (t1 != end1 || t2 != end2) return false; } if (tok1->tokType() == Token::eIncDecOp || tok1->isAssignmentOp()) return false; // bailout when we see ({..}) if (tok1->str() == "{") return false; // cast => assert that the casts are equal if (tok1->str() == "(" && tok1->previous() && !tok1->previous()->isName() && !(tok1->previous()->str() == ">" && tok1->previous()->link())) { const Token *t1 = tok1->next(); const Token *t2 = tok2->next(); while (t1 && t2 && t1->str() == t2->str() && t1->isLong() == t2->isLong() && t1->isUnsigned() == t2->isUnsigned() && t1->isSigned() == t2->isSigned() && (t1->isName() || t1->str() == "*")) { t1 = t1->next(); t2 = t2->next(); } if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")") return false; } bool noncommutativeEquals = isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand1(), library, pure, followVar, errors); noncommutativeEquals = noncommutativeEquals && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand2(), library, pure, followVar, errors); if (noncommutativeEquals) return true; // in c++, a+b might be different to b+a, depending on the type of a and b if (cpp && tok1->str() == "+" && tok1->isBinaryOp()) { const ValueType* vt1 = tok1->astOperand1()->valueType(); const ValueType* vt2 = tok1->astOperand2()->valueType(); if (!(vt1 && (vt1->type >= ValueType::VOID || vt1->pointer) && vt2 && (vt2->type >= ValueType::VOID || vt2->pointer))) return false; } const bool commutative = tok1->isBinaryOp() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!="); bool commutativeEquals = commutative && isSameExpression(cpp, macro, tok1->astOperand2(), tok2->astOperand1(), library, pure, followVar, errors); commutativeEquals = commutativeEquals && isSameExpression(cpp, macro, tok1->astOperand1(), tok2->astOperand2(), library, pure, followVar, errors); return commutativeEquals; } static bool isZeroBoundCond(const Token * const cond) { if (cond == nullptr) return false; // Assume unsigned // TODO: Handle reverse conditions const bool isZero = cond->astOperand2()->getValue(0); if (cond->str() == "==" || cond->str() == ">=") return isZero; if (cond->str() == "<=") return true; if (cond->str() == "<") return !isZero; if (cond->str() == ">") return false; return false; } bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const Library& library, bool pure, bool followVar, ErrorPath* errors) { if (!cond1 || !cond2) return false; if (cond1->str() == "!") { if (cond2->str() == "!=") { if (cond2->astOperand1() && cond2->astOperand1()->str() == "0") return isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand2(), library, pure, followVar, errors); if (cond2->astOperand2() && cond2->astOperand2()->str() == "0") return isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors); } return isSameExpression(cpp, true, cond1->astOperand1(), cond2, library, pure, followVar, errors); } if (cond2->str() == "!") return isOppositeCond(isNot, cpp, cond2, cond1, library, pure, followVar, errors); if (!isNot) { if (cond1->str() == "==" && cond2->str() == "==") { if (isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors)) return isDifferentKnownValues(cond1->astOperand2(), cond2->astOperand2()); if (isSameExpression(cpp, true, cond1->astOperand2(), cond2->astOperand2(), library, pure, followVar, errors)) return isDifferentKnownValues(cond1->astOperand1(), cond2->astOperand1()); } // TODO: Handle reverse conditions if (Library::isContainerYield(cond1, Library::Container::Yield::EMPTY, "empty") && Library::isContainerYield(cond2->astOperand1(), Library::Container::Yield::SIZE, "size") && cond1->astOperand1()->astOperand1()->varId() == cond2->astOperand1()->astOperand1()->astOperand1()->varId()) { return !isZeroBoundCond(cond2); } if (Library::isContainerYield(cond2, Library::Container::Yield::EMPTY, "empty") && Library::isContainerYield(cond1->astOperand1(), Library::Container::Yield::SIZE, "size") && cond2->astOperand1()->astOperand1()->varId() == cond1->astOperand1()->astOperand1()->astOperand1()->varId()) { return !isZeroBoundCond(cond1); } } if (!cond1->isComparisonOp() || !cond2->isComparisonOp()) return false; const std::string &comp1 = cond1->str(); // condition found .. get comparator std::string comp2; if (isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors) && isSameExpression(cpp, true, cond1->astOperand2(), cond2->astOperand2(), library, pure, followVar, errors)) { comp2 = cond2->str(); } else if (isSameExpression(cpp, true, cond1->astOperand1(), cond2->astOperand2(), library, pure, followVar, errors) && isSameExpression(cpp, true, cond1->astOperand2(), cond2->astOperand1(), library, pure, followVar, errors)) { comp2 = cond2->str(); if (comp2[0] == '>') comp2[0] = '<'; else if (comp2[0] == '<') comp2[0] = '>'; } if (!isNot && comp2.empty()) { const Token *expr1 = nullptr, *value1 = nullptr, *expr2 = nullptr, *value2 = nullptr; std::string op1 = cond1->str(), op2 = cond2->str(); if (cond1->astOperand2()->hasKnownIntValue()) { expr1 = cond1->astOperand1(); value1 = cond1->astOperand2(); } else if (cond1->astOperand1()->hasKnownIntValue()) { expr1 = cond1->astOperand2(); value1 = cond1->astOperand1(); if (op1[0] == '>') op1[0] = '<'; else if (op1[0] == '<') op1[0] = '>'; } if (cond2->astOperand2()->hasKnownIntValue()) { expr2 = cond2->astOperand1(); value2 = cond2->astOperand2(); } else if (cond2->astOperand1()->hasKnownIntValue()) { expr2 = cond2->astOperand2(); value2 = cond2->astOperand1(); if (op2[0] == '>') op2[0] = '<'; else if (op2[0] == '<') op2[0] = '>'; } if (!expr1 || !value1 || !expr2 || !value2) { return false; } if (!isSameExpression(cpp, true, expr1, expr2, library, pure, followVar, errors)) return false; const ValueFlow::Value &rhsValue1 = value1->values().front(); const ValueFlow::Value &rhsValue2 = value2->values().front(); if (op1 == "<" || op1 == "<=") return (op2 == "==" || op2 == ">" || op2 == ">=") && (rhsValue1.intvalue < rhsValue2.intvalue); else if (op1 == ">=" || op1 == ">") return (op2 == "==" || op2 == "<" || op2 == "<=") && (rhsValue1.intvalue > rhsValue2.intvalue); return false; } // is condition opposite? return ((comp1 == "==" && comp2 == "!=") || (comp1 == "!=" && comp2 == "==") || (comp1 == "<" && comp2 == ">=") || (comp1 == "<=" && comp2 == ">") || (comp1 == ">" && comp2 == "<=") || (comp1 == ">=" && comp2 == "<") || (!isNot && ((comp1 == "<" && comp2 == ">") || (comp1 == ">" && comp2 == "<") || (comp1 == "==" && (comp2 == "!=" || comp2 == ">" || comp2 == "<")) || ((comp1 == "!=" || comp1 == ">" || comp1 == "<") && comp2 == "==") ))); } bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * const tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors) { if (!tok1 || !tok2) return false; if (isOppositeCond(true, cpp, tok1, tok2, library, pure, followVar, errors)) return true; if (tok1->isUnaryOp("-")) return isSameExpression(cpp, true, tok1->astOperand1(), tok2, library, pure, followVar, errors); if (tok2->isUnaryOp("-")) return isSameExpression(cpp, true, tok2->astOperand1(), tok1, library, pure, followVar, errors); return false; } bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp) { if (!tok) return true; if (tok->isName() && tok->next()->str() == "(") { if (!tok->function() && !Token::Match(tok->previous(), ".|::") && !library.isFunctionConst(tok->str(), pure)) return false; else if (tok->function() && !tok->function()->isConst()) return false; } if (tok->tokType() == Token::eIncDecOp) return false; if (tok->isAssignmentOp()) return false; if (isLikelyStreamRead(cpp, tok)) return false; // bailout when we see ({..}) if (tok->str() == "{") return false; return isConstExpression(tok->astOperand1(), library, pure, cpp) && isConstExpression(tok->astOperand2(), library, pure, cpp); } bool isWithoutSideEffects(bool cpp, const Token* tok) { if (!cpp) return true; while (tok && tok->astOperand2() && tok->astOperand2()->str() != "(") tok = tok->astOperand2(); if (tok && tok->varId()) { const Variable* var = tok->variable(); return var && (!var->isClass() || var->isPointer() || var->isStlType()); } return true; } bool isUniqueExpression(const Token* tok) { if (!tok) return true; if (tok->function()) { const Function * fun = tok->function(); const Scope * scope = fun->nestedIn; if (!scope) return true; const std::string returnType = fun->retType ? fun->retType->name() : fun->retDef->stringifyList(fun->tokenDef); for (const Function& f:scope->functionList) { if (f.type != Function::eFunction) continue; const std::string freturnType = f.retType ? f.retType->name() : f.retDef->stringifyList(f.tokenDef); if (f.argumentList.size() == fun->argumentList.size() && returnType == freturnType && f.name() != fun->name()) { return false; } } } else if (tok->variable()) { const Variable * var = tok->variable(); const Scope * scope = var->scope(); if (!scope) return true; const Type * varType = var->type(); // Iterate over the variables in scope and the parameters of the function if possible const Function * fun = scope->function; const std::list* setOfVars[] = {&scope->varlist, fun ? &fun->argumentList : nullptr}; for (const std::list* vars:setOfVars) { if (!vars) continue; bool other = std::any_of(vars->cbegin(), vars->cend(), [=](const Variable &v) { if (varType) return v.type() && v.type()->name() == varType->name() && v.name() != var->name(); return v.isFloatingType() == var->isFloatingType() && v.isEnumType() == var->isEnumType() && v.isClass() == var->isClass() && v.isArray() == var->isArray() && v.isPointer() == var->isPointer() && v.name() != var->name(); }); if (other) return false; } } else if (!isUniqueExpression(tok->astOperand1())) { return false; } return isUniqueExpression(tok->astOperand2()); } static bool isEscaped(const Token* tok, bool functionsScope) { if (functionsScope) return Token::simpleMatch(tok, "throw"); else return Token::Match(tok, "return|throw"); } static bool isEscapedOrJump(const Token* tok, bool functionsScope) { if (functionsScope) return Token::simpleMatch(tok, "throw"); else return Token::Match(tok, "return|goto|throw|continue|break"); } bool isReturnScope(const Token * const endToken, const Library * library, bool functionScope) { if (!endToken || endToken->str() != "}") return false; const Token *prev = endToken->previous(); while (prev && Token::simpleMatch(prev->previous(), "; ;")) prev = prev->previous(); if (prev && Token::simpleMatch(prev->previous(), "} ;")) prev = prev->previous(); if (Token::simpleMatch(prev, "}")) { if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {")) return isReturnScope(prev, library, functionScope) && isReturnScope(prev->link()->tokAt(-2), library, functionScope); if (Token::simpleMatch(prev->link()->previous(), ") {") && Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "switch (") && !Token::findsimplematch(prev->link(), "break", prev)) { return true; } if (isEscaped(prev->link()->astTop(), functionScope)) return true; if (Token::Match(prev->link()->previous(), "[;{}] {")) return isReturnScope(prev, library, functionScope); } else if (Token::simpleMatch(prev, ";")) { if (Token::simpleMatch(prev->previous(), ") ;") && Token::Match(prev->linkAt(-1)->tokAt(-2), "[;{}] %name% (")) { const Token * ftok = prev->linkAt(-1)->previous(); const Function * function = ftok->function(); if (function) { if (function->isEscapeFunction()) return true; if (function->isAttributeNoreturn()) return true; } else if (library) { if (library->isnoreturn(ftok)) return true; } return false; } if (Token::simpleMatch(prev->previous(), ") ;") && prev->previous()->link() && isEscaped(prev->previous()->link()->astTop(), functionScope)) return true; if (isEscaped(prev->previous()->astTop(), functionScope)) return true; // return/goto statement prev = prev->previous(); while (prev && !Token::Match(prev, ";|{|}") && !isEscapedOrJump(prev, functionScope)) prev = prev->previous(); return prev && prev->isName(); } return false; } bool isVariableChangedByFunctionCall(const Token *tok, int indirect, nonneg int varid, const Settings *settings, bool *inconclusive) { if (!tok) return false; if (tok->varId() == varid) return isVariableChangedByFunctionCall(tok, indirect, settings, inconclusive); return isVariableChangedByFunctionCall(tok->astOperand1(), indirect, varid, settings, inconclusive) || isVariableChangedByFunctionCall(tok->astOperand2(), indirect, varid, settings, inconclusive); } bool isScopeBracket(const Token* tok) { if (!Token::Match(tok, "{|}")) return false; if (!tok->scope()) return false; if (tok->str() == "{") return tok->scope()->bodyStart == tok; if (tok->str() == "}") return tok->scope()->bodyEnd == tok; return false; } const Token * getTokenArgumentFunction(const Token * tok, int& argn) { argn = -1; { const Token *parent = tok->astParent(); if (parent && parent->isUnaryOp("&")) parent = parent->astParent(); while (parent && parent->isCast()) parent = parent->astParent(); // passing variable to subfunction? if (Token::Match(parent, "[(,{]")) ; else if (Token::simpleMatch(parent, ":")) { while (Token::Match(parent, "[?:]")) parent = parent->astParent(); while (Token::simpleMatch(parent, ",")) parent = parent->astParent(); if (!parent || parent->str() != "(") return nullptr; } else return nullptr; } // goto start of function call and get argn argn = 0; while (tok && !Token::simpleMatch(tok, ";") && !isScopeBracket(tok)) { if (tok->str() == ",") ++argn; else if (Token::Match(tok, ")|}")) tok = tok->link(); else if (Token::Match(tok->previous(), "%name% (|{")) break; else if (Token::Match(tok->previous(), "> (|{") && tok->previous()->link()) break; tok = tok->previous(); } if (!Token::Match(tok, "{|(")) return nullptr; tok = tok->previous(); if (tok && tok->link() && tok->str() == ">") tok = tok->link()->previous(); if (!Token::Match(tok, "%name% [({<]")) return nullptr; return tok; } bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive) { if (!tok) return false; const Token * const tok1 = tok; // address of variable const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&"); int argnr; tok = getTokenArgumentFunction(tok, argnr); if (!tok) return false; // not a function => variable not changed const Token * parenTok = tok->next(); if (Token::simpleMatch(parenTok, "<") && parenTok->link()) parenTok = parenTok->link()->next(); const bool possiblyPassedByReference = (parenTok->next() == tok1 || Token::Match(tok1->previous(), ", %name% [,)}]")); // Constructor call if (tok->variable() && tok->variable()->nameToken() == tok) { // Find constructor.. const int argCount = numberOfArguments(tok); const Scope *typeScope = tok->variable()->typeScope(); if (typeScope) { for (const Function &function : typeScope->functionList) { if (!function.isConstructor() || function.argCount() < argCount) continue; const Variable *arg = function.getArgumentVar(argnr); if (arg && arg->isReference() && !arg->isConst()) return true; } return false; } if (inconclusive) *inconclusive = true; return false; } if (!tok->function()) { // Check if direction (in, out, inout) is specified in the library configuration and use that if (!addressOf && settings) { const Library::ArgumentChecks::Direction argDirection = settings->library.getArgDirection(tok, 1 + argnr); if (argDirection == Library::ArgumentChecks::Direction::DIR_IN) return false; else if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT || argDirection == Library::ArgumentChecks::Direction::DIR_INOUT) { // With out or inout the direction of the content is specified, not a pointer itself, so ignore pointers for now const ValueType * const valueType = tok1->valueType(); if (valueType && valueType->pointer == indirect) { return true; } } } // if the library says 0 is invalid // => it is assumed that parameter is an in parameter (TODO: this is a bad heuristic) if (!addressOf && settings && settings->library.isnullargbad(tok, 1+argnr)) return false; // possible pass-by-reference => inconclusive if (possiblyPassedByReference) { if (inconclusive != nullptr) *inconclusive = true; return false; } // Safe guess: Assume that parameter is changed by function call return true; } const Variable *arg = tok->function()->getArgumentVar(argnr); if (addressOf || (indirect > 0 && arg && arg->isPointer())) { if (!(arg && arg->isConst())) return true; // If const is applied to the pointer, then the value can still be modified if (arg && Token::simpleMatch(arg->typeEndToken(), "* const")) return true; } return arg && !arg->isConst() && arg->isReference(); } bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, bool cpp, int depth) { if (!tok) return false; const Token *tok2 = tok; while (Token::simpleMatch(tok2->astParent(), "*") || (Token::simpleMatch(tok2->astParent(), ".") && !Token::simpleMatch(tok2->astParent()->astParent(), "(")) || (Token::simpleMatch(tok2->astParent(), "[") && tok2 == tok2->astParent()->astOperand1())) tok2 = tok2->astParent(); while (Token::simpleMatch(tok2->astParent(), "?") || (Token::simpleMatch(tok2->astParent(), ":") && Token::simpleMatch(tok2->astParent()->astParent(), "?"))) tok2 = tok2->astParent(); if (Token::Match(tok2->astParent(), "++|--")) return true; if (tok2->astParent() && tok2->astParent()->isAssignmentOp()) { if (tok2 == tok2->astParent()->astOperand1()) return true; // Check if assigning to a non-const lvalue const Variable * var = getLHSVariable(tok2->astParent()); if (var && var->isReference() && !var->isConst() && var->nameToken() && var->nameToken()->next() == tok2->astParent()) { if (!var->isLocal() || isVariableChanged(var, settings, cpp, depth - 1)) return true; } } if (isLikelyStreamRead(cpp, tok->previous())) return true; if (isLikelyStream(cpp, tok2)) return true; // Member function call if (tok->variable() && Token::Match(tok2->astParent(), ". %name%") && isFunctionCall(tok2->astParent()->next()) && tok2->astParent()->astOperand1() == tok2) { const Variable * var = tok->variable(); bool isConst = var && var->isConst(); if (!isConst && var) { const ValueType * valueType = var->valueType(); isConst = (valueType && valueType->pointer == 1 && valueType->constness == 1); } const Token *ftok = tok->tokAt(2); const Function * fun = ftok->function(); if (!isConst && (!fun || !fun->isConst())) return true; else return false; } const Token *ftok = tok2; while (ftok && (!Token::Match(ftok, "[({]") || ftok->isCast())) ftok = ftok->astParent(); if (ftok && Token::Match(ftok->link(), ")|} !!{")) { const Token * ptok = tok2; while (Token::Match(ptok->astParent(), ".|::|[")) ptok = ptok->astParent(); bool inconclusive = false; bool isChanged = isVariableChangedByFunctionCall(ptok, indirect, settings, &inconclusive); isChanged |= inconclusive; if (isChanged) return true; } const Token *parent = tok2->astParent(); while (Token::Match(parent, ".|::")) parent = parent->astParent(); if (parent && parent->tokType() == Token::eIncDecOp) return true; if (Token::simpleMatch(tok2->astParent(), ":") && tok2->astParent()->astParent() && Token::simpleMatch(tok2->astParent()->astParent()->previous(), "for (")) { const Token * varTok = tok2->astParent()->previous(); if (!varTok) return false; const Variable * loopVar = varTok->variable(); if (!loopVar) return false; if (!loopVar->isConst() && loopVar->isReference() && isVariableChanged(loopVar, settings, cpp, depth - 1)) return true; return false; } return false; } bool isVariableChanged(const Token *start, const Token *end, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth) { return findVariableChanged(start, end, 0, varid, globalvar, settings, cpp, depth) != nullptr; } Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth) { if (!precedes(start, end)) return nullptr; if (depth < 0) return start; for (Token *tok = start; tok != end; tok = tok->next()) { if (tok->varId() != varid) { if (globalvar && Token::Match(tok, "%name% (")) // TODO: Is global variable really changed by function call? return tok; continue; } if (isVariableChanged(tok, indirect, settings, cpp, depth)) return tok; } return nullptr; } const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth) { return findVariableChanged(const_cast(start), end, indirect, varid, globalvar, settings, cpp, depth); } bool isVariableChanged(const Variable * var, const Settings *settings, bool cpp, int depth) { if (!var) return false; if (!var->scope()) return false; const Token * start = var->declEndToken(); if (!start) return false; if (Token::Match(start, "; %varid% =", var->declarationId())) start = start->tokAt(2); return isVariableChanged(start->next(), var->scope()->bodyEnd, var->declarationId(), var->isGlobal(), settings, cpp, depth); } bool isVariablesChanged(const Token* start, const Token* end, int indirect, std::vector vars, const Settings* settings, bool cpp) { std::set varids; std::transform(vars.begin(), vars.end(), std::inserter(varids, varids.begin()), [](const Variable* var) { return var->declarationId(); }); const bool globalvar = std::any_of(vars.begin(), vars.end(), [](const Variable* var) { return var->isGlobal(); }); for (const Token* tok = start; tok != end; tok = tok->next()) { if (tok->varId() == 0 || varids.count(tok->varId()) == 0) { if (globalvar && Token::Match(tok, "%name% (")) // TODO: Is global variable really changed by function call? return true; continue; } if (isVariableChanged(tok, indirect, settings, cpp)) return true; } return false; } int numberOfArguments(const Token *start) { int arguments=0; const Token* const openBracket = start->next(); if (openBracket && openBracket->str()=="(" && openBracket->next() && openBracket->next()->str()!=")") { const Token* argument=openBracket->next(); while (argument) { ++arguments; argument = argument->nextArgument(); } } return arguments; } std::vector getArguments(const Token *ftok) { const Token* tok = ftok; if (Token::Match(tok, "%name% (|{")) tok = ftok->next(); if (!Token::Match(tok, "(|{|[")) return std::vector {}; const Token *startTok = tok->astOperand2(); if (!startTok && tok->next() != tok->link()) startTok = tok->astOperand1(); return astFlatten(startTok, ","); } const Token *findLambdaStartToken(const Token *last) { if (!last || last->str() != "}") return nullptr; const Token* tok = last->link(); if (Token::simpleMatch(tok->astParent(), "(")) tok = tok->astParent(); if (Token::simpleMatch(tok->astParent(), "[")) return tok->astParent(); return nullptr; } const Token *findLambdaEndToken(const Token *first) { if (!first || first->str() != "[") return nullptr; if (!Token::Match(first->link(), "] (|{")) return nullptr; if (first->astOperand1() != first->link()->next()) return nullptr; const Token * tok = first; if (tok->astOperand1() && tok->astOperand1()->str() == "(") tok = tok->astOperand1(); if (tok->astOperand1() && tok->astOperand1()->str() == "{") return tok->astOperand1()->link(); return nullptr; } bool isLikelyStream(bool cpp, const Token *stream) { if (!cpp) return false; if (!stream) return false; if (!Token::Match(stream->astParent(), "&|<<|>>") || !stream->astParent()->isBinaryOp()) return false; if (stream->astParent()->astOperand1() != stream) return false; return !astIsIntegral(stream, false); } bool isLikelyStreamRead(bool cpp, const Token *op) { if (!cpp) return false; if (!Token::Match(op, "&|>>") || !op->isBinaryOp()) return false; if (!Token::Match(op->astOperand2(), "%name%|.|*|[") && op->str() != op->astOperand2()->str()) return false; const Token *parent = op; while (parent->astParent() && parent->astParent()->str() == op->str()) parent = parent->astParent(); if (parent->astParent() && !Token::Match(parent->astParent(), "%oror%|&&|(|,|!")) return false; if (op->str() == "&" && parent->astParent()) return false; if (!parent->astOperand1() || !parent->astOperand2()) return false; return (!parent->astOperand1()->valueType() || !parent->astOperand1()->valueType()->isIntegral()); } bool isCPPCast(const Token* tok) { return tok && Token::simpleMatch(tok->previous(), "> (") && tok->astOperand2() && tok->astOperand1() && tok->astOperand1()->str().find("_cast") != std::string::npos; } bool isConstVarExpression(const Token *tok) { if (!tok) return false; if (Token::simpleMatch(tok->previous(), "sizeof (")) return true; if (Token::Match(tok->previous(), "%name% (")) { std::vector args = getArguments(tok); return std::all_of(args.begin(), args.end(), &isConstVarExpression); } if (isCPPCast(tok)) { return isConstVarExpression(tok->astOperand2()); } if (Token::Match(tok, "( %type%")) return isConstVarExpression(tok->astOperand1()); if (tok->str() == "::" && tok->hasKnownValue()) return isConstVarExpression(tok->astOperand2()); if (Token::Match(tok, "%cop%|[|.")) { if (tok->astOperand1() && !isConstVarExpression(tok->astOperand1())) return false; if (tok->astOperand2() && !isConstVarExpression(tok->astOperand2())) return false; return true; } if (Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL")) return true; if (tok->isEnumerator()) return true; if (tok->variable()) return tok->variable()->isConst(); return false; } static const Variable *getLHSVariableRecursive(const Token *tok) { if (!tok) return nullptr; if (Token::Match(tok, "*|&|&&|[")) { const Variable *var = getLHSVariableRecursive(tok->astOperand1()); if (var || Token::simpleMatch(tok, "[")) return var; return getLHSVariableRecursive(tok->astOperand2()); } if (Token::Match(tok->previous(), "this . %var%")) return tok->next()->variable(); return tok->variable(); } const Variable *getLHSVariable(const Token *tok) { if (!Token::Match(tok, "%assign%")) return nullptr; if (!tok->astOperand1()) return nullptr; if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable()) return tok->astOperand1()->variable(); return getLHSVariableRecursive(tok->astOperand1()); } static bool nonLocal(const Variable* var, bool deref) { return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || var->isStatic() || var->isReference() || var->isExtern(); } static bool hasGccCompoundStatement(const Token *tok) { if (!tok) return false; if (tok->str() == "{" && Token::simpleMatch(tok->previous(), "( {")) return true; return hasGccCompoundStatement(tok->astOperand1()) || hasGccCompoundStatement(tok->astOperand2()); } static bool hasFunctionCall(const Token *tok) { if (!tok) return false; if (Token::Match(tok, "%name% (")) // todo, const/pure function? return true; return hasFunctionCall(tok->astOperand1()) || hasFunctionCall(tok->astOperand2()); } static bool isUnchanged(const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local) { for (const Token *tok = startToken; tok != endToken; tok = tok->next()) { if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) // TODO: this is a quick bailout return false; if (tok->varId() <= 0 || exprVarIds.find(tok->varId()) == exprVarIds.end()) continue; const Token *parent = tok; while (parent->astParent() && !parent->astParent()->isAssignmentOp() && parent->astParent()->tokType() != Token::Type::eIncDecOp) { if (parent->str() == "," || parent->isUnaryOp("&")) // TODO: This is a quick bailout return false; parent = parent->astParent(); } if (parent->astParent()) { if (parent->astParent()->tokType() == Token::Type::eIncDecOp) return false; else if (parent->astParent()->isAssignmentOp() && parent == parent->astParent()->astOperand1()) return false; } } return true; } struct FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local, bool inInnerClass, int depth) { // Parse the given tokens if (++depth > 1000) return Result(Result::Type::BAILOUT); for (const Token* tok = startToken; precedes(tok, endToken); tok = tok->next()) { if (Token::simpleMatch(tok, "try {")) { // TODO: handle try return Result(Result::Type::BAILOUT); } if (Token::simpleMatch(tok, "break ;")) { return Result(Result::Type::BREAK, tok); } if (Token::simpleMatch(tok, "goto")) return Result(Result::Type::BAILOUT); if (!inInnerClass && tok->str() == "{" && tok->scope()->isClassOrStruct()) { // skip returns from local class definition FwdAnalysis::Result result = checkRecursive(expr, tok, tok->link(), exprVarIds, local, true, depth); if (result.type != Result::Type::NONE) return result; tok=tok->link(); } if (tok->str() == "continue") // TODO return Result(Result::Type::BAILOUT); if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { tok = lambdaEndToken; const Result lambdaResult = checkRecursive(expr, lambdaEndToken->link()->next(), lambdaEndToken, exprVarIds, local, inInnerClass, depth); if (lambdaResult.type == Result::Type::READ || lambdaResult.type == Result::Type::BAILOUT) return lambdaResult; } if (Token::Match(tok, "return|throw")) { // TODO: Handle these better // Is expr variable used in expression? const Token *end = tok->findExpressionStartEndTokens().second->next(); for (const Token *tok2 = tok; tok2 != end; tok2 = tok2->next()) { if (!local && Token::Match(tok2, "%name% (")) return Result(Result::Type::READ); if (tok2->varId() && exprVarIds.find(tok2->varId()) != exprVarIds.end()) return Result(Result::Type::READ); } // #9167: if the return is inside an inner class, it does not tell us anything if (!inInnerClass) { if (!local && mWhat == What::Reassign) return Result(Result::Type::BAILOUT); return Result(Result::Type::RETURN); } } if (tok->str() == "}") { // Known value => possible value if (tok->scope() == expr->scope()) mValueFlowKnown = false; Scope::ScopeType scopeType = tok->scope()->type; if (scopeType == Scope::eWhile || scopeType == Scope::eFor || scopeType == Scope::eDo) { // check condition const Token *conditionStart = nullptr; const Token *conditionEnd = nullptr; if (Token::simpleMatch(tok->link()->previous(), ") {")) { conditionEnd = tok->link()->previous(); conditionStart = conditionEnd->link(); } else if (Token::simpleMatch(tok->link()->previous(), "do {") && Token::simpleMatch(tok, "} while (")) { conditionStart = tok->tokAt(2); conditionEnd = conditionStart->link(); } if (conditionStart && conditionEnd) { bool used = false; for (const Token *condTok = conditionStart; condTok != conditionEnd; condTok = condTok->next()) { if (exprVarIds.find(condTok->varId()) != exprVarIds.end()) used = true; } if (used) return Result(Result::Type::BAILOUT); } // check loop body again.. const struct FwdAnalysis::Result &result = checkRecursive(expr, tok->link(), tok, exprVarIds, local, inInnerClass, depth); if (result.type == Result::Type::BAILOUT || result.type == Result::Type::READ) return result; } } if (Token::simpleMatch(tok, "else {")) tok = tok->linkAt(1); if (Token::simpleMatch(tok, "asm (")) return Result(Result::Type::BAILOUT); if (mWhat == What::ValueFlow && (Token::Match(tok, "while|for (") || Token::simpleMatch(tok, "do {"))) { const Token *bodyStart = nullptr; const Token *conditionStart = nullptr; if (Token::simpleMatch(tok, "do {")) { bodyStart = tok->next(); if (Token::simpleMatch(bodyStart->link(), "} while (")) conditionStart = bodyStart->link()->tokAt(2); } else { conditionStart = tok->next(); if (Token::simpleMatch(conditionStart->link(), ") {")) bodyStart = conditionStart->link()->next(); } if (!bodyStart || !conditionStart) return Result(Result::Type::BAILOUT); // Is expr changed in condition? if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) return Result(Result::Type::BAILOUT); // Is expr changed in loop body? if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) return Result(Result::Type::BAILOUT); } if (mWhat == What::ValueFlow && Token::simpleMatch(tok, "if (") && Token::simpleMatch(tok->linkAt(1), ") {")) { const Token *bodyStart = tok->linkAt(1)->next(); const Token *conditionStart = tok->next(); const Token *condTok = conditionStart->astOperand2(); if (condTok->hasKnownIntValue()) { bool cond = condTok->values().front().intvalue; if (cond) { FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth); if (result.type != Result::Type::NONE) return result; } else if (Token::simpleMatch(bodyStart->link(), "} else {")) { bodyStart = bodyStart->link()->tokAt(2); FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth); if (result.type != Result::Type::NONE) return result; } } tok = bodyStart->link(); if (isReturnScope(tok, &mLibrary)) return Result(Result::Type::BAILOUT); if (Token::simpleMatch(tok, "} else {")) tok = tok->linkAt(2); if (!tok) return Result(Result::Type::BAILOUT); // Is expr changed in condition? if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) return Result(Result::Type::BAILOUT); // Is expr changed in condition body? if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) return Result(Result::Type::BAILOUT); } if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { // TODO: this is a quick bailout return Result(Result::Type::BAILOUT); } if (mWhat == What::Reassign && Token::simpleMatch(tok, ";") && Token::simpleMatch(tok->astParent(), ";") && Token::simpleMatch(tok->astParent()->astParent(), "(") && Token::simpleMatch(tok->astParent()->astParent()->previous(), "for (") && !isUnchanged(tok, tok->astParent()->astParent()->link(), exprVarIds, local)) // TODO: This is a quick bailout to avoid FP #9420, there are false negatives (TODO_ASSERT_EQUALS) return Result(Result::Type::BAILOUT); if (expr->isName() && Token::Match(tok, "%name% (") && tok->str().find("<") != std::string::npos && tok->str().find(expr->str()) != std::string::npos) return Result(Result::Type::BAILOUT); if (exprVarIds.find(tok->varId()) != exprVarIds.end()) { const Token *parent = tok; bool other = false; bool same = tok->astParent() && isSameExpression(mCpp, false, expr, tok, mLibrary, true, false, nullptr); while (!same && Token::Match(parent->astParent(), "*|.|::|[|(|%cop%")) { parent = parent->astParent(); if (parent && isSameExpression(mCpp, false, expr, parent, mLibrary, true, false, nullptr)) { same = true; if (mWhat == What::ValueFlow) { KnownAndToken v; v.known = mValueFlowKnown; v.token = parent; mValueFlow.push_back(v); } } if (Token::Match(parent, ". %var%") && parent->next()->varId() && exprVarIds.find(parent->next()->varId()) == exprVarIds.end()) { other = true; break; } } if (mWhat != What::ValueFlow && same && Token::simpleMatch(parent->astParent(), "[") && parent == parent->astParent()->astOperand2()) { return Result(Result::Type::READ); } if (other) continue; if (Token::simpleMatch(parent->astParent(), "=") && parent == parent->astParent()->astOperand1()) { if (!local && hasFunctionCall(parent->astParent()->astOperand2())) { // TODO: this is a quick bailout return Result(Result::Type::BAILOUT); } if (hasOperand(parent->astParent()->astOperand2(), expr)) { if (mWhat == What::Reassign) return Result(Result::Type::READ); continue; } // ({ .. }) if (hasGccCompoundStatement(parent->astParent()->astOperand2())) return Result(Result::Type::BAILOUT); const bool reassign = isSameExpression(mCpp, false, expr, parent, mLibrary, false, false, nullptr); if (reassign) return Result(Result::Type::WRITE, parent->astParent()); return Result(Result::Type::READ); } else if (mWhat == What::Reassign && parent->valueType() && parent->valueType()->pointer && Token::Match(parent->astParent(), "%assign%") && parent == parent->astParent()->astOperand1()) { return Result(Result::Type::READ); } else if (Token::Match(parent->astParent(), "%assign%") && !parent->astParent()->astParent() && parent == parent->astParent()->astOperand1()) { if (mWhat == What::Reassign) return Result(Result::Type::BAILOUT, parent->astParent()); continue; } else { // TODO: this is a quick bailout return Result(Result::Type::BAILOUT, parent->astParent()); } } if (Token::Match(tok, ")|do {")) { if (tok->str() == ")" && Token::simpleMatch(tok->link()->previous(), "switch (")) // TODO: parse switch return Result(Result::Type::BAILOUT); const Result &result1 = checkRecursive(expr, tok->tokAt(2), tok->linkAt(1), exprVarIds, local, inInnerClass, depth); if (result1.type == Result::Type::READ || result1.type == Result::Type::BAILOUT) return result1; if (mWhat == What::ValueFlow && result1.type == Result::Type::WRITE) mValueFlowKnown = false; if (Token::simpleMatch(tok->linkAt(1), "} else {")) { const Token *elseStart = tok->linkAt(1)->tokAt(2); const Result &result2 = checkRecursive(expr, elseStart, elseStart->link(), exprVarIds, local, inInnerClass, depth); if (mWhat == What::ValueFlow && result2.type == Result::Type::WRITE) mValueFlowKnown = false; if (result2.type == Result::Type::READ || result2.type == Result::Type::BAILOUT) return result2; if (result1.type == Result::Type::WRITE && result2.type == Result::Type::WRITE) return result1; tok = elseStart->link(); } else { tok = tok->linkAt(1); } } } return Result(Result::Type::NONE); } bool FwdAnalysis::isGlobalData(const Token *expr) const { bool globalData = false; visitAstNodes(expr, [&](const Token *tok) { if (tok->varId() && !tok->variable()) { // Bailout, this is probably global globalData = true; return ChildrenToVisit::none; } if (tok->originalName() == "->") { // TODO check if pointer points at local data globalData = true; return ChildrenToVisit::none; } else if (Token::Match(tok, "[*[]") && tok->astOperand1() && tok->astOperand1()->variable()) { // TODO check if pointer points at local data const Variable *lhsvar = tok->astOperand1()->variable(); const ValueType *lhstype = tok->astOperand1()->valueType(); if (lhsvar->isPointer()) { globalData = true; return ChildrenToVisit::none; } else if (lhsvar->isArgument() && lhsvar->isArray()) { globalData = true; return ChildrenToVisit::none; } else if (lhsvar->isArgument() && (!lhstype || (lhstype->type <= ValueType::Type::VOID && !lhstype->container))) { globalData = true; return ChildrenToVisit::none; } } if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") { globalData = true; return ChildrenToVisit::none; } if (tok->variable()) { // TODO : Check references if (tok->variable()->isReference() && tok != tok->variable()->nameToken()) { globalData = true; return ChildrenToVisit::none; } if (tok->variable()->isExtern()) { globalData = true; return ChildrenToVisit::none; } if (tok->previous()->str() != "." && !tok->variable()->isLocal() && !tok->variable()->isArgument()) { globalData = true; return ChildrenToVisit::none; } if (tok->variable()->isArgument() && tok->variable()->isPointer() && tok != expr) { globalData = true; return ChildrenToVisit::none; } if (tok->variable()->isPointerArray()) { globalData = true; return ChildrenToVisit::none; } } // Unknown argument type => it might be some reference type.. if (mCpp && tok->str() == "." && tok->astOperand1() && tok->astOperand1()->variable() && !tok->astOperand1()->valueType()) { globalData = true; return ChildrenToVisit::none; } if (Token::Match(tok, ".|[")) return ChildrenToVisit::op1; return ChildrenToVisit::op1_and_op2; }); return globalData; } std::set FwdAnalysis::getExprVarIds(const Token* expr, bool* localOut, bool* unknownVarIdOut) const { // all variable ids in expr. std::set exprVarIds; bool local = true; bool unknownVarId = false; visitAstNodes(expr, [&](const Token *tok) { if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") { // unknown variable unknownVarId = true; return ChildrenToVisit::none; } if (tok->varId() > 0) { exprVarIds.insert(tok->varId()); if (!Token::simpleMatch(tok->previous(), ".")) { const Variable *var = tok->variable(); if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && !isGlobalData(var->nameToken()->next()->astOperand2())) return ChildrenToVisit::none; const bool deref = tok->astParent() && (tok->astParent()->isUnaryOp("*") || (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); local &= !nonLocal(tok->variable(), deref); } } return ChildrenToVisit::op1_and_op2; }); if (localOut) *localOut = local; if (unknownVarIdOut) *unknownVarIdOut = unknownVarId; return exprVarIds; } FwdAnalysis::Result FwdAnalysis::check(const Token* expr, const Token* startToken, const Token* endToken) { // all variable ids in expr. bool local = true; bool unknownVarId = false; std::set exprVarIds = getExprVarIds(expr, &local, &unknownVarId); if (unknownVarId) return Result(FwdAnalysis::Result::Type::BAILOUT); if (mWhat == What::Reassign && isGlobalData(expr)) local = false; // In unused values checking we do not want to check assignments to // global data. if (mWhat == What::UnusedValue && isGlobalData(expr)) return Result(FwdAnalysis::Result::Type::BAILOUT); Result result = checkRecursive(expr, startToken, endToken, exprVarIds, local, false); // Break => continue checking in outer scope while (mWhat!=What::ValueFlow && result.type == FwdAnalysis::Result::Type::BREAK) { const Scope *s = result.token->scope(); while (s->type == Scope::eIf) s = s->nestedIn; if (s->type != Scope::eSwitch && s->type != Scope::eWhile && s->type != Scope::eFor) break; result = checkRecursive(expr, s->bodyEnd->next(), endToken, exprVarIds, local, false); } return result; } bool FwdAnalysis::hasOperand(const Token *tok, const Token *lhs) const { if (!tok) return false; if (isSameExpression(mCpp, false, tok, lhs, mLibrary, false, false, nullptr)) return true; return hasOperand(tok->astOperand1(), lhs) || hasOperand(tok->astOperand2(), lhs); } const Token *FwdAnalysis::reassign(const Token *expr, const Token *startToken, const Token *endToken) { mWhat = What::Reassign; Result result = check(expr, startToken, endToken); return result.type == FwdAnalysis::Result::Type::WRITE ? result.token : nullptr; } bool FwdAnalysis::unusedValue(const Token *expr, const Token *startToken, const Token *endToken) { if (isEscapedAlias(expr)) return false; mWhat = What::UnusedValue; Result result = check(expr, startToken, endToken); return (result.type == FwdAnalysis::Result::Type::NONE || result.type == FwdAnalysis::Result::Type::RETURN) && !possiblyAliased(expr, startToken); } std::vector FwdAnalysis::valueFlow(const Token *expr, const Token *startToken, const Token *endToken) { mWhat = What::ValueFlow; mValueFlowKnown = true; check(expr, startToken, endToken); return mValueFlow; } bool FwdAnalysis::possiblyAliased(const Token *expr, const Token *startToken) const { if (expr->isUnaryOp("*")) return true; const bool macro = false; const bool pure = false; const bool followVar = false; for (const Token *tok = startToken; tok; tok = tok->previous()) { if (tok->str() == "{" && tok->scope()->type == Scope::eFunction) break; if (Token::Match(tok, "%name% (") && !Token::Match(tok, "if|while|for")) { // Is argument passed by reference? const std::vector args = getArguments(tok); for (int argnr = 0; argnr < args.size(); ++argnr) { if (!Token::Match(args[argnr], "%name%|.|::")) continue; if (tok->function() && tok->function()->getArgumentVar(argnr) && !tok->function()->getArgumentVar(argnr)->isReference() && !tok->function()->isConst()) continue; for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { if (isSameExpression(mCpp, macro, subexpr, args[argnr], mLibrary, pure, followVar)) return true; } } continue; } const Token *addrOf = nullptr; if (Token::Match(tok, "& %name% =")) addrOf = tok->tokAt(2)->astOperand2(); else if (tok->isUnaryOp("&")) addrOf = tok->astOperand1(); else if (Token::simpleMatch(tok, "std :: ref (")) addrOf = tok->tokAt(3)->astOperand2(); else continue; for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { if (isSameExpression(mCpp, macro, subexpr, addrOf, mLibrary, pure, followVar)) return true; } } return false; } bool FwdAnalysis::isEscapedAlias(const Token* expr) { for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { for (const ValueFlow::Value &val : subexpr->values()) { if (!val.isLocalLifetimeValue()) continue; const Variable* var = val.tokvalue->variable(); if (!var) continue; if (!var->isLocal()) return true; if (var->isArgument()) return true; } } return false; } bool FwdAnalysis::isNullOperand(const Token *expr) { if (!expr) return false; if (Token::Match(expr, "( %name% %name%| * )") && Token::Match(expr->astOperand1(), "0|NULL|nullptr")) return true; return Token::Match(expr, "NULL|nullptr"); } cppcheck-1.90/lib/astutils.h000066400000000000000000000244221357737443600160410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef astutilsH #define astutilsH //--------------------------------------------------------------------------- #include #include #include #include "errorlogger.h" #include "utils.h" class Library; class Settings; class Scope; class Token; class Variable; enum class ChildrenToVisit { none, op1, op2, op1_and_op2, done // found what we looked for, don't visit any more children }; /** * Visit AST nodes recursively. The order is not "well defined" */ void visitAstNodes(const Token *ast, std::function visitor); std::vector astFlatten(const Token* tok, const char* op); bool astHasToken(const Token* root, const Token * tok); bool astHasVar(const Token * tok, nonneg int varid); /** Is expression a 'signed char' if no promotion is used */ bool astIsSignedChar(const Token *tok); /** Is expression a 'char' if no promotion is used? */ bool astIsUnknownSignChar(const Token *tok); /** Is expression of integral type? */ bool astIsIntegral(const Token *tok, bool unknown); /** Is expression of floating point type? */ bool astIsFloat(const Token *tok, bool unknown); /** Is expression of boolean type? */ bool astIsBool(const Token *tok); bool astIsPointer(const Token *tok); bool astIsSmartPointer(const Token* tok); bool astIsIterator(const Token *tok); bool astIsContainer(const Token *tok); /** * Get canonical type of expression. const/static/etc are not included and neither *&. * For example: * Expression type Return * std::string std::string * int * int * static const int int * std::vector std::vector */ std::string astCanonicalType(const Token *expr); /** Is given syntax tree a variable comparison against value */ const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr); bool isTemporary(bool cpp, const Token* tok, const Library* library); const Token * nextAfterAstRightmostLeaf(const Token * tok); Token* astParentSkipParens(Token* tok); const Token* astParentSkipParens(const Token* tok); const Token* getParentMember(const Token * tok); bool precedes(const Token * tok1, const Token * tok2); bool exprDependsOnThis(const Token* expr, nonneg int depth = 0); bool isSameExpression(bool cpp, bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); bool isEqualKnownValue(const Token * const tok1, const Token * const tok2); bool isDifferentKnownValues(const Token * const tok1, const Token * const tok2); /** * Are two conditions opposite * @param isNot do you want to know if cond1 is !cond2 or if cond1 and cond2 are non-overlapping. true: cond1==!cond2 false: cond1==true => cond2==false * @param cpp c++ file * @param cond1 condition1 * @param cond2 condition2 * @param library files data * @param pure boolean */ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token * const cond2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * const tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp); bool isWithoutSideEffects(bool cpp, const Token* tok); bool isUniqueExpression(const Token* tok); /** Is scope a return scope (scope will unconditionally return) */ bool isReturnScope(const Token * const endToken, const Library * library=nullptr, bool functionScope=false); /// Return the token to the function and the argument number const Token * getTokenArgumentFunction(const Token * tok, int& argn); /** Is variable changed by function call? * In case the answer of the question is inconclusive, e.g. because the function declaration is not known * the return value is false and the output parameter inconclusive is set to true * * @param tok ast tree * @param varid Variable Id * @param settings program settings * @param inconclusive pointer to output variable which indicates that the answer of the question is inconclusive */ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, nonneg int varid, const Settings *settings, bool *inconclusive); /** Is variable changed by function call? * In case the answer of the question is inconclusive, e.g. because the function declaration is not known * the return value is false and the output parameter inconclusive is set to true * * @param tok token of variable in function call * @param settings program settings * @param inconclusive pointer to output variable which indicates that the answer of the question is inconclusive */ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive); /** Is variable changed in block of code? */ bool isVariableChanged(const Token *start, const Token *end, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth = 20); bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, bool cpp, int depth = 20); bool isVariableChanged(const Variable * var, const Settings *settings, bool cpp, int depth = 20); bool isVariablesChanged(const Token* start, const Token* end, int indirect, std::vector vars, const Settings* settings, bool cpp); const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth = 20); Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth = 20); /// If token is an alias if another variable bool isAliasOf(const Token *tok, nonneg int varid); bool isAliased(const Variable *var); /** Determines the number of arguments - if token is a function call or macro * @param start token which is supposed to be the function/macro name. * \return Number of arguments */ int numberOfArguments(const Token *start); /** * Get arguments (AST) */ std::vector getArguments(const Token *ftok); const Token *findLambdaStartToken(const Token *last); /** * find lambda function end token * \param first The [ token * \return nullptr or the } */ const Token *findLambdaEndToken(const Token *first); bool isLikelyStream(bool cpp, const Token *stream); /** * do we see a likely write of rhs through overloaded operator * s >> x; * a & x; */ bool isLikelyStreamRead(bool cpp, const Token *op); bool isCPPCast(const Token* tok); bool isConstVarExpression(const Token *tok); const Variable *getLHSVariable(const Token *tok); bool isScopeBracket(const Token* tok); /** * Forward data flow analysis for checks * - unused value * - redundant assignment * - valueflow analysis */ class FwdAnalysis { public: FwdAnalysis(bool cpp, const Library &library) : mCpp(cpp), mLibrary(library), mWhat(What::Reassign), mValueFlowKnown(true) {} bool hasOperand(const Token *tok, const Token *lhs) const; /** * Check if "expr" is reassigned. The "expr" can be a tree (x.y[12]). * @param expr Symbolic expression to perform forward analysis for * @param startToken First token in forward analysis * @param endToken Last token in forward analysis * @return Token where expr is reassigned. If it's not reassigned then nullptr is returned. */ const Token *reassign(const Token *expr, const Token *startToken, const Token *endToken); /** * Check if "expr" is used. The "expr" can be a tree (x.y[12]). * @param expr Symbolic expression to perform forward analysis for * @param startToken First token in forward analysis * @param endToken Last token in forward analysis * @return true if expr is used. */ bool unusedValue(const Token *expr, const Token *startToken, const Token *endToken); struct KnownAndToken { bool known; const Token *token; }; std::vector valueFlow(const Token *expr, const Token *startToken, const Token *endToken); /** Is there some possible alias for given expression */ bool possiblyAliased(const Token *expr, const Token *startToken) const; std::set getExprVarIds(const Token* expr, bool* localOut = nullptr, bool* unknownVarIdOut = nullptr) const; static bool isNullOperand(const Token *expr); private: static bool isEscapedAlias(const Token* expr); /** Result of forward analysis */ struct Result { enum class Type { NONE, READ, WRITE, BREAK, RETURN, BAILOUT } type; explicit Result(Type type) : type(type), token(nullptr) {} Result(Type type, const Token *token) : type(type), token(token) {} const Token *token; }; struct Result check(const Token *expr, const Token *startToken, const Token *endToken); struct Result checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local, bool inInnerClass, int depth=0); // Is expression a l-value global data? bool isGlobalData(const Token *expr) const; const bool mCpp; const Library &mLibrary; enum class What { Reassign, UnusedValue, ValueFlow } mWhat; std::vector mValueFlow; bool mValueFlowKnown; }; #endif // astutilsH cppcheck-1.90/lib/check.cpp000066400000000000000000000046051357737443600156020ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "check.h" #include //--------------------------------------------------------------------------- Check::Check(const std::string &aname) : mTokenizer(nullptr), mSettings(nullptr), mErrorLogger(nullptr), mName(aname) { for (std::list::iterator i = instances().begin(); i != instances().end(); ++i) { if ((*i)->name() > aname) { instances().insert(i, this); return; } } instances().push_back(this); } void Check::reportError(const ErrorLogger::ErrorMessage &errmsg) { std::cout << errmsg.toXML() << std::endl; } bool Check::wrongData(const Token *tok, bool condition, const char *str) { #if defined(DACA2) || defined(UNSTABLE) if (condition) { reportError(tok, Severity::debug, "DacaWrongData", "Wrong data detected by condition " + std::string(str)); } #else (void)tok; (void)str; #endif return condition; } std::list &Check::instances() { #ifdef __SVR4 // Under Solaris, destructors are called in wrong order which causes a segmentation fault. // This fix ensures pointer remains valid and reachable until program terminates. static std::list *_instances= new std::list; return *_instances; #else static std::list _instances; return _instances; #endif } std::string Check::getMessageId(const ValueFlow::Value &value, const char id[]) { if (value.condition != nullptr) return id + std::string("Cond"); if (value.safe) return std::string("safe") + (char)std::toupper(id[0]) + (id + 1); return id; } cppcheck-1.90/lib/check.h000066400000000000000000000156341357737443600152530ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkH #define checkH //--------------------------------------------------------------------------- #include "config.h" #include "errorlogger.h" #include "settings.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include namespace tinyxml2 { class XMLElement; } namespace CTU { class FileInfo; } /** Use WRONG_DATA in checkers to mark conditions that check that data is correct */ #define WRONG_DATA(COND, TOK) (wrongData((TOK), (COND), #COND)) /// @addtogroup Core /// @{ /** * @brief Interface class that cppcheck uses to communicate with the checks. * All checking classes must inherit from this class */ class CPPCHECKLIB Check { public: /** This constructor is used when registering the CheckClass */ explicit Check(const std::string &aname); /** This constructor is used when running checks. */ Check(const std::string &aname, const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger), mName(aname) { } virtual ~Check() { if (!mTokenizer) instances().remove(this); } /** List of registered check classes. This is used by Cppcheck to run checks and generate documentation */ static std::list &instances(); /** run checks, the token list is not simplified */ virtual void runChecks(const Tokenizer *, const Settings *, ErrorLogger *) = 0; /** get error messages */ virtual void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const = 0; /** class name, used to generate documentation */ const std::string& name() const { return mName; } /** get information about this class, used to generate documentation */ virtual std::string classInfo() const = 0; /** * Write given error to errorlogger or to out stream in xml format. * This is for for printout out the error list with --errorlist * @param errmsg Error message to write */ static void reportError(const ErrorLogger::ErrorMessage &errmsg); /** Base class used for whole-program analysis */ class CPPCHECKLIB FileInfo { public: FileInfo() {} virtual ~FileInfo() {} virtual std::string toString() const { return std::string(); } }; virtual FileInfo * getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { (void)tokenizer; (void)settings; return nullptr; } virtual FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { (void)xmlElement; return nullptr; } // Return true if an error is reported. virtual bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { (void)ctu; (void)fileInfo; (void)settings; (void)errorLogger; return false; } static std::string getMessageId(const ValueFlow::Value &value, const char id[]); protected: const Tokenizer * const mTokenizer; const Settings * const mSettings; ErrorLogger * const mErrorLogger; /** report an error */ template void reportError(const Token *tok, const Severity::SeverityType severity, const T id, const U msg) { reportError(tok, severity, id, msg, CWE(0U), false); } /** report an error */ template void reportError(const Token *tok, const Severity::SeverityType severity, const T id, const U msg, const CWE &cwe, bool inconclusive) { const std::list callstack(1, tok); reportError(callstack, severity, id, msg, cwe, inconclusive); } /** report an error */ template void reportError(const std::list &callstack, Severity::SeverityType severity, const T id, const U msg) { reportError(callstack, severity, id, msg, CWE(0U), false); } /** report an error */ template void reportError(const std::list &callstack, Severity::SeverityType severity, const T id, const U msg, const CWE &cwe, bool inconclusive) { const ErrorLogger::ErrorMessage errmsg(callstack, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, inconclusive); if (mErrorLogger) mErrorLogger->reportErr(errmsg); else reportError(errmsg); } void reportError(const ErrorPath &errorPath, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, bool inconclusive) { const ErrorLogger::ErrorMessage errmsg(errorPath, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, inconclusive); if (mErrorLogger) mErrorLogger->reportErr(errmsg); else reportError(errmsg); } ErrorPath getErrorPath(const Token *errtok, const ValueFlow::Value *value, const std::string &bug) const { ErrorPath errorPath; if (!value) { errorPath.emplace_back(errtok,bug); } else if (mSettings->verbose || mSettings->xml || !mSettings->templateLocation.empty()) { errorPath = value->errorPath; errorPath.emplace_back(errtok,bug); } else { if (value->condition) errorPath.emplace_back(value->condition, "condition '" + value->condition->expressionString() + "'"); //else if (!value->isKnown() || value->defaultArg) // errorPath = value->callstack; errorPath.emplace_back(errtok,bug); } return errorPath; } /** * Use WRONG_DATA in checkers when you check for wrong data. That * will call this method */ bool wrongData(const Token *tok, bool condition, const char *str); /** disabled assignment operator and copy constructor */ void operator=(const Check &) = delete; Check(const Check &) = delete; private: const std::string mName; }; /// @} //--------------------------------------------------------------------------- #endif // checkH cppcheck-1.90/lib/check64bit.cpp000066400000000000000000000162041357737443600164510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // 64-bit portability //--------------------------------------------------------------------------- #include "check64bit.h" #include "errorlogger.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include //--------------------------------------------------------------------------- // CWE ids used static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior // Register this check class (by creating a static instance of it) namespace { Check64BitPortability instance; } void Check64BitPortability::pointerassignment() { if (!mSettings->isEnabled(Settings::PORTABILITY)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // Check return values for (const Scope * scope : symbolDatabase->functionScopes) { if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body continue; bool retPointer = false; if (scope->function->token->strAt(-1) == "*") // Function returns a pointer retPointer = true; else if (Token::Match(scope->function->token->previous(), "int|long|DWORD")) // Function returns an integer ; else continue; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // skip nested functions if (tok->str() == "{") { if (tok->scope()->type == Scope::ScopeType::eFunction || tok->scope()->type == Scope::ScopeType::eLambda) tok = tok->link(); } if (tok->str() != "return") continue; if (!tok->astOperand1() || tok->astOperand1()->isNumber()) continue; const ValueType * const returnType = tok->astOperand1()->valueType(); if (!returnType) continue; if (retPointer && !returnType->typeScope && returnType->pointer == 0U) returnIntegerError(tok); if (!retPointer && returnType->pointer >= 1U) returnPointerError(tok); } } // Check assignments for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() != "=") continue; const ValueType *lhstype = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType *rhstype = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; if (!lhstype || !rhstype) continue; // Assign integer to pointer.. if (lhstype->pointer >= 1U && !tok->astOperand2()->isNumber() && rhstype->pointer == 0U && rhstype->originalTypeName.empty() && rhstype->type == ValueType::Type::INT) assignmentIntegerToAddressError(tok); // Assign pointer to integer.. if (rhstype->pointer >= 1U && lhstype->pointer == 0U && lhstype->originalTypeName.empty() && lhstype->isIntegral() && lhstype->type >= ValueType::Type::CHAR && lhstype->type <= ValueType::Type::INT) assignmentAddressToIntegerError(tok); } } } void Check64BitPortability::assignmentAddressToIntegerError(const Token *tok) { reportError(tok, Severity::portability, "AssignmentAddressToInteger", "Assigning a pointer to an integer is not portable.\n" "Assigning a pointer to an integer (int/long/etc) is not portable across different platforms and " "compilers. For example in 32-bit Windows and linux they are same width, but in 64-bit Windows and linux " "they are of different width. In worst case you end up assigning 64-bit address to 32-bit integer. The safe " "way is to store addresses only in pointer types (or typedefs like uintptr_t).", CWE758, false); } void Check64BitPortability::assignmentIntegerToAddressError(const Token *tok) { reportError(tok, Severity::portability, "AssignmentIntegerToAddress", "Assigning an integer to a pointer is not portable.\n" "Assigning an integer (int/long/etc) to a pointer is not portable across different platforms and " "compilers. For example in 32-bit Windows and linux they are same width, but in 64-bit Windows and linux " "they are of different width. In worst case you end up assigning 64-bit integer to 32-bit pointer. The safe " "way is to store addresses only in pointer types (or typedefs like uintptr_t).", CWE758, false); } void Check64BitPortability::returnPointerError(const Token *tok) { reportError(tok, Severity::portability, "CastAddressToIntegerAtReturn", "Returning an address value in a function with integer return type is not portable.\n" "Returning an address value in a function with integer (int/long/etc) return type is not portable across " "different platforms and compilers. For example in 32-bit Windows and Linux they are same width, but in " "64-bit Windows and Linux they are of different width. In worst case you end up casting 64-bit address down " "to 32-bit integer. The safe way is to always return an integer.", CWE758, false); } void Check64BitPortability::returnIntegerError(const Token *tok) { reportError(tok, Severity::portability, "CastIntegerToAddressAtReturn", "Returning an integer in a function with pointer return type is not portable.\n" "Returning an integer (int/long/etc) in a function with pointer return type is not portable across different " "platforms and compilers. For example in 32-bit Windows and Linux they are same width, but in 64-bit Windows " "and Linux they are of different width. In worst case you end up casting 64-bit integer down to 32-bit pointer. " "The safe way is to always return a pointer.", CWE758, false); } cppcheck-1.90/lib/check64bit.h000066400000000000000000000056561357737443600161270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef check64bitH #define check64bitH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** * @brief Check for 64-bit portability issues */ class CPPCHECKLIB Check64BitPortability : public Check { public: /** This constructor is used when registering the Check64BitPortability */ Check64BitPortability() : Check(myName()) { } /** This constructor is used when running checks. */ Check64BitPortability(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { Check64BitPortability check64BitPortability(tokenizer, settings, errorLogger); check64BitPortability.pointerassignment(); } /** Check for pointer assignment */ void pointerassignment(); private: void assignmentAddressToIntegerError(const Token *tok); void assignmentIntegerToAddressError(const Token *tok); void returnIntegerError(const Token *tok); void returnPointerError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { Check64BitPortability c(nullptr, settings, errorLogger); c.assignmentAddressToIntegerError(nullptr); c.assignmentIntegerToAddressError(nullptr); c.returnIntegerError(nullptr); c.returnPointerError(nullptr); } static std::string myName() { return "64-bit portability"; } std::string classInfo() const OVERRIDE { return "Check if there is 64-bit portability issues:\n" "- assign address to/from int/long\n" "- casting address from/to integer when returning from function\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // check64bitH cppcheck-1.90/lib/checkassert.cpp000066400000000000000000000134301357737443600170200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // You should not write statements with side effects in assert() //--------------------------------------------------------------------------- #include "checkassert.h" #include "errorlogger.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" //--------------------------------------------------------------------------- // CWE ids used static const struct CWE CWE398(398U); // Indicator of Poor Code Quality // Register this check class (by creating a static instance of it) namespace { CheckAssert instance; } void CheckAssert::assertWithSideEffects() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Token* tok = mTokenizer->list.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "assert (")) continue; const Token *endTok = tok->next()->link(); for (const Token* tmp = tok->next(); tmp != endTok; tmp = tmp->next()) { checkVariableAssignment(tmp, tok->scope()); if (tmp->tokType() != Token::eFunction) continue; const Function* f = tmp->function(); if (f->nestedIn->isClassOrStruct() && !f->isStatic() && !f->isConst()) { sideEffectInAssertError(tmp, f->name()); // Non-const member function called continue; } const Scope* scope = f->functionScope; if (!scope) continue; for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { if (!tok2->isAssignmentOp() && tok2->tokType() != Token::eIncDecOp) continue; const Variable* var = tok2->previous()->variable(); if (!var || var->isLocal() || (var->isArgument() && !var->isReference() && !var->isPointer())) continue; // See ticket #4937. Assigning function arguments not passed by reference is ok. if (var->isArgument() && var->isPointer() && tok2->strAt(-2) != "*") continue; // Pointers need to be dereferenced, otherwise there is no error bool noReturnInScope = true; for (const Token *rt = scope->bodyStart; rt != scope->bodyEnd; rt = rt->next()) { if (rt->str() != "return") continue; // find all return statements if (inSameScope(rt, tok2)) { noReturnInScope = false; break; } } if (noReturnInScope) continue; sideEffectInAssertError(tmp, f->name()); break; } } tok = endTok; } } //--------------------------------------------------------------------------- void CheckAssert::sideEffectInAssertError(const Token *tok, const std::string& functionName) { reportError(tok, Severity::warning, "assertWithSideEffect", "$symbol:" + functionName + "\n" "Assert statement calls a function which may have desired side effects: '$symbol'.\n" "Non-pure function: '$symbol' is called inside assert statement. " "Assert statements are removed from release builds so the code inside " "assert statement is not executed. If the code is needed also in release " "builds, this is a bug.", CWE398, false); } void CheckAssert::assignmentInAssertError(const Token *tok, const std::string& varname) { reportError(tok, Severity::warning, "assignmentInAssert", "$symbol:" + varname + "\n" "Assert statement modifies '$symbol'.\n" "Variable '$symbol' is modified inside assert statement. " "Assert statements are removed from release builds so the code inside " "assert statement is not executed. If the code is needed also in release " "builds, this is a bug.", CWE398, false); } // checks if side effects happen on the variable prior to tmp void CheckAssert::checkVariableAssignment(const Token* assignTok, const Scope *assertionScope) { const Variable* prevVar = assignTok->previous()->variable(); if (!prevVar) return; // Variable declared in inner scope in assert => don't warn if (assertionScope != prevVar->scope()) { const Scope *s = prevVar->scope(); while (s && s != assertionScope) s = s->nestedIn; if (s == assertionScope) return; } // assignment if (assignTok->isAssignmentOp() || assignTok->tokType() == Token::eIncDecOp) { if (prevVar->isConst()) return; assignmentInAssertError(assignTok, prevVar->name()); } // TODO: function calls on prevVar } bool CheckAssert::inSameScope(const Token* returnTok, const Token* assignTok) { // TODO: even if a return is in the same scope, the assignment might not affect it. return returnTok->scope() == assignTok->scope(); } cppcheck-1.90/lib/checkassert.h000066400000000000000000000052761357737443600164760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkassertH #define checkassertH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Scope; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** * @brief Checking for side effects in assert statements */ class CPPCHECKLIB CheckAssert : public Check { public: CheckAssert() : Check(myName()) { } CheckAssert(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** run checks, the token list is not simplified */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckAssert checkAssert(tokenizer, settings, errorLogger); checkAssert.assertWithSideEffects(); } void assertWithSideEffects(); protected: void checkVariableAssignment(const Token* assignTok, const Scope *assertionScope); static bool inSameScope(const Token* returnTok, const Token* assignTok); private: void sideEffectInAssertError(const Token *tok, const std::string& functionName); void assignmentInAssertError(const Token *tok, const std::string &varname); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckAssert c(nullptr, settings, errorLogger); c.sideEffectInAssertError(nullptr, "function"); c.assignmentInAssertError(nullptr, "var"); } static std::string myName() { return "Assert"; } std::string classInfo() const OVERRIDE { return "Warn if there are side effects in assert statements (since this cause different behaviour in debug/release builds).\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkassertH cppcheck-1.90/lib/checkautovariables.cpp000066400000000000000000000645611357737443600203730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Auto variables checks //--------------------------------------------------------------------------- #include "checkautovariables.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include //--------------------------------------------------------------------------- // Register this check class into cppcheck by creating a static instance of it.. namespace { CheckAutoVariables instance; } static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE562(562U); // Return of Stack Variable Address static const CWE CWE590(590U); // Free of Memory not on the Heap static bool isPtrArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isPointer()); } static bool isArrayArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isArray()); } static bool isArrayVar(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArray()); } static bool isRefPtrArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && var->isReference() && var->isPointer()); } static bool isNonReferenceArg(const Token *tok) { const Variable *var = tok->variable(); return (var && var->isArgument() && !var->isReference() && (var->isPointer() || var->valueType()->type >= ValueType::Type::CONTAINER || var->type())); } static bool isAutoVar(const Token *tok) { const Variable *var = tok->variable(); if (!var || !var->isLocal() || var->isStatic()) return false; if (var->isReference()) { // address of reference variable can be taken if the address // of the variable it points at is not a auto-var // TODO: check what the reference variable references. return false; } if (Token::Match(tok, "%name% .|::")) { do { tok = tok->tokAt(2); } while (Token::Match(tok, "%name% .|::")); if (Token::Match(tok, "%name% (")) return false; } return true; } static bool isAutoVarArray(const Token *tok) { if (!tok) return false; // &x[..] if (tok->isUnaryOp("&") && Token::simpleMatch(tok->astOperand1(), "[")) return isAutoVarArray(tok->astOperand1()->astOperand1()); // x+y if (tok->str() == "+") return isAutoVarArray(tok->astOperand1()) || isAutoVarArray(tok->astOperand2()); // x-intexpr if (tok->str() == "-") return isAutoVarArray(tok->astOperand1()) && tok->astOperand2() && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->isIntegral(); const Variable *var = tok->variable(); if (!var) return false; // Variable if (var->isLocal() && !var->isStatic() && var->isArray() && !var->isPointer()) return true; // ValueFlow if (var->isPointer() && !var->isArgument()) { for (std::list::const_iterator it = tok->values().begin(); it != tok->values().end(); ++it) { const ValueFlow::Value &val = *it; if (val.isTokValue() && isAutoVarArray(val.tokvalue)) return true; } } return false; } // Verification that we really take the address of a local variable static bool checkRvalueExpression(const Token * const vartok) { const Variable * const var = vartok->variable(); if (var == nullptr) return false; if (Token::Match(vartok->previous(), "& %name% [") && var->isPointer()) return false; const Token * const next = vartok->next(); // &a.b[0] if (Token::Match(vartok, "%name% . %var% [") && !var->isPointer()) { const Variable *var2 = next->next()->variable(); return var2 && !var2->isPointer(); } return ((next->str() != "." || (!var->isPointer() && (!var->isClass() || var->type()))) && next->strAt(2) != "."); } static bool isAddressOfLocalVariable(const Token *expr) { if (!expr) return false; if (Token::Match(expr, "+|-")) return isAddressOfLocalVariable(expr->astOperand1()) || isAddressOfLocalVariable(expr->astOperand2()); if (expr->isCast()) return isAddressOfLocalVariable(expr->astOperand2() ? expr->astOperand2() : expr->astOperand1()); if (expr->isUnaryOp("&")) { const Token *op = expr->astOperand1(); bool deref = false; while (Token::Match(op, ".|[")) { if (op->originalName() == "->") return false; if (op->str() == "[") deref = true; op = op->astOperand1(); } return op && isAutoVar(op) && (!deref || !op->variable()->isPointer()); } return false; } static bool variableIsUsedInScope(const Token* start, nonneg int varId, const Scope *scope) { if (!start) // Ticket #5024 return false; for (const Token *tok = start; tok && tok != scope->bodyEnd; tok = tok->next()) { if (tok->varId() == varId) return true; const Scope::ScopeType scopeType = tok->scope()->type; if (scopeType == Scope::eFor || scopeType == Scope::eDo || scopeType == Scope::eWhile) // In case of loops, better checking would be necessary return true; if (Token::simpleMatch(tok, "asm (")) return true; } return false; } void CheckAutoVariables::assignFunctionArg() { const bool printStyle = mSettings->isEnabled(Settings::STYLE); const bool printWarning = mSettings->isEnabled(Settings::WARNING); if (!printStyle && !printWarning) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { // TODO: What happens if this is removed? if (tok->astParent()) continue; if (!(tok->isAssignmentOp() || Token::Match(tok, "++|--")) || !Token::Match(tok->astOperand1(), "%var%")) continue; const Token* const vartok = tok->astOperand1(); if (isNonReferenceArg(vartok) && !Token::Match(vartok->next(), "= %varid% ;", vartok->varId()) && !variableIsUsedInScope(Token::findsimplematch(vartok->next(), ";"), vartok->varId(), scope) && !Token::findsimplematch(vartok, "goto", scope->bodyEnd)) { if (vartok->variable()->isPointer() && printWarning) errorUselessAssignmentPtrArg(vartok); else if (printStyle) errorUselessAssignmentArg(vartok); } } } } void CheckAutoVariables::autoVariables() { const bool printInconclusive = mSettings->inconclusive; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { // Skip lambda.. if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { tok = lambdaEndToken; continue; } // Critical assignment if (Token::Match(tok, "[;{}] %var% = & %var%") && isRefPtrArg(tok->next()) && isAutoVar(tok->tokAt(4))) { if (checkRvalueExpression(tok->tokAt(4))) errorAutoVariableAssignment(tok->next(), false); } else if (Token::Match(tok, "[;{}] * %var% =") && isPtrArg(tok->tokAt(2)) && isAddressOfLocalVariable(tok->tokAt(3)->astOperand2())) { errorAutoVariableAssignment(tok->next(), false); } else if (Token::Match(tok, "[;{}] %var% . %var% =") && isPtrArg(tok->next()) && isAddressOfLocalVariable(tok->tokAt(4)->astOperand2())) { errorAutoVariableAssignment(tok->next(), false); } else if (Token::Match(tok, "[;{}] %var% . %var% = %var% ;")) { // TODO: check if the parameter is only changed temporarily (#2969) if (printInconclusive && isPtrArg(tok->next())) { if (isAutoVarArray(tok->tokAt(5))) errorAutoVariableAssignment(tok->next(), true); } tok = tok->tokAt(5); } else if (Token::Match(tok, "[;{}] * %var% = %var% ;")) { const Variable * var1 = tok->tokAt(2)->variable(); if (var1 && var1->isArgument() && Token::Match(var1->nameToken()->tokAt(-3), "%type% * *")) { if (isAutoVarArray(tok->tokAt(4))) errorAutoVariableAssignment(tok->next(), false); } tok = tok->tokAt(4); } else if (Token::Match(tok, "[;{}] %var% [") && Token::simpleMatch(tok->linkAt(2), "] =") && (isPtrArg(tok->next()) || isArrayArg(tok->next())) && isAddressOfLocalVariable(tok->linkAt(2)->next()->astOperand2())) { errorAutoVariableAssignment(tok->next(), false); } // Invalid pointer deallocation else if ((Token::Match(tok, "%name% ( %var% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) || (mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| %var% !!["))) { tok = Token::findmatch(tok->next(), "%var%"); if (isArrayVar(tok)) errorInvalidDeallocation(tok, nullptr); else if (tok && tok->variable() && tok->variable()->isPointer()) { for (const ValueFlow::Value &v : tok->values()) { if (v.isTokValue() && isArrayVar(v.tokvalue)) { errorInvalidDeallocation(tok, &v); break; } } } } else if ((Token::Match(tok, "%name% ( & %var% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) || (mTokenizer->isCPP() && Token::Match(tok, "delete [| ]| (| & %var% !!["))) { tok = Token::findmatch(tok->next(), "%var%"); if (isAutoVar(tok)) errorInvalidDeallocation(tok, nullptr); } } } } //--------------------------------------------------------------------------- void CheckAutoVariables::errorReturnAddressToAutoVariable(const Token *tok) { reportError(tok, Severity::error, "returnAddressOfAutoVariable", "Address of an auto-variable returned.", CWE562, false); } void CheckAutoVariables::errorReturnAddressToAutoVariable(const Token *tok, const ValueFlow::Value *value) { reportError(tok, Severity::error, "returnAddressOfAutoVariable", "Address of auto-variable '" + value->tokvalue->astOperand1()->expressionString() + "' returned", CWE562, false); } void CheckAutoVariables::errorReturnPointerToLocalArray(const Token *tok) { reportError(tok, Severity::error, "returnLocalVariable", "Pointer to local array variable returned.", CWE562, false); } void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok, bool inconclusive) { if (!inconclusive) { reportError(tok, Severity::error, "autoVariables", "Address of local auto-variable assigned to a function parameter.\n" "Dangerous assignment - the function parameter is assigned the address of a local " "auto-variable. Local auto-variables are reserved from the stack which " "is freed when the function ends. So the pointer to a local variable " "is invalid after the function ends.", CWE562, false); } else { reportError(tok, Severity::error, "autoVariables", "Address of local auto-variable assigned to a function parameter.\n" "Function parameter is assigned the address of a local auto-variable. " "Local auto-variables are reserved from the stack which is freed when " "the function ends. The address is invalid after the function ends and it " "might 'leak' from the function through the parameter.", CWE562, true); } } void CheckAutoVariables::errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "returnAddressOfFunctionParameter", "$symbol:" + varname + "\n" "Address of function parameter '$symbol' returned.\n" "Address of the function parameter '$symbol' becomes invalid after the function exits because " "function parameters are stored on the stack which is freed when the function exits. Thus the returned " "value is invalid.", CWE562, false); } void CheckAutoVariables::errorUselessAssignmentArg(const Token *tok) { reportError(tok, Severity::style, "uselessAssignmentArg", "Assignment of function parameter has no effect outside the function.", CWE398, false); } void CheckAutoVariables::errorUselessAssignmentPtrArg(const Token *tok) { reportError(tok, Severity::warning, "uselessAssignmentPtrArg", "Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?", CWE398, false); } //--------------------------------------------------------------------------- static bool astHasAutoResult(const Token *tok) { if (tok->astOperand1() && !astHasAutoResult(tok->astOperand1())) return false; if (tok->astOperand2() && !astHasAutoResult(tok->astOperand2())) return false; if (tok->isOp()) { if (tok->tokType() == Token::eIncDecOp) return false; if ((tok->str() == "<<" || tok->str() == ">>") && tok->astOperand1()) { const Token* tok2 = tok->astOperand1(); while (tok2 && tok2->isUnaryOp("*")) tok2 = tok2->astOperand1(); return tok2 && tok2->variable() && !tok2->variable()->isClass() && !tok2->variable()->isStlType(); // Class or unknown type on LHS: Assume it is a stream } return true; } if (tok->isLiteral()) return true; if (tok->isName()) { // TODO: check function calls, struct members, arrays, etc also if (!tok->variable()) return false; if (tok->variable()->isStlType()) return true; if (tok->variable()->isClass() || tok->variable()->isPointer() || tok->variable()->isReference()) // TODO: Properly handle pointers/references to classes in symbol database return false; return true; } return false; } static bool isInScope(const Token * tok, const Scope * scope) { if (!tok) return false; if (!scope) return false; const Variable * var = tok->variable(); if (var && (var->isGlobal() || var->isStatic() || var->isExtern())) return false; if (tok->scope() && tok->scope()->isNestedIn(scope)) return true; if (!var) return false; if (var->isArgument() && !var->isReference()) { const Scope * tokScope = tok->scope(); if (!tokScope) return false; for (const Scope * argScope:tokScope->nestedList) { if (argScope && argScope->isNestedIn(scope)) return true; } } return false; } static bool isDeadScope(const Token * tok, const Scope * scope) { if (!tok) return false; if (!scope) return false; const Variable * var = tok->variable(); if (var && (!var->isLocal() || var->isStatic() || var->isExtern())) return false; if (tok->scope() && tok->scope()->bodyEnd != scope->bodyEnd && precedes(tok->scope()->bodyEnd, scope->bodyEnd)) return true; return false; } static const Token * getParentLifetime(const Token *tok) { if (!tok) return tok; const Variable * var = tok->variable(); // TODO: Call getLifetimeVariable for deeper analysis if (!var) return tok; if (var->isLocal()) return tok; const Token * parent = getParentMember(tok); if (parent != tok) return getParentLifetime(parent); return tok; } static int getPointerDepth(const Token *tok) { if (!tok) return 0; return tok->valueType() ? tok->valueType()->pointer : 0; } static bool isDeadTemporary(bool cpp, const Token* tok, const Token* expr, const Library* library) { if (!isTemporary(cpp, tok, library)) return false; if (expr && !precedes(nextAfterAstRightmostLeaf(tok->astTop()), nextAfterAstRightmostLeaf(expr->astTop()))) return false; return true; } void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end) { if (!start) return; const Scope * scope = start->scope(); if (!scope) return; // If the scope is not set correctly then skip checking it if (scope->bodyStart != start) return; bool returnRef = Function::returnsReference(scope->function); for (const Token *tok = start; tok && tok != end; tok = tok->next()) { // Return reference from function if (returnRef && Token::simpleMatch(tok->astParent(), "return")) { for (const LifetimeToken& lt : getLifetimeTokens(tok)) { const Variable* var = lt.token->variable(); if (var && !var->isGlobal() && !var->isStatic() && !var->isReference() && !var->isRValueReference() && isInScope(var->nameToken(), tok->scope())) { errorReturnReference(tok, lt.errorPath, lt.inconclusive); break; } else if (isDeadTemporary(mTokenizer->isCPP(), lt.token, nullptr, &mSettings->library)) { errorReturnTempReference(tok, lt.errorPath, lt.inconclusive); break; } } // Assign reference to non-local variable } else if (Token::Match(tok->previous(), "&|&& %var% =") && tok->astParent() == tok->next() && tok->variable() && tok->variable()->nameToken() == tok && tok->variable()->declarationId() == tok->varId() && tok->variable()->isStatic() && !tok->variable()->isArgument()) { ErrorPath errorPath; const Variable *var = getLifetimeVariable(tok, errorPath); if (var && isInScope(var->nameToken(), tok->scope())) { errorDanglingReference(tok, var, errorPath); continue; } } for (const ValueFlow::Value& val:tok->values()) { if (!val.isLocalLifetimeValue()) continue; const Token * tokvalue = getParentLifetime(val.tokvalue); if (Token::Match(tok->astParent(), "return|throw")) { if (getPointerDepth(tok) < getPointerDepth(tokvalue)) continue; if (!isLifetimeBorrowed(tok, mSettings)) continue; if ((tokvalue->variable() && isInScope(tokvalue->variable()->nameToken(), scope)) || isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) { errorReturnDanglingLifetime(tok, &val); break; } } else if (tokvalue->variable() && isDeadScope(tokvalue->variable()->nameToken(), tok->scope())) { errorInvalidLifetime(tok, &val); break; } else if (!tokvalue->variable() && isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) { errorDanglingTemporaryLifetime(tok, &val); break; } else if (tokvalue->variable() && isInScope(tokvalue->variable()->nameToken(), tok->scope())) { const Variable * var = nullptr; const Token * tok2 = tok; if (Token::simpleMatch(tok->astParent(), "=")) { if (tok->astParent()->astOperand2() == tok) { var = getLHSVariable(tok->astParent()); tok2 = tok->astParent()->astOperand1(); } } else if (tok->variable() && tok->variable()->declarationId() == tok->varId()) { var = tok->variable(); } if (!isLifetimeBorrowed(tok, mSettings)) continue; if (var && !var->isLocal() && !var->isArgument() && !isVariableChanged(tok->next(), tok->scope()->bodyEnd, var->declarationId(), var->isGlobal(), mSettings, mTokenizer->isCPP())) { errorDanglngLifetime(tok2, &val); break; } } } const Token *lambdaEndToken = findLambdaEndToken(tok); if (lambdaEndToken) { checkVarLifetimeScope(lambdaEndToken->link(), lambdaEndToken); tok = lambdaEndToken; } if (tok->str() == "{" && tok->scope()) { // Check functions in local classes if (tok->scope()->type == Scope::eClass || tok->scope()->type == Scope::eStruct || tok->scope()->type == Scope::eUnion) { for (const Function& f:tok->scope()->functionList) { if (f.functionScope) checkVarLifetimeScope(f.functionScope->bodyStart, f.functionScope->bodyEnd); } tok = tok->link(); } } } } void CheckAutoVariables::checkVarLifetime() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { if (!scope->function) continue; checkVarLifetimeScope(scope->bodyStart, scope->bodyEnd); } } void CheckAutoVariables::errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value *val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); std::string msg = "Returning " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "returnDanglingLifetime", msg + " that will be invalid when returning.", CWE562, inconclusive); } void CheckAutoVariables::errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "invalidLifetime", msg + " that is out of scope.", CWE562, inconclusive); } void CheckAutoVariables::errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "danglingTemporaryLifetime", msg + " to temporary.", CWE562, inconclusive); } void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); std::string tokName = tok ? tok->expressionString() : "x"; std::string msg = "Non-local variable '" + tokName + "' will use " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, inconclusive); } void CheckAutoVariables::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "returnReference", "Reference to local variable returned.", CWE562, inconclusive); } void CheckAutoVariables::errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath) { std::string tokName = tok ? tok->str() : "x"; std::string varName = var ? var->name() : "y"; std::string msg = "Non-local reference variable '" + tokName + "' to local variable '" + varName + "'"; errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "danglingReference", msg, CWE562, false); } void CheckAutoVariables::errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "returnTempReference", "Reference to temporary returned.", CWE562, inconclusive); } void CheckAutoVariables::errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val) { const Variable *var = val ? val->tokvalue->variable() : (tok ? tok->variable() : nullptr); std::string type = "auto-variable"; if (var) { if (var->isGlobal()) type = "global variable"; else if (var->isStatic()) type = "static variable"; } if (val) type += " (" + val->tokvalue->str() + ")"; reportError(getErrorPath(tok, val, "Deallocating memory that was not dynamically allocated"), Severity::error, "autovarInvalidDeallocation", "Deallocation of an " + type + " results in undefined behaviour.\n" "The deallocation of an " + type + " results in undefined behaviour. You should only free memory " "that has been allocated dynamically.", CWE590, false); } cppcheck-1.90/lib/checkautovariables.h000066400000000000000000000120501357737443600200220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkautovariablesH #define checkautovariablesH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /** @brief Various small checks for automatic variables */ /// @{ class CPPCHECKLIB CheckAutoVariables : public Check { public: /** This constructor is used when registering the CheckClass */ CheckAutoVariables() : Check(myName()) { } /** This constructor is used when running checks. */ CheckAutoVariables(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger); checkAutoVariables.assignFunctionArg(); checkAutoVariables.checkVarLifetime(); checkAutoVariables.autoVariables(); } /** assign function argument */ void assignFunctionArg(); /** Check auto variables */ void autoVariables(); void checkVarLifetime(); void checkVarLifetimeScope(const Token * start, const Token * end); private: void errorReturnAddressToAutoVariable(const Token *tok); void errorReturnAddressToAutoVariable(const Token *tok, const ValueFlow::Value *value); void errorReturnPointerToLocalArray(const Token *tok); void errorAutoVariableAssignment(const Token *tok, bool inconclusive); void errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value* val); void errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val); void errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val); void errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val); void errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive); void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath); void errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val); void errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname); void errorUselessAssignmentArg(const Token *tok); void errorUselessAssignmentPtrArg(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { ErrorPath errorPath; CheckAutoVariables c(nullptr,settings,errorLogger); c.errorAutoVariableAssignment(nullptr, false); c.errorReturnAddressToAutoVariable(nullptr); c.errorReturnPointerToLocalArray(nullptr); c.errorReturnReference(nullptr, errorPath, false); c.errorDanglingReference(nullptr, nullptr, errorPath); c.errorReturnTempReference(nullptr, errorPath, false); c.errorInvalidDeallocation(nullptr, nullptr); c.errorReturnAddressOfFunctionParameter(nullptr, "parameter"); c.errorUselessAssignmentArg(nullptr); c.errorUselessAssignmentPtrArg(nullptr); c.errorReturnDanglingLifetime(nullptr, nullptr); c.errorInvalidLifetime(nullptr, nullptr); c.errorDanglngLifetime(nullptr, nullptr); c.errorDanglingTemporaryLifetime(nullptr, nullptr); } static std::string myName() { return "Auto Variables"; } std::string classInfo() const OVERRIDE { return "A pointer to a variable is only valid as long as the variable is in scope.\n" "Check:\n" "- returning a pointer to auto or temporary variable\n" "- assigning address of an variable to an effective parameter of a function\n" "- returning reference to local/temporary variable\n" "- returning address of function parameter\n" "- suspicious assignment of pointer argument\n" "- useless assignment of function argument\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkautovariablesH cppcheck-1.90/lib/checkbool.cpp000066400000000000000000000454331357737443600164620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkbool.h" #include "astutils.h" #include "errorlogger.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckBool instance; } static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE571(571U); // Expression is Always True static const CWE CWE587(587U); // Assignment of a Fixed Address to a Pointer static const CWE CWE704(704U); // Incorrect Type Conversion or Cast static bool isBool(const Variable* var) { return (var && Token::Match(var->typeEndToken(), "bool|_Bool")); } //--------------------------------------------------------------------------- void CheckBool::checkIncrementBoolean() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (astIsBool(tok) && tok->astParent() && tok->astParent()->str() == "++") { incrementBooleanError(tok); } } } } void CheckBool::incrementBooleanError(const Token *tok) { reportError( tok, Severity::style, "incrementboolean", "Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n" "The operand of a postfix increment operator may be of type bool but it is deprecated by C++ Standard (Annex D-1) and the operand is always set to true. You should assign it the value 'true' instead.", CWE398, false ); } //--------------------------------------------------------------------------- // if (bool & bool) -> if (bool && bool) // if (bool | bool) -> if (bool || bool) //--------------------------------------------------------------------------- void CheckBool::checkBitwiseOnBoolean() { if (!mSettings->isEnabled(Settings::STYLE)) return; // danmar: this is inconclusive because I don't like that there are // warnings for calculations. Example: set_flag(a & b); if (!mSettings->inconclusive) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->isBinaryOp() && (tok->str() == "&" || tok->str() == "|")) { if (astIsBool(tok->astOperand1()) || astIsBool(tok->astOperand2())) { if (tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2()) continue; const std::string expression = astIsBool(tok->astOperand1()) ? tok->astOperand1()->expressionString() : tok->astOperand2()->expressionString(); bitwiseOnBooleanError(tok, expression, tok->str() == "&" ? "&&" : "||"); } } } } } void CheckBool::bitwiseOnBooleanError(const Token *tok, const std::string &expression, const std::string &op) { reportError(tok, Severity::style, "bitwiseOnBoolean", "Boolean expression '" + expression + "' is used in bitwise operation. Did you mean '" + op + "'?", CWE398, true); } //--------------------------------------------------------------------------- // if (!x==3) <- Probably meant to be "x!=3" //--------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolWithInt() { if (!mSettings->isEnabled(Settings::WARNING) || !mTokenizer->isCPP()) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || !tok->isBinaryOp()) continue; const Token* const left = tok->astOperand1(); const Token* const right = tok->astOperand2(); if (left->isBoolean() && right->varId()) { // Comparing boolean constant with variable if (tok->str() != "==" && tok->str() != "!=") { comparisonOfBoolWithInvalidComparator(right, left->str()); } } else if (left->varId() && right->isBoolean()) { // Comparing variable with boolean constant if (tok->str() != "==" && tok->str() != "!=") { comparisonOfBoolWithInvalidComparator(right, left->str()); } } } } } void CheckBool::comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression) { reportError(tok, Severity::warning, "comparisonOfBoolWithInvalidComparator", "Comparison of a boolean value using relational operator (<, >, <= or >=).\n" "The result of the expression '" + expression + "' is of type 'bool'. " "Comparing 'bool' value using relational (<, >, <= or >=)" " operator could cause unexpected results."); } //------------------------------------------------------------------------------- // Comparing functions which are returning value of type bool //------------------------------------------------------------------------------- static bool tokenIsFunctionReturningBool(const Token* tok) { const Function* func = tok->function(); if (func && Token::Match(tok, "%name% (")) { if (func->tokenDef && Token::Match(func->tokenDef->previous(), "bool|_Bool")) { return true; } } return false; } void CheckBool::checkComparisonOfFuncReturningBool() { if (!mSettings->isEnabled(Settings::STYLE)) return; if (!mTokenizer->isCPP()) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=") continue; const Token *firstToken = tok->previous(); if (tok->strAt(-1) == ")") { firstToken = firstToken->link()->previous(); } const Token *secondToken = tok->next(); while (secondToken->str() == "!") { secondToken = secondToken->next(); } const bool firstIsFunctionReturningBool = tokenIsFunctionReturningBool(firstToken); const bool secondIsFunctionReturningBool = tokenIsFunctionReturningBool(secondToken); if (firstIsFunctionReturningBool && secondIsFunctionReturningBool) { comparisonOfTwoFuncsReturningBoolError(firstToken->next(), firstToken->str(), secondToken->str()); } else if (firstIsFunctionReturningBool) { comparisonOfFuncReturningBoolError(firstToken->next(), firstToken->str()); } else if (secondIsFunctionReturningBool) { comparisonOfFuncReturningBoolError(secondToken->previous(), secondToken->str()); } } } } void CheckBool::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression) { reportError(tok, Severity::style, "comparisonOfFuncReturningBoolError", "Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" "The return type of function '" + expression + "' is 'bool' " "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" " operator could cause unexpected results.", CWE398, false); } void CheckBool::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2) { reportError(tok, Severity::style, "comparisonOfTwoFuncsReturningBoolError", "Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" "The return type of function '" + expression1 + "' and function '" + expression2 + "' is 'bool' " "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" " operator could cause unexpected results.", CWE398, false); } //------------------------------------------------------------------------------- // Comparison of bool with bool //------------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolWithBool() { // FIXME: This checking is "experimental" because of the false positives // when self checking lib/tokenize.cpp (#2617) if (!mSettings->experimental) return; if (!mSettings->isEnabled(Settings::STYLE)) return; if (!mTokenizer->isCPP()) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=") continue; bool firstTokenBool = false; const Token *firstToken = tok->previous(); if (firstToken->varId()) { if (isBool(firstToken->variable())) { firstTokenBool = true; } } if (!firstTokenBool) continue; bool secondTokenBool = false; const Token *secondToken = tok->next(); if (secondToken->varId()) { if (isBool(secondToken->variable())) { secondTokenBool = true; } } if (secondTokenBool) { comparisonOfBoolWithBoolError(firstToken->next(), secondToken->str()); } } } } void CheckBool::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression) { reportError(tok, Severity::style, "comparisonOfBoolWithBoolError", "Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n" "The variable '" + expression + "' is of type 'bool' " "and comparing 'bool' value using relational (<, >, <= or >=)" " operator could cause unexpected results.", CWE398, false); } //----------------------------------------------------------------------------- void CheckBool::checkAssignBoolToPointer() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "=" && astIsPointer(tok->astOperand1()) && astIsBool(tok->astOperand2())) { assignBoolToPointerError(tok); } } } } void CheckBool::assignBoolToPointerError(const Token *tok) { reportError(tok, Severity::error, "assignBoolToPointer", "Boolean value assigned to pointer.", CWE587, false); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolExpressionWithInt() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp()) continue; const Token* numTok = nullptr; const Token* boolExpr = nullptr; bool numInRhs; if (astIsBool(tok->astOperand1())) { boolExpr = tok->astOperand1(); numTok = tok->astOperand2(); numInRhs = true; } else if (astIsBool(tok->astOperand2())) { boolExpr = tok->astOperand2(); numTok = tok->astOperand1(); numInRhs = false; } else { continue; } if (!numTok || !boolExpr) continue; if (boolExpr->isOp() && numTok->isName() && Token::Match(tok, "==|!=")) // there is weird code such as: ((aisNumber()) { const MathLib::bigint num = MathLib::toLongNumber(numTok->str()); if (num==0 && (numInRhs ? Token::Match(tok, ">|==|!=") : Token::Match(tok, "<|==|!="))) continue; if (num==1 && (numInRhs ? Token::Match(tok, "<|==|!=") : Token::Match(tok, ">|==|!="))) continue; comparisonOfBoolExpressionWithIntError(tok, true); } else if (astIsIntegral(numTok, false) && mTokenizer->isCPP()) comparisonOfBoolExpressionWithIntError(tok, false); } } } void CheckBool::comparisonOfBoolExpressionWithIntError(const Token *tok, bool n0o1) { if (n0o1) reportError(tok, Severity::warning, "compareBoolExpressionWithInt", "Comparison of a boolean expression with an integer other than 0 or 1.", CWE398, false); else reportError(tok, Severity::warning, "compareBoolExpressionWithInt", "Comparison of a boolean expression with an integer.", CWE398, false); } void CheckBool::pointerArithBool() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf && scope.type != Scope::eWhile && scope.type != Scope::eDo && scope.type != Scope::eFor) continue; const Token* tok = scope.classDef->next()->astOperand2(); if (scope.type == Scope::eFor) { tok = Token::findsimplematch(scope.classDef->tokAt(2), ";"); if (tok) tok = tok->astOperand2(); if (tok) tok = tok->astOperand1(); } else if (scope.type == Scope::eDo) tok = (scope.bodyEnd->tokAt(2)) ? scope.bodyEnd->tokAt(2)->astOperand2() : nullptr; pointerArithBoolCond(tok); } } void CheckBool::pointerArithBoolCond(const Token *tok) { if (!tok) return; if (Token::Match(tok, "&&|%oror%")) { pointerArithBoolCond(tok->astOperand1()); pointerArithBoolCond(tok->astOperand2()); return; } if (tok->str() != "+" && tok->str() != "-") return; if (tok->isBinaryOp() && tok->astOperand1()->isName() && tok->astOperand1()->variable() && tok->astOperand1()->variable()->isPointer() && tok->astOperand2()->isNumber()) pointerArithBoolError(tok); } void CheckBool::pointerArithBoolError(const Token *tok) { reportError(tok, Severity::error, "pointerArithBool", "Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n" "Converting pointer arithmetic result to bool. The boolean result is always true unless there is pointer arithmetic overflow, and overflow is undefined behaviour. Probably a dereference is forgotten.", CWE571, false); } void CheckBool::checkAssignBoolToFloat() { if (!mTokenizer->isCPP()) return; if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "=" && astIsFloat(tok->astOperand1(), false) && astIsBool(tok->astOperand2())) { assignBoolToFloatError(tok); } } } } void CheckBool::assignBoolToFloatError(const Token *tok) { reportError(tok, Severity::style, "assignBoolToFloat", "Boolean value assigned to floating point variable.", CWE704, false); } void CheckBool::returnValueOfFunctionReturningBool() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { if (!(scope->function && Token::Match(scope->function->retDef, "bool|_Bool"))) continue; for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { // Skip lambdas const Token* tok2 = findLambdaEndToken(tok); if (tok2) tok = tok2; else if (tok->scope() && tok->scope()->isClassOrStruct()) tok = tok->scope()->bodyEnd; else if (Token::simpleMatch(tok, "return") && tok->astOperand1() && (tok->astOperand1()->getValueGE(2, mSettings) || tok->astOperand1()->getValueLE(-1, mSettings)) && !(tok->astOperand1()->astOperand1() && Token::Match(tok->astOperand1(), "&|%or%"))) returnValueBoolError(tok); } } } void CheckBool::returnValueBoolError(const Token *tok) { reportError(tok, Severity::style, "returnNonBoolInBooleanFunction", "Non-boolean value returned from function returning bool"); } cppcheck-1.90/lib/checkbool.h000066400000000000000000000140511357737443600161170ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkboolH #define checkboolH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** @brief checks dealing with suspicious usage of boolean type (not for evaluating conditions) */ class CPPCHECKLIB CheckBool : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckBool() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckBool(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckBool checkBool(tokenizer, settings, errorLogger); // Checks checkBool.checkComparisonOfBoolExpressionWithInt(); checkBool.checkComparisonOfBoolWithInt(); checkBool.checkAssignBoolToFloat(); checkBool.pointerArithBool(); checkBool.returnValueOfFunctionReturningBool(); checkBool.checkComparisonOfFuncReturningBool(); checkBool.checkComparisonOfBoolWithBool(); checkBool.checkIncrementBoolean(); checkBool.checkAssignBoolToPointer(); checkBool.checkBitwiseOnBoolean(); } /** @brief %Check for comparison of function returning bool*/ void checkComparisonOfFuncReturningBool(); /** @brief %Check for comparison of variable of type bool*/ void checkComparisonOfBoolWithBool(); /** @brief %Check for using postfix increment on bool */ void checkIncrementBoolean(); /** @brief %Check for suspicious comparison of a bool and a non-zero (and non-one) value (e.g. "if (!x==4)") */ void checkComparisonOfBoolWithInt(); /** @brief assigning bool to pointer */ void checkAssignBoolToPointer(); /** @brief assigning bool to float */ void checkAssignBoolToFloat(); /** @brief %Check for using bool in bitwise expression */ void checkBitwiseOnBoolean(); /** @brief %Check for comparing a bool expression with an integer other than 0 or 1 */ void checkComparisonOfBoolExpressionWithInt(); /** @brief %Check for 'if (p+1)' etc. either somebody forgot to dereference, or else somebody uses pointer overflow */ void pointerArithBool(); void pointerArithBoolCond(const Token *tok); /** @brief %Check if a function returning bool returns an integer other than 0 or 1 */ void returnValueOfFunctionReturningBool(); private: // Error messages.. void comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression); void comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2); void comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression); void incrementBooleanError(const Token *tok); void comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression); void assignBoolToPointerError(const Token *tok); void assignBoolToFloatError(const Token *tok); void bitwiseOnBooleanError(const Token *tok, const std::string &expression, const std::string &op); void comparisonOfBoolExpressionWithIntError(const Token *tok, bool n0o1); void pointerArithBoolError(const Token *tok); void returnValueBoolError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckBool c(nullptr, settings, errorLogger); c.assignBoolToPointerError(nullptr); c.assignBoolToFloatError(nullptr); c.comparisonOfFuncReturningBoolError(nullptr, "func_name"); c.comparisonOfTwoFuncsReturningBoolError(nullptr, "func_name1", "func_name2"); c.comparisonOfBoolWithBoolError(nullptr, "var_name"); c.incrementBooleanError(nullptr); c.bitwiseOnBooleanError(nullptr, "expression", "&&"); c.comparisonOfBoolExpressionWithIntError(nullptr, true); c.pointerArithBoolError(nullptr); c.comparisonOfBoolWithInvalidComparator(nullptr, "expression"); c.returnValueBoolError(nullptr); } static std::string myName() { return "Boolean"; } std::string classInfo() const OVERRIDE { return "Boolean type checks\n" "- using increment on boolean\n" "- comparison of a boolean expression with an integer other than 0 or 1\n" "- comparison of a function returning boolean value using relational operator\n" "- comparison of a boolean value with boolean value using relational operator\n" "- using bool in bitwise expression\n" "- pointer addition in condition (either dereference is forgot or pointer overflow is required to make the condition false)\n" "- Assigning bool value to pointer or float\n" "- Returning an integer other than 0 or 1 from a function with boolean return value\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkboolH cppcheck-1.90/lib/checkboost.cpp000066400000000000000000000047021357737443600166470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkboost.h" #include "errorlogger.h" #include "symboldatabase.h" #include "token.h" #include // Register this check class (by creating a static instance of it) namespace { CheckBoost instance; } static const CWE CWE664(664); void CheckBoost::checkBoostForeachModification() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "BOOST_FOREACH (")) continue; const Token *containerTok = tok->next()->link()->previous(); if (!Token::Match(containerTok, "%var% ) {")) continue; const Token *tok2 = containerTok->tokAt(2); const Token *end = tok2->link(); for (; tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "%varid% . insert|erase|push_back|push_front|pop_front|pop_back|clear|swap|resize|assign|merge|remove|remove_if|reverse|sort|splice|unique|pop|push", containerTok->varId())) { const Token* nextStatement = Token::findsimplematch(tok2->linkAt(3), ";", end); if (!Token::Match(nextStatement, "; break|return|throw")) boostForeachError(tok2); break; } } } } } void CheckBoost::boostForeachError(const Token *tok) { reportError(tok, Severity::error, "boostForeachError", "BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.", CWE664, false ); } cppcheck-1.90/lib/checkboost.h000066400000000000000000000050621357737443600163140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkboostH #define checkboostH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "tokenize.h" #include class ErrorLogger; class Settings; class Token; /// @addtogroup Checks /// @{ /** @brief %Check Boost usage */ class CPPCHECKLIB CheckBoost : public Check { public: /** This constructor is used when registering the CheckClass */ CheckBoost() : Check(myName()) { } /** This constructor is used when running checks. */ CheckBoost(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (!tokenizer->isCPP()) return; CheckBoost checkBoost(tokenizer, settings, errorLogger); checkBoost.checkBoostForeachModification(); } /** @brief %Check for container modification while using the BOOST_FOREACH macro */ void checkBoostForeachModification(); private: void boostForeachError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckBoost c(nullptr, settings, errorLogger); c.boostForeachError(nullptr); } static std::string myName() { return "Boost usage"; } std::string classInfo() const OVERRIDE { return "Check for invalid usage of Boost:\n" "- container modification during BOOST_FOREACH\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkboostH cppcheck-1.90/lib/checkbufferoverrun.cpp000066400000000000000000001136301357737443600204140ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Buffer overrun.. //--------------------------------------------------------------------------- #include "checkbufferoverrun.h" #include "astutils.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include "valueflow.h" #include #include #include #include // std::accumulate #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckBufferOverrun instance; } //--------------------------------------------------------------------------- // CWE ids used: static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size static const CWE CWE170(170U); // Improper Null Termination static const CWE CWE_ARRAY_INDEX_THEN_CHECK(398U); // Indicator of Poor Code Quality static const CWE CWE682(682U); // Incorrect Calculation static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE_POINTER_ARITHMETIC_OVERFLOW(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE_BUFFER_UNDERRUN(786U); // Access of Memory Location Before Start of Buffer static const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After End of Buffer //--------------------------------------------------------------------------- static const ValueFlow::Value *getBufferSizeValue(const Token *tok) { const std::list &tokenValues = tok->values(); const auto it = std::find_if(tokenValues.begin(), tokenValues.end(), std::mem_fn(&ValueFlow::Value::isBufferSizeValue)); return it == tokenValues.end() ? nullptr : &*it; } static int getMinFormatStringOutputLength(const std::vector ¶meters, nonneg int formatStringArgNr) { if (formatStringArgNr <= 0 || formatStringArgNr > parameters.size()) return 0; if (parameters[formatStringArgNr - 1]->tokType() != Token::eString) return 0; const std::string &formatString = parameters[formatStringArgNr - 1]->str(); bool percentCharFound = false; int outputStringSize = 0; bool handleNextParameter = false; std::string digits_string; bool i_d_x_f_found = false; int parameterLength = 0; int inputArgNr = formatStringArgNr; for (int i = 1; i + 1 < formatString.length(); ++i) { if (formatString[i] == '\\') { if (i < formatString.length() - 1 && formatString[i + 1] == '0') break; ++outputStringSize; ++i; continue; } if (percentCharFound) { switch (formatString[i]) { case 'f': case 'x': case 'X': case 'i': i_d_x_f_found = true; handleNextParameter = true; parameterLength = 1; // TODO break; case 'c': case 'e': case 'E': case 'g': case 'o': case 'u': case 'p': case 'n': handleNextParameter = true; parameterLength = 1; // TODO break; case 'd': i_d_x_f_found = true; parameterLength = 1; if (inputArgNr < parameters.size() && parameters[inputArgNr]->hasKnownIntValue()) parameterLength = MathLib::toString(parameters[inputArgNr]->getKnownIntValue()).length(); handleNextParameter = true; break; case 's': parameterLength = 0; if (inputArgNr < parameters.size() && parameters[inputArgNr]->tokType() == Token::eString) parameterLength = Token::getStrLength(parameters[inputArgNr]); handleNextParameter = true; break; } } if (formatString[i] == '%') percentCharFound = !percentCharFound; else if (percentCharFound) { digits_string.append(1, formatString[i]); } if (!percentCharFound) outputStringSize++; if (handleNextParameter) { int tempDigits = std::abs(std::atoi(digits_string.c_str())); if (i_d_x_f_found) tempDigits = std::max(tempDigits, 1); if (digits_string.find('.') != std::string::npos) { const std::string endStr = digits_string.substr(digits_string.find('.') + 1); const int maxLen = std::max(std::abs(std::atoi(endStr.c_str())), 1); if (formatString[i] == 's') { // For strings, the length after the dot "%.2s" will limit // the length of the string. if (parameterLength > maxLen) parameterLength = maxLen; } else { // For integers, the length after the dot "%.2d" can // increase required length if (tempDigits < maxLen) tempDigits = maxLen; } } if (tempDigits < parameterLength) outputStringSize += parameterLength; else outputStringSize += tempDigits; parameterLength = 0; digits_string.clear(); i_d_x_f_found = false; percentCharFound = false; handleNextParameter = false; ++inputArgNr; } } return outputStringSize; } //--------------------------------------------------------------------------- static bool getDimensionsEtc(const Token * const arrayToken, const Settings *settings, std::vector * const dimensions, ErrorPath * const errorPath, bool * const mightBeLarger) { const Token *array = arrayToken; while (Token::Match(array, ".|::")) array = array->astOperand2(); if (array->variable() && array->variable()->isArray() && !array->variable()->dimensions().empty()) { *dimensions = array->variable()->dimensions(); if (dimensions->size() >= 1 && ((*dimensions)[0].num <= 1 || !(*dimensions)[0].tok)) { visitAstNodes(arrayToken, [&](const Token *child) { if (child->originalName() == "->") { *mightBeLarger = true; return ChildrenToVisit::none; } return ChildrenToVisit::op1_and_op2; }); } } else if (const Token *stringLiteral = array->getValueTokenMinStrSize(settings)) { Dimension dim; dim.tok = nullptr; dim.num = Token::getStrArraySize(stringLiteral); dim.known = array->hasKnownValue(); dimensions->emplace_back(dim); } else if (array->valueType() && array->valueType()->pointer >= 1 && array->valueType()->isIntegral()) { const ValueFlow::Value *value = getBufferSizeValue(array); if (!value) return false; *errorPath = value->errorPath; Dimension dim; dim.known = value->isKnown(); dim.tok = nullptr; dim.num = value->intvalue / array->valueType()->typeSize(*settings); dimensions->emplace_back(dim); } return !dimensions->empty(); } static std::vector getOverrunIndexValues(const Token *tok, const Token *arrayToken, const std::vector &dimensions, const std::vector &indexTokens) { const Token *array = arrayToken; while (Token::Match(array, ".|::")) array = array->astOperand2(); for (int cond = 0; cond < 2; cond++) { bool equal = false; bool overflow = false; bool allKnown = true; std::vector indexValues; for (int i = 0; i < dimensions.size() && i < indexTokens.size(); ++i) { const ValueFlow::Value *value = indexTokens[i]->getMaxValue(cond == 1); indexValues.push_back(value); if (!value) continue; if (!value->isKnown()) { if (!allKnown) continue; allKnown = false; } if (array->variable() && array->variable()->isArray() && dimensions[i].num == 0) continue; if (value->intvalue == dimensions[i].num) equal = true; else if (value->intvalue > dimensions[i].num) overflow = true; } if (equal && tok->str() != "[") continue; if (!overflow && equal) { const Token *parent = tok; while (Token::simpleMatch(parent, "[")) parent = parent->astParent(); if (!parent || parent->isUnaryOp("&")) continue; } if (overflow || equal) return indexValues; } return std::vector(); } void CheckBufferOverrun::arrayIndex() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "[") continue; const Token *array = tok->astOperand1(); while (Token::Match(array, ".|::")) array = array->astOperand2(); if (!array || ((!array->variable() || array->variable()->nameToken() == array) && array->tokType() != Token::eString)) continue; if (!array->scope()->isExecutable()) { // LHS in non-executable scope => This is just a definition const Token *parent = tok; while (parent && !Token::simpleMatch(parent->astParent(), "=")) parent = parent->astParent(); if (!parent || parent == parent->astParent()->astOperand1()) continue; } if (astIsContainer(array)) continue; std::vector indexTokens; for (const Token *tok2 = tok; tok2 && tok2->str() == "["; tok2 = tok2->link()->next()) { if (!tok2->astOperand2()) { indexTokens.clear(); break; } indexTokens.emplace_back(tok2->astOperand2()); } if (indexTokens.empty()) continue; std::vector dimensions; ErrorPath errorPath; bool mightBeLarger = false; if (!getDimensionsEtc(tok->astOperand1(), mSettings, &dimensions, &errorPath, &mightBeLarger)) continue; // Positive index if (!mightBeLarger) { // TODO check arrays with dim 1 also const std::vector &indexValues = getOverrunIndexValues(tok, tok->astOperand1(), dimensions, indexTokens); if (!indexValues.empty()) arrayIndexError(tok, dimensions, indexValues); } // Negative index bool neg = false; std::vector negativeIndexes; for (const Token * indexToken : indexTokens) { const ValueFlow::Value *negativeValue = indexToken->getValueLE(-1, mSettings); negativeIndexes.emplace_back(negativeValue); if (negativeValue) neg = true; } if (neg) { negativeIndexError(tok, dimensions, negativeIndexes); } } } static std::string stringifyIndexes(const std::string &array, const std::vector &indexValues) { if (indexValues.size() == 1) return MathLib::toString(indexValues[0]->intvalue); std::ostringstream ret; ret << array; for (const ValueFlow::Value *index : indexValues) { ret << "["; if (index) ret << index->intvalue; else ret << "*"; ret << "]"; } return ret.str(); } static std::string arrayIndexMessage(const Token *tok, const std::vector &dimensions, const std::vector &indexValues, const Token *condition) { auto add_dim = [](const std::string &s, const Dimension &dim) { return s + "[" + MathLib::toString(dim.num) + "]"; }; const std::string array = std::accumulate(dimensions.begin(), dimensions.end(), tok->astOperand1()->expressionString(), add_dim); std::ostringstream errmsg; if (condition) errmsg << ValueFlow::eitherTheConditionIsRedundant(condition) << " or the array '" + array + "' is accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds."; else errmsg << "Array '" << array << "' accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds."; return errmsg.str(); } void CheckBufferOverrun::arrayIndexError(const Token *tok, const std::vector &dimensions, const std::vector &indexes) { if (!tok) { reportError(tok, Severity::error, "arrayIndexOutOfBounds", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, false); reportError(tok, Severity::warning, "arrayIndexOutOfBoundsCond", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, false); return; } const Token *condition = nullptr; const ValueFlow::Value *index = nullptr; for (const ValueFlow::Value *indexValue: indexes) { if (!indexValue) continue; if (!indexValue->errorSeverity() && !mSettings->isEnabled(Settings::WARNING)) return; if (indexValue->condition) condition = indexValue->condition; if (!index || !indexValue->errorPath.empty()) index = indexValue; } reportError(getErrorPath(tok, index, "Array index out of bounds"), index->errorSeverity() ? Severity::error : Severity::warning, index->condition ? "arrayIndexOutOfBoundsCond" : "arrayIndexOutOfBounds", arrayIndexMessage(tok, dimensions, indexes, condition), CWE_BUFFER_OVERRUN, index->isInconclusive()); } void CheckBufferOverrun::negativeIndexError(const Token *tok, const std::vector &dimensions, const std::vector &indexes) { if (!tok) { reportError(tok, Severity::error, "negativeIndex", "Negative array index", CWE_BUFFER_UNDERRUN, false); return; } const Token *condition = nullptr; const ValueFlow::Value *negativeValue = nullptr; for (const ValueFlow::Value *indexValue: indexes) { if (!indexValue) continue; if (!indexValue->errorSeverity() && !mSettings->isEnabled(Settings::WARNING)) return; if (indexValue->condition) condition = indexValue->condition; if (!negativeValue || !indexValue->errorPath.empty()) negativeValue = indexValue; } reportError(getErrorPath(tok, negativeValue, "Negative array index"), negativeValue->errorSeverity() ? Severity::error : Severity::warning, "negativeIndex", arrayIndexMessage(tok, dimensions, indexes, condition), CWE_BUFFER_UNDERRUN, negativeValue->isInconclusive()); } //--------------------------------------------------------------------------- void CheckBufferOverrun::pointerArithmetic() { if (!mSettings->isEnabled(Settings::PORTABILITY)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "+|-")) continue; if (!tok->valueType() || tok->valueType()->pointer == 0) continue; if (!tok->astOperand1() || !tok->astOperand2()) continue; if (!tok->astOperand1()->valueType() || !tok->astOperand2()->valueType()) continue; const Token *arrayToken, *indexToken; if (tok->astOperand1()->valueType()->pointer > 0) { arrayToken = tok->astOperand1(); indexToken = tok->astOperand2(); } else { arrayToken = tok->astOperand2(); indexToken = tok->astOperand1(); } if (!indexToken || !indexToken->valueType() || indexToken->valueType()->pointer > 0 || !indexToken->valueType()->isIntegral()) continue; std::vector dimensions; ErrorPath errorPath; bool mightBeLarger = false; if (!getDimensionsEtc(arrayToken, mSettings, &dimensions, &errorPath, &mightBeLarger)) continue; if (tok->str() == "+") { // Positive index if (!mightBeLarger) { // TODO check arrays with dim 1 also const std::vector indexTokens{indexToken}; const std::vector &indexValues = getOverrunIndexValues(tok, arrayToken, dimensions, indexTokens); if (!indexValues.empty()) pointerArithmeticError(tok, indexToken, indexValues.front()); } if (const ValueFlow::Value *neg = indexToken->getValueLE(-1, mSettings)) pointerArithmeticError(tok, indexToken, neg); } else if (tok->str() == "-") { // TODO } } } void CheckBufferOverrun::pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue) { if (!tok) { reportError(tok, Severity::portability, "pointerOutOfBounds", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, false); reportError(tok, Severity::portability, "pointerOutOfBoundsCond", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, false); return; } std::string errmsg; if (indexValue->condition) errmsg = "Undefined behaviour, when '" + indexToken->expressionString() + "' is " + MathLib::toString(indexValue->intvalue) + " the pointer arithmetic '" + tok->expressionString() + "' is out of bounds."; else errmsg = "Undefined behaviour, pointer arithmetic '" + tok->expressionString() + "' is out of bounds."; reportError(getErrorPath(tok, indexValue, "Pointer arithmetic overflow"), Severity::portability, indexValue->condition ? "pointerOutOfBoundsCond" : "pointerOutOfBounds", errmsg, CWE_POINTER_ARITHMETIC_OVERFLOW, indexValue->isInconclusive()); } //--------------------------------------------------------------------------- ValueFlow::Value CheckBufferOverrun::getBufferSize(const Token *bufTok) const { if (!bufTok->valueType()) return ValueFlow::Value(-1); const Variable *var = bufTok->variable(); if (!var || var->dimensions().empty()) { const ValueFlow::Value *value = getBufferSizeValue(bufTok); if (value) return *value; } if (!var) return ValueFlow::Value(-1); MathLib::bigint dim = std::accumulate(var->dimensions().begin(), var->dimensions().end(), 1LL, [](MathLib::bigint i1, const Dimension &dim) { return i1 * dim.num; }); ValueFlow::Value v; v.setKnown(); v.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; if (var->isPointerArray()) v.intvalue = dim * mSettings->sizeof_pointer; else if (var->isPointer()) return ValueFlow::Value(-1); else { const MathLib::bigint typeSize = bufTok->valueType()->typeSize(*mSettings); v.intvalue = dim * typeSize; } return v; } //--------------------------------------------------------------------------- static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::MinSize &minsize, const std::vector &args, const MathLib::bigint bufferSize, const Settings *settings) { const Token * const arg = (minsize.arg > 0 && minsize.arg - 1 < args.size()) ? args[minsize.arg - 1] : nullptr; const Token * const arg2 = (minsize.arg2 > 0 && minsize.arg2 - 1 < args.size()) ? args[minsize.arg2 - 1] : nullptr; switch (minsize.type) { case Library::ArgumentChecks::MinSize::Type::STRLEN: if (settings->library.isargformatstr(ftok, minsize.arg)) { return getMinFormatStringOutputLength(args, minsize.arg) < bufferSize; } else if (arg) { const Token *strtoken = arg->getValueTokenMaxStrLength(); if (strtoken) return Token::getStrLength(strtoken) < bufferSize; } break; case Library::ArgumentChecks::MinSize::Type::ARGVALUE: if (arg && arg->hasKnownIntValue()) return arg->getKnownIntValue() <= bufferSize; break; case Library::ArgumentChecks::MinSize::Type::SIZEOF: // TODO break; case Library::ArgumentChecks::MinSize::Type::MUL: if (arg && arg2 && arg->hasKnownIntValue() && arg2->hasKnownIntValue()) return (arg->getKnownIntValue() * arg2->getKnownIntValue()) <= bufferSize; break; case Library::ArgumentChecks::MinSize::Type::VALUE: return minsize.value <= bufferSize; case Library::ArgumentChecks::MinSize::Type::NONE: break; }; return true; } void CheckBufferOverrun::bufferOverflow() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% (") || Token::simpleMatch(tok, ") {")) continue; if (!mSettings->library.hasminsize(tok)) continue; const std::vector args = getArguments(tok); for (int argnr = 0; argnr < args.size(); ++argnr) { if (!args[argnr]->valueType() || args[argnr]->valueType()->pointer == 0) continue; const std::vector *minsizes = mSettings->library.argminsizes(tok, argnr + 1); if (!minsizes || minsizes->empty()) continue; // Get buffer size.. const Token *argtok = args[argnr]; while (argtok && argtok->isCast()) argtok = argtok->astOperand2() ? argtok->astOperand2() : argtok->astOperand1(); while (Token::Match(argtok, ".|::")) argtok = argtok->astOperand2(); if (!argtok || !argtok->variable()) continue; if (argtok->valueType() && argtok->valueType()->pointer == 0) continue; // TODO: strcpy(buf+10, "hello"); const ValueFlow::Value bufferSize = getBufferSize(argtok); if (bufferSize.intvalue <= 1) continue; bool error = std::none_of(minsizes->begin(), minsizes->end(), [=](const Library::ArgumentChecks::MinSize &minsize) { return checkBufferSize(tok, minsize, args, bufferSize.intvalue, mSettings); }); if (error) bufferOverflowError(args[argnr], &bufferSize); } } } } void CheckBufferOverrun::bufferOverflowError(const Token *tok, const ValueFlow::Value *value) { reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? tok->expressionString() : "buf"), CWE_BUFFER_OVERRUN, false); } //--------------------------------------------------------------------------- void CheckBufferOverrun::arrayIndexThenCheck() { if (!mSettings->isEnabled(Settings::PORTABILITY)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * const scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { tok = tok->linkAt(1); continue; } if (Token::Match(tok, "%name% [ %var% ]")) { tok = tok->next(); const int indexID = tok->next()->varId(); const std::string& indexName(tok->strAt(1)); // Iterate AST upwards const Token* tok2 = tok; const Token* tok3 = tok2; while (tok2->astParent() && tok2->tokType() != Token::eLogicalOp && tok2->str() != "?") { tok3 = tok2; tok2 = tok2->astParent(); } // Ensure that we ended at a logical operator and that we came from its left side if (tok2->tokType() != Token::eLogicalOp || tok2->astOperand1() != tok3) continue; // check if array index is ok // statement can be closed in parentheses, so "(| " is using if (Token::Match(tok2, "&& (| %varid% <|<=", indexID)) arrayIndexThenCheckError(tok, indexName); else if (Token::Match(tok2, "&& (| %any% >|>= %varid% !!+", indexID)) arrayIndexThenCheckError(tok, indexName); } } } } void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::string &indexName) { reportError(tok, Severity::style, "arrayIndexThenCheck", "$symbol:" + indexName + "\n" "Array index '$symbol' is used before limits check.\n" "Defensive programming: The variable '$symbol' is used as an array index before it " "is checked that is within limits. This can mean that the array might be accessed out of bounds. " "Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will " "not be accessed if the index is out of limits.", CWE_ARRAY_INDEX_THEN_CHECK, false); } //--------------------------------------------------------------------------- void CheckBufferOverrun::stringNotZeroTerminated() { // this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3 if (!mSettings->isEnabled(Settings::WARNING) || !mSettings->inconclusive) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * const scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "strncpy (")) continue; const std::vector args = getArguments(tok); if (args.size() != 3) continue; const Token *sizeToken = args[2]; if (!sizeToken->hasKnownIntValue()) continue; const ValueFlow::Value &bufferSize = getBufferSize(args[0]); if (bufferSize.intvalue < 0 || sizeToken->getKnownIntValue() < bufferSize.intvalue) continue; const Token *srcValue = args[1]->getValueTokenMaxStrLength(); if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue()) continue; // Is the buffer zero terminated after the call? bool isZeroTerminated = false; for (const Token *tok2 = tok->next()->link(); tok2 != scope->bodyEnd; tok2 = tok2->next()) { if (!Token::simpleMatch(tok2, "] =")) continue; const Token *rhs = tok2->next()->astOperand2(); if (!rhs || !rhs->hasKnownIntValue() || rhs->getKnownIntValue() != 0) continue; if (isSameExpression(mTokenizer->isCPP(), false, args[0], tok2->link()->astOperand1(), mSettings->library, false, false)) isZeroTerminated = true; } if (isZeroTerminated) continue; // TODO: Locate unsafe string usage.. terminateStrncpyError(tok, args[0]->expressionString()); } } } void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname) { const std::string shortMessage = "The buffer '$symbol' may not be null-terminated after the call to strncpy()."; reportError(tok, Severity::warning, "terminateStrncpy", "$symbol:" + varname + '\n' + shortMessage + '\n' + shortMessage + ' ' + "If the source string's size fits or exceeds the given size, strncpy() does not add a " "zero at the end of the buffer. This causes bugs later in the code if the code " "assumes buffer is null-terminated.", CWE170, true); } void CheckBufferOverrun::bufferNotZeroTerminatedError(const Token *tok, const std::string &varname, const std::string &function) { const std::string errmsg = "$symbol:" + varname + '\n' + "$symbol:" + function + '\n' + "The buffer '" + varname + "' is not null-terminated after the call to " + function + "().\n" "The buffer '" + varname + "' is not null-terminated after the call to " + function + "(). " "This will cause bugs later in the code if the code assumes the buffer is null-terminated."; reportError(tok, Severity::warning, "bufferNotZeroTerminated", errmsg, CWE170, true); } //--------------------------------------------------------------------------- // CTU.. //--------------------------------------------------------------------------- std::string CheckBufferOverrun::MyFileInfo::toString() const { std::string xml; if (!unsafeArrayIndex.empty()) xml = " \n" + CTU::toString(unsafeArrayIndex) + " \n"; if (!unsafePointerArith.empty()) xml += " \n" + CTU::toString(unsafePointerArith) + " \n"; return xml; } bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Check *check, const Token *argtok, MathLib::bigint *offset, int type) { const CheckBufferOverrun *c = dynamic_cast(check); if (!c) return false; if (!argtok->valueType() || argtok->valueType()->typeSize(*c->mSettings) == 0) return false; const Token *indexTok = nullptr; if (type == 1 && Token::Match(argtok, "%name% [") && argtok->astParent() == argtok->next() && !Token::simpleMatch(argtok->linkAt(1), "] [")) indexTok = argtok->next()->astOperand2(); else if (type == 2 && Token::simpleMatch(argtok->astParent(), "+")) indexTok = (argtok == argtok->astParent()->astOperand1()) ? argtok->astParent()->astOperand2() : argtok->astParent()->astOperand1(); if (!indexTok) return false; if (!indexTok->hasKnownIntValue()) return false; if (!offset) return false; *offset = indexTok->getKnownIntValue() * argtok->valueType()->typeSize(*c->mSettings); return true; } bool CheckBufferOverrun::isCtuUnsafeArrayIndex(const Check *check, const Token *argtok, MathLib::bigint *offset) { return CheckBufferOverrun::isCtuUnsafeBufferUsage(check, argtok, offset, 1); } bool CheckBufferOverrun::isCtuUnsafePointerArith(const Check *check, const Token *argtok, MathLib::bigint *offset) { return CheckBufferOverrun::isCtuUnsafeBufferUsage(check, argtok, offset, 2); } /** @brief Parse current TU and extract file info */ Check::FileInfo *CheckBufferOverrun::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { CheckBufferOverrun checkBufferOverrun(tokenizer, settings, nullptr); MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeArrayIndex = CTU::getUnsafeUsage(tokenizer, settings, &checkBufferOverrun, isCtuUnsafeArrayIndex); fileInfo->unsafePointerArith = CTU::getUnsafeUsage(tokenizer, settings, &checkBufferOverrun, isCtuUnsafePointerArith); if (fileInfo->unsafeArrayIndex.empty() && fileInfo->unsafePointerArith.empty()) { delete fileInfo; return nullptr; } return fileInfo; } Check::FileInfo * CheckBufferOverrun::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { const std::string arrayIndex("array-index"); const std::string pointerArith("pointer-arith"); MyFileInfo *fileInfo = new MyFileInfo; for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { if (e->Name() == arrayIndex) fileInfo->unsafeArrayIndex = CTU::loadUnsafeUsageListFromXml(e); else if (e->Name() == pointerArith) fileInfo->unsafePointerArith = CTU::loadUnsafeUsageListFromXml(e); } if (fileInfo->unsafeArrayIndex.empty() && fileInfo->unsafePointerArith.empty()) { delete fileInfo; return nullptr; } return fileInfo; } /** @brief Analyse all file infos for all TU */ bool CheckBufferOverrun::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { if (!ctu) return false; bool foundErrors = false; (void)settings; // This argument is unused const std::map> callsMap = ctu->getCallsMap(); for (Check::FileInfo *fi1 : fileInfo) { const MyFileInfo *fi = dynamic_cast(fi1); if (!fi) continue; for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeArrayIndex) foundErrors |= analyseWholeProgram1(ctu, callsMap, unsafeUsage, 1, errorLogger); for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafePointerArith) foundErrors |= analyseWholeProgram1(ctu, callsMap, unsafeUsage, 2, errorLogger); } return foundErrors; } bool CheckBufferOverrun::analyseWholeProgram1(const CTU::FileInfo *ctu, const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger) { const CTU::FileInfo::FunctionCall *functionCall = nullptr; const std::list &locationList = ctu->getErrorPath(CTU::FileInfo::InvalidValueType::bufferOverflow, unsafeUsage, callsMap, "Using argument ARG", &functionCall, false); if (locationList.empty()) return false; const char *errorId = nullptr; std::string errmsg; CWE cwe(0); if (type == 1) { errorId = "ctuArrayIndex"; if (unsafeUsage.value > 0) errmsg = "Array index out of bounds; '" + unsafeUsage.myArgumentName + "' buffer size is " + MathLib::toString(functionCall->callArgValue) + " and it is accessed at offset " + MathLib::toString(unsafeUsage.value) + "."; else errmsg = "Array index out of bounds; buffer '" + unsafeUsage.myArgumentName + "' is accessed at offset " + MathLib::toString(unsafeUsage.value) + "."; cwe = (unsafeUsage.value > 0) ? CWE_BUFFER_OVERRUN : CWE_BUFFER_UNDERRUN; } else { errorId = "ctuPointerArith"; errmsg = "Pointer arithmetic overflow; '" + unsafeUsage.myArgumentName + "' buffer size is " + MathLib::toString(functionCall->callArgValue); cwe = CWE_POINTER_ARITHMETIC_OVERFLOW; } const ErrorLogger::ErrorMessage errorMessage(locationList, emptyString, Severity::error, errmsg, errorId, cwe, false); errorLogger.reportErr(errorMessage); return true; } void CheckBufferOverrun::objectIndex() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "[")) continue; const Token *obj = tok->astOperand1(); const Token *idx = tok->astOperand2(); if (!idx || !obj) continue; if (idx->hasKnownIntValue() && idx->getKnownIntValue() == 0) continue; ValueFlow::Value v = getLifetimeObjValue(obj); if (!v.isLocalLifetimeValue()) continue; if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) continue; const Variable *var = v.tokvalue->variable(); if (var->isReference()) continue; if (var->isRValueReference()) continue; if (var->isArray()) continue; if (var->isPointer()) continue; objectIndexError(tok, &v, idx->hasKnownIntValue()); } } } void CheckBufferOverrun::objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known) { ErrorPath errorPath; std::string name; if (v) { name = v->tokvalue->variable()->name(); errorPath = v->errorPath; } errorPath.emplace_back(tok, ""); std::string verb = known ? "is" : "might be"; reportError(errorPath, known ? Severity::error : Severity::warning, "objectIndex", "The address of local variable '" + name + "' " + verb + " accessed at non-zero index.", CWE758, false); } cppcheck-1.90/lib/checkbufferoverrun.h000066400000000000000000000141741357737443600200640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkbufferoverrunH #define checkbufferoverrunH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "ctu.h" #include "errorlogger.h" #include "mathlib.h" #include "tokenize.h" #include "symboldatabase.h" #include #include #include #include #include /// @addtogroup Checks /// @{ /** * @brief buffer overruns and array index out of bounds * * Buffer overrun and array index out of bounds are pretty much the same. * But I generally use 'array index' if the code contains []. And the given * index is out of bounds. * I generally use 'buffer overrun' if you for example call a strcpy or * other function and pass a buffer and reads or writes too much data. */ class CPPCHECKLIB CheckBufferOverrun : public Check { public: /** This constructor is used when registering the CheckClass */ CheckBufferOverrun() : Check(myName()) { } /** This constructor is used when running checks. */ CheckBufferOverrun(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckBufferOverrun checkBufferOverrun(tokenizer, settings, errorLogger); checkBufferOverrun.arrayIndex(); checkBufferOverrun.pointerArithmetic(); checkBufferOverrun.bufferOverflow(); checkBufferOverrun.arrayIndexThenCheck(); checkBufferOverrun.stringNotZeroTerminated(); checkBufferOverrun.objectIndex(); } void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckBufferOverrun c(nullptr, settings, errorLogger); c.arrayIndexError(nullptr, std::vector(), std::vector()); c.pointerArithmeticError(nullptr, nullptr, nullptr); c.negativeIndexError(nullptr, std::vector(), std::vector()); c.arrayIndexThenCheckError(nullptr, "i"); c.bufferOverflowError(nullptr, nullptr); c.objectIndexError(nullptr, nullptr, true); } /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; private: void arrayIndex(); void arrayIndexError(const Token *tok, const std::vector &dimensions, const std::vector &indexes); void negativeIndexError(const Token *tok, const std::vector &dimensions, const std::vector &indexes); void pointerArithmetic(); void pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue); void bufferOverflow(); void bufferOverflowError(const Token *tok, const ValueFlow::Value *value); void arrayIndexThenCheck(); void arrayIndexThenCheckError(const Token *tok, const std::string &indexName); void stringNotZeroTerminated(); void terminateStrncpyError(const Token *tok, const std::string &varname); void bufferNotZeroTerminatedError(const Token *tok, const std::string &varname, const std::string &function); void objectIndex(); void objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known); ValueFlow::Value getBufferSize(const Token *bufTok) const; // CTU /** data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: /** unsafe array index usage */ std::list unsafeArrayIndex; /** unsafe pointer arithmetics */ std::list unsafePointerArith; /** Convert MyFileInfo data into xml string */ std::string toString() const OVERRIDE; }; static bool isCtuUnsafeBufferUsage(const Check *check, const Token *argtok, MathLib::bigint *offset, int type); static bool isCtuUnsafeArrayIndex(const Check *check, const Token *argtok, MathLib::bigint *offset); static bool isCtuUnsafePointerArith(const Check *check, const Token *argtok, MathLib::bigint *offset); Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const OVERRIDE; bool analyseWholeProgram1(const CTU::FileInfo *ctu, const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger); static std::string myName() { return "Bounds checking"; } std::string classInfo() const OVERRIDE { return "Out of bounds checking:\n" "- Array index out of bounds\n" "- Pointer arithmetic overflow\n" "- Buffer overflow\n" "- Dangerous usage of strncat()\n" "- Using array index before checking it\n" "- Partial string write that leads to buffer that is not zero terminated.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkbufferoverrunH cppcheck-1.90/lib/checkclass.cpp000066400000000000000000003457371357737443600166460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkclass.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include #include #include #include //--------------------------------------------------------------------------- // Register CheckClass.. namespace { CheckClass instance; } static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE404(404U); // Improper Resource Shutdown or Release static const CWE CWE665(665U); // Improper Initialization static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE762(762U); // Mismatched Memory Management Routines static const char * getFunctionTypeName(Function::Type type) { switch (type) { case Function::eConstructor: return "constructor"; case Function::eCopyConstructor: return "copy constructor"; case Function::eMoveConstructor: return "move constructor"; case Function::eDestructor: return "destructor"; case Function::eFunction: return "function"; case Function::eOperatorEqual: return "operator="; case Function::eLambda: return "lambda"; } return ""; } static bool isVariableCopyNeeded(const Variable &var) { return var.isPointer() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True) || (var.valueType()->type >= ValueType::Type::CHAR); } //--------------------------------------------------------------------------- CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), mSymbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr) { } //--------------------------------------------------------------------------- // ClassCheck: Check that all class constructors are ok. //--------------------------------------------------------------------------- void CheckClass::constructors() { const bool printStyle = mSettings->isEnabled(Settings::STYLE); const bool printWarnings = mSettings->isEnabled(Settings::WARNING); if (!printStyle && !printWarnings) return; const bool printInconclusive = mSettings->inconclusive; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { const bool unusedTemplate = Token::simpleMatch(scope->classDef->previous(), ">"); bool usedInUnion = false; for (const Scope &unionScope : mSymbolDatabase->scopeList) { if (unionScope.type != Scope::eUnion) continue; for (const Variable &var : unionScope.varlist) { if (var.type() && var.type()->classScope == scope) { usedInUnion = true; break; } } } // There are no constructors. if (scope->numConstructors == 0 && printStyle && !usedInUnion) { // If there is a private variable, there should be a constructor.. for (const Variable &var : scope->varlist) { const Token *initTok = var.nameToken(); while (Token::simpleMatch(initTok->next(), "[")) initTok = initTok->linkAt(1); if (var.isPrivate() && !var.isStatic() && !Token::Match(var.nameToken(), "%varid% ; %varid% =", var.declarationId()) && !Token::Match(initTok, "%var%|] {|=") && (!var.isClass() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True))) { noConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct"); break; } } } if (!printWarnings) continue; // #3196 => bailout if there are nested unions // TODO: handle union variables better { bool bailout = false; for (const Scope * const nestedScope : scope->nestedList) { if (nestedScope->type == Scope::eUnion) { bailout = true; break; } } if (bailout) continue; } std::vector usage(scope->varlist.size()); for (const Function &func : scope->functionList) { if (!func.hasBody() || !(func.isConstructor() || func.type == Function::eOperatorEqual)) continue; // Bail: If initializer list is not recognized as a variable or type then skip since parsing is incomplete if (unusedTemplate && func.type == Function::eConstructor) { const Token *initList = func.constructorMemberInitialization(); if (Token::Match(initList, ": %name% (") && initList->next()->tokType() == Token::eName) break; } // Mark all variables not used clearAllVar(usage); std::list callstack; initializeVarList(func, callstack, scope, usage); // Check if any variables are uninitialized int count = -1; for (const Variable &var : scope->varlist) { ++count; // check for C++11 initializer if (var.hasDefault()) { usage[count].init = true; continue; } if (usage[count].assign || usage[count].init || var.isStatic()) continue; if (var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty()) continue; if (var.isConst() && func.isOperator()) // We can't set const members in assignment operator continue; // Check if this is a class constructor if (!var.isPointer() && !var.isPointerArray() && var.isClass() && func.type == Function::eConstructor) { // Unknown type so assume it is initialized if (!var.type()) continue; // Known type that doesn't need initialization or // known type that has member variables of an unknown type else if (var.type()->needInitialization != Type::NeedInitialization::True) continue; } // Check if type can't be copied if (!var.isPointer() && !var.isPointerArray() && var.typeScope()) { if (func.type == Function::eMoveConstructor) { if (canNotMove(var.typeScope())) continue; } else { if (canNotCopy(var.typeScope())) continue; } } bool inconclusive = false; // Don't warn about unknown types in copy constructors since we // don't know if they can be copied or not.. if ((func.type == Function::eCopyConstructor || func.type == Function::eMoveConstructor || func.type == Function::eOperatorEqual) && !isVariableCopyNeeded(var)) inconclusive = true; if (!printInconclusive && inconclusive) continue; // It's non-static and it's not initialized => error if (func.type == Function::eOperatorEqual) { const Token *operStart = func.arg; bool classNameUsed = false; for (const Token *operTok = operStart; operTok != operStart->link(); operTok = operTok->next()) { if (operTok->str() == scope->className) { classNameUsed = true; break; } } if (classNameUsed) operatorEqVarError(func.token, scope->className, var.name(), inconclusive); } else if (func.access != AccessControl::Private || mSettings->standards.cpp >= Standards::CPP11) { // If constructor is not in scope then we maybe using a constructor from a different template specialization if (!precedes(scope->bodyStart, func.tokenDef)) continue; const Scope *varType = var.typeScope(); if (!varType || varType->type != Scope::eUnion) { if (func.type == Function::eConstructor && func.nestedIn && (func.nestedIn->numConstructors - func.nestedIn->numCopyOrMoveConstructors) > 1 && func.argCount() == 0 && func.functionScope && func.arg && func.arg->link()->next() == func.functionScope->bodyStart && func.functionScope->bodyStart->link() == func.functionScope->bodyStart->next()) { // don't warn about user defined default constructor when there are other constructors if (printInconclusive) uninitVarError(func.token, func.access == AccessControl::Private, scope->className, var.name(), true); } else uninitVarError(func.token, func.access == AccessControl::Private, scope->className, var.name(), inconclusive); } } } } } } void CheckClass::checkExplicitConstructors() { if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // Do not perform check, if the class/struct has not any constructors if (scope->numConstructors == 0) continue; // Is class abstract? Maybe this test is over-simplification, but it will suffice for simple cases, // and it will avoid false positives. bool isAbstractClass = false; for (const Function &func : scope->functionList) { if (func.isPure()) { isAbstractClass = true; break; } } // Abstract classes can't be instantiated. But if there is C++11 // "misuse" by derived classes then these constructors must be explicit. if (isAbstractClass && mSettings->standards.cpp != Standards::CPP11) continue; for (const Function &func : scope->functionList) { // We are looking for constructors, which are meeting following criteria: // 1) Constructor is declared with a single parameter // 2) Constructor is not declared as explicit // 3) It is not a copy/move constructor of non-abstract class // 4) Constructor is not marked as delete (programmer can mark the default constructor as deleted, which is ok) if (!func.isConstructor() || func.isDelete() || (!func.hasBody() && func.access == AccessControl::Private)) continue; if (!func.isExplicit() && func.minArgCount() == 1 && func.type != Function::eCopyConstructor && func.type != Function::eMoveConstructor) { noExplicitConstructorError(func.tokenDef, scope->className, scope->type == Scope::eStruct); } } } } static bool isNonCopyable(const Scope *scope, bool *unknown) { bool u = false; // check if there is base class that is not copyable for (const Type::BaseInfo &baseInfo : scope->definedType->derivedFrom) { if (!baseInfo.type || !baseInfo.type->classScope) { u = true; continue; } if (isNonCopyable(baseInfo.type->classScope, &u)) return true; for (const Function &func : baseInfo.type->classScope->functionList) { if (func.type != Function::eCopyConstructor) continue; if (func.access == AccessControl::Private || func.isDelete()) return true; } } *unknown = u; return false; } void CheckClass::copyconstructors() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { std::map allocatedVars; for (const Function &func : scope->functionList) { if (func.type != Function::eConstructor || !func.functionScope) continue; const Token* tok = func.token->linkAt(1); for (const Token* const end = func.functionScope->bodyStart; tok != end; tok = tok->next()) { if (Token::Match(tok, "%var% ( new") || (Token::Match(tok, "%var% ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) { const Variable* var = tok->variable(); if (var && var->isPointer() && var->scope() == scope) allocatedVars[tok->varId()] = tok; } } for (const Token* const end = func.functionScope->bodyEnd; tok != end; tok = tok->next()) { if (Token::Match(tok, "%var% = new") || (Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) { const Variable* var = tok->variable(); if (var && var->isPointer() && var->scope() == scope && !var->isStatic()) allocatedVars[tok->varId()] = tok; } } } if (!allocatedVars.empty()) { const Function *funcCopyCtor = nullptr; const Function *funcOperatorEq = nullptr; const Function *funcDestructor = nullptr; for (const Function &func : scope->functionList) { if (func.type == Function::eCopyConstructor) funcCopyCtor = &func; else if (func.type == Function::eOperatorEqual) funcOperatorEq = &func; else if (func.type == Function::eDestructor) funcDestructor = &func; } if (!funcCopyCtor || funcCopyCtor->isDefault()) { bool unknown = false; if (!isNonCopyable(scope, &unknown) && !unknown) noCopyConstructorError(scope, funcCopyCtor, allocatedVars.begin()->second, unknown); } if (!funcOperatorEq || funcOperatorEq->isDefault()) { bool unknown = false; if (!isNonCopyable(scope, &unknown) && !unknown) noOperatorEqError(scope, funcOperatorEq, allocatedVars.begin()->second, unknown); } if (!funcDestructor || funcDestructor->isDefault()) { const Token * mustDealloc = nullptr; for (std::map::const_iterator it = allocatedVars.begin(); it != allocatedVars.end(); ++it) { if (!Token::Match(it->second, "%var% [(=] new %type%")) { mustDealloc = it->second; break; } if (it->second->valueType() && it->second->valueType()->isIntegral()) { mustDealloc = it->second; break; } const Variable *var = it->second->variable(); if (var && var->typeScope() && var->typeScope()->functionList.empty() && var->type()->derivedFrom.empty()) mustDealloc = it->second; } if (mustDealloc) noDestructorError(scope, funcDestructor, mustDealloc); } } std::set copiedVars; const Token* copyCtor = nullptr; for (const Function &func : scope->functionList) { if (func.type != Function::eCopyConstructor) continue; copyCtor = func.tokenDef; if (!func.functionScope) { allocatedVars.clear(); break; } const Token* tok = func.tokenDef->linkAt(1)->next(); if (tok->str()==":") { tok=tok->next(); while (Token::Match(tok, "%name% (")) { if (allocatedVars.find(tok->varId()) != allocatedVars.end()) { if (tok->varId() && Token::Match(tok->tokAt(2), "%name% . %name% )")) copiedVars.insert(tok); else if (!Token::Match(tok->tokAt(2), "%any% )")) allocatedVars.erase(tok->varId()); // Assume memory is allocated } tok = tok->linkAt(1)->tokAt(2); } } for (tok = func.functionScope->bodyStart; tok != func.functionScope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%var% = new|malloc|g_malloc|g_try_malloc|realloc|g_realloc|g_try_realloc")) { allocatedVars.erase(tok->varId()); } else if (Token::Match(tok, "%var% = %name% . %name% ;") && allocatedVars.find(tok->varId()) != allocatedVars.end()) { copiedVars.insert(tok); } } break; } if (copyCtor && !copiedVars.empty()) { for (const Token *cv : copiedVars) copyConstructorShallowCopyError(cv, cv->str()); // throw error if count mismatch /* FIXME: This doesn't work. See #4154 for (std::map::const_iterator i = allocatedVars.begin(); i != allocatedVars.end(); ++i) { copyConstructorMallocError(copyCtor, i->second, i->second->str()); } */ } } } /* This doesn't work. See #4154 void CheckClass::copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& varname) { std::list callstack; callstack.push_back(cctor); callstack.push_back(alloc); reportError(callstack, Severity::warning, "copyCtorNoAllocation", "Copy constructor does not allocate memory for member '" + varname + "' although memory has been allocated in other constructors."); } */ void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string& varname) { reportError(tok, Severity::warning, "copyCtorPointerCopying", "$symbol:" + varname + "\nValue of pointer '$symbol', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, false); } static std::string noMemberErrorMessage(const Scope *scope, const char function[], bool isdefault) { const std::string &classname = scope ? scope->className : "class"; const std::string type = (scope && scope->type == Scope::eStruct) ? "Struct" : "Class"; const bool isDestructor = (function[0] == 'd'); std::string errmsg = "$symbol:" + classname + '\n'; if (isdefault) { errmsg += type + " '$symbol' has dynamic memory/resource allocation(s). The " + function + " is explicitly defaulted but the default " + function + " does not work well."; if (isDestructor) errmsg += " It is recommended to define the " + std::string(function) + '.'; else errmsg += " It is recommended to define or delete the " + std::string(function) + '.'; } else { errmsg += type + " '$symbol' does not have a " + function + " which is recommended since it has dynamic memory/resource allocation(s)."; } return errmsg; } void CheckClass::noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) { reportError(alloc, Severity::warning, "noCopyConstructor", noMemberErrorMessage(scope, "copy constructor", isdefault), CWE398, inconclusive); } void CheckClass::noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) { reportError(alloc, Severity::warning, "noOperatorEq", noMemberErrorMessage(scope, "operator=", isdefault), CWE398, inconclusive); } void CheckClass::noDestructorError(const Scope *scope, bool isdefault, const Token *alloc) { reportError(alloc, Severity::warning, "noDestructor", noMemberErrorMessage(scope, "destructor", isdefault), CWE398, false); } bool CheckClass::canNotCopy(const Scope *scope) { bool constructor = false; bool publicAssign = false; bool publicCopy = false; for (const Function &func : scope->functionList) { if (func.isConstructor()) constructor = true; if (func.access != AccessControl::Public) continue; if (func.type == Function::eCopyConstructor) { publicCopy = true; break; } else if (func.type == Function::eOperatorEqual) { publicAssign = true; break; } } return constructor && !(publicAssign || publicCopy); } bool CheckClass::canNotMove(const Scope *scope) { bool constructor = false; bool publicAssign = false; bool publicCopy = false; bool publicMove = false; for (const Function &func : scope->functionList) { if (func.isConstructor()) constructor = true; if (func.access != AccessControl::Public) continue; if (func.type == Function::eCopyConstructor) { publicCopy = true; break; } else if (func.type == Function::eMoveConstructor) { publicMove = true; break; } else if (func.type == Function::eOperatorEqual) { publicAssign = true; break; } } return constructor && !(publicAssign || publicCopy || publicMove); } void CheckClass::assignVar(nonneg int varid, const Scope *scope, std::vector &usage) { int count = 0; for (std::list::const_iterator var = scope->varlist.begin(); var != scope->varlist.end(); ++var, ++count) { if (var->declarationId() == varid) { usage[count].assign = true; return; } } } void CheckClass::initVar(nonneg int varid, const Scope *scope, std::vector &usage) { int count = 0; for (std::list::const_iterator var = scope->varlist.begin(); var != scope->varlist.end(); ++var, ++count) { if (var->declarationId() == varid) { usage[count].init = true; return; } } } void CheckClass::assignAllVar(std::vector &usage) { for (Usage & i : usage) i.assign = true; } void CheckClass::clearAllVar(std::vector &usage) { for (Usage & i : usage) { i.assign = false; i.init = false; } } bool CheckClass::isBaseClassFunc(const Token *tok, const Scope *scope) { // Iterate through each base class... for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { const Type *derivedFrom = i.type; // Check if base class exists in database if (derivedFrom && derivedFrom->classScope) { const std::list& functionList = derivedFrom->classScope->functionList; for (const Function &func : functionList) { if (func.tokenDef->str() == tok->str()) return true; } } // Base class not found so assume it is in it. else return true; } return false; } void CheckClass::initializeVarList(const Function &func, std::list &callstack, const Scope *scope, std::vector &usage) { if (!func.functionScope) throw InternalError(nullptr, "Internal Error: Invalid syntax"); // #5702 bool initList = func.isConstructor(); const Token *ftok = func.arg->link()->next(); int level = 0; for (; ftok && ftok != func.functionScope->bodyEnd; ftok = ftok->next()) { // Class constructor.. initializing variables like this // clKalle::clKalle() : var(value) { } if (initList) { if (level == 0 && Token::Match(ftok, "%name% {|(") && Token::Match(ftok->linkAt(1), "}|) ,|{")) { if (ftok->str() != func.name()) { initVar(ftok->varId(), scope, usage); } else { // c++11 delegate constructor const Function *member = ftok->function(); // member function not found => assume it initializes all members if (!member) { assignAllVar(usage); return; } // recursive call // assume that all variables are initialized if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { /** @todo false negative: just bail */ assignAllVar(usage); return; } // member function has implementation if (member->hasBody()) { // initialize variable use list using member function callstack.push_back(member); initializeVarList(*member, callstack, scope, usage); callstack.pop_back(); } // there is a called member function, but it has no implementation, so we assume it initializes everything else { assignAllVar(usage); } } } else if (level != 0 && Token::Match(ftok, "%name% =")) // assignment in the initializer: var(value = x) assignVar(ftok->varId(), scope, usage); // Level handling if (ftok->link() && Token::Match(ftok, "(|<")) level++; else if (ftok->str() == "{") { if (level != 0 || (Token::Match(ftok->previous(), "%name%|>") && Token::Match(ftok->link(), "} ,|{"))) level++; else initList = false; } else if (ftok->link() && Token::Match(ftok, ")|>|}")) level--; } if (initList) continue; // Variable getting value from stream? if (Token::Match(ftok, ">>|& %name%") && isLikelyStreamRead(true, ftok)) { assignVar(ftok->next()->varId(), scope, usage); } // If assignment comes after an && or || this is really inconclusive because of short circuiting if (Token::Match(ftok, "%oror%|&&")) continue; if (Token::simpleMatch(ftok, "( !")) ftok = ftok->next(); // Using the operator= function to initialize all variables.. if (Token::Match(ftok->next(), "return| (| * this )| =")) { assignAllVar(usage); break; } // Using swap to assign all variables.. if (func.type == Function::eOperatorEqual && Token::Match(ftok, "[;{}] %name% (") && Token::Match(ftok->linkAt(2), ") . %name% ( *| this ) ;")) { assignAllVar(usage); break; } // Calling member variable function? if (Token::Match(ftok->next(), "%var% . %name% (")) { for (const Variable &var : scope->varlist) { if (var.declarationId() == ftok->next()->varId()) { /** @todo false negative: we assume function changes variable state */ assignVar(ftok->next()->varId(), scope, usage); break; } } ftok = ftok->tokAt(2); } if (!Token::Match(ftok->next(), "::| %name%") && !Token::Match(ftok->next(), "*| this . %name%") && !Token::Match(ftok->next(), "* %name% =") && !Token::Match(ftok->next(), "( * this ) . %name%")) continue; // Goto the first token in this statement.. ftok = ftok->next(); // skip "return" if (ftok->str() == "return") ftok = ftok->next(); // Skip "( * this )" if (Token::simpleMatch(ftok, "( * this ) .")) { ftok = ftok->tokAt(5); } // Skip "this->" if (Token::simpleMatch(ftok, "this .")) ftok = ftok->tokAt(2); // Skip "classname :: " if (Token::Match(ftok, ":: %name%")) ftok = ftok->next(); while (Token::Match(ftok, "%name% ::")) ftok = ftok->tokAt(2); // Clearing all variables.. if (Token::Match(ftok, "::| memset ( this ,")) { assignAllVar(usage); return; } // Ticket #7068 else if (Token::Match(ftok, "::| memset ( &| this . %name%")) { if (ftok->str() == "::") ftok = ftok->next(); int offsetToMember = 4; if (ftok->strAt(2) == "&") ++offsetToMember; assignVar(ftok->tokAt(offsetToMember)->varId(), scope, usage); ftok = ftok->linkAt(1); continue; } // Clearing array.. else if (Token::Match(ftok, "::| memset ( %name% ,")) { if (ftok->str() == "::") ftok = ftok->next(); assignVar(ftok->tokAt(2)->varId(), scope, usage); ftok = ftok->linkAt(1); continue; } // Calling member function? else if (Token::simpleMatch(ftok, "operator= (") && ftok->previous()->str() != "::") { if (ftok->function() && ftok->function()->nestedIn == scope) { const Function *member = ftok->function(); // recursive call // assume that all variables are initialized if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { /** @todo false negative: just bail */ assignAllVar(usage); return; } // member function has implementation if (member->hasBody()) { // initialize variable use list using member function callstack.push_back(member); initializeVarList(*member, callstack, scope, usage); callstack.pop_back(); } // there is a called member function, but it has no implementation, so we assume it initializes everything else { assignAllVar(usage); } } // using default operator =, assume everything initialized else { assignAllVar(usage); } } else if (Token::Match(ftok, "::| %name% (") && !Token::Match(ftok, "if|while|for")) { if (ftok->str() == "::") ftok = ftok->next(); // Passing "this" => assume that everything is initialized for (const Token *tok2 = ftok->next()->link(); tok2 && tok2 != ftok; tok2 = tok2->previous()) { if (tok2->str() == "this") { assignAllVar(usage); return; } } // check if member function if (ftok->function() && ftok->function()->nestedIn == scope && !ftok->function()->isConstructor()) { const Function *member = ftok->function(); // recursive call // assume that all variables are initialized if (std::find(callstack.begin(), callstack.end(), member) != callstack.end()) { assignAllVar(usage); return; } // member function has implementation if (member->hasBody()) { // initialize variable use list using member function callstack.push_back(member); initializeVarList(*member, callstack, scope, usage); callstack.pop_back(); // Assume that variables that are passed to it are initialized.. for (const Token *tok2 = ftok; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}]")) break; if (Token::Match(tok2, "[(,] &| %name% [,)]")) { tok2 = tok2->next(); if (tok2->str() == "&") tok2 = tok2->next(); assignVar(tok2->varId(), scope, usage); } } } // there is a called member function, but it has no implementation, so we assume it initializes everything else { assignAllVar(usage); } } // not member function else { // could be a base class virtual function, so we assume it initializes everything if (!func.isConstructor() && isBaseClassFunc(ftok, scope)) { /** @todo False Negative: we should look at the base class functions to see if they * call any derived class virtual functions that change the derived class state */ assignAllVar(usage); } // has friends, so we assume it initializes everything if (!scope->definedType->friendList.empty()) assignAllVar(usage); // the function is external and it's neither friend nor inherited virtual function. // assume all variables that are passed to it are initialized.. else { for (const Token *tok = ftok->tokAt(2); tok && tok != ftok->next()->link(); tok = tok->next()) { if (tok->isName()) { assignVar(tok->varId(), scope, usage); } } } } } // Assignment of member variable? else if (Token::Match(ftok, "%name% =")) { assignVar(ftok->varId(), scope, usage); bool bailout = ftok->variable() && ftok->variable()->isReference(); const Token* tok2 = ftok->tokAt(2); if (tok2->str() == "&") { tok2 = tok2->next(); bailout = true; } if (tok2->variable() && (bailout || tok2->variable()->isArray()) && tok2->strAt(1) != "[") assignVar(tok2->varId(), scope, usage); } // Assignment of array item of member variable? else if (Token::Match(ftok, "%name% [|.")) { const Token *tok2 = ftok; while (tok2) { if (tok2->strAt(1) == "[") tok2 = tok2->next()->link(); else if (Token::Match(tok2->next(), ". %name%")) tok2 = tok2->tokAt(2); else break; } if (tok2 && tok2->strAt(1) == "=") assignVar(ftok->varId(), scope, usage); } // Assignment of array item of member variable? else if (Token::Match(ftok, "* %name% =")) { assignVar(ftok->next()->varId(), scope, usage); } else if (Token::Match(ftok, "* this . %name% =")) { assignVar(ftok->tokAt(3)->varId(), scope, usage); } // The functions 'clear' and 'Clear' are supposed to initialize variable. if (Token::Match(ftok, "%name% . clear|Clear (")) { assignVar(ftok->varId(), scope, usage); } } } void CheckClass::noConstructorError(const Token *tok, const std::string &classname, bool isStruct) { // For performance reasons the constructor might be intentionally missing. Therefore this is not a "warning" reportError(tok, Severity::style, "noConstructor", "$symbol:" + classname + "\n" + "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a constructor although it has private member variables.\n" "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' does not have a constructor " "although it has private member variables. Member variables of builtin types are left " "uninitialized when the class is instantiated. That may cause bugs or undefined behavior.", CWE398, false); } void CheckClass::noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct) { const std::string message(std::string(isStruct ? "Struct" : "Class") + " '$symbol' has a constructor with 1 argument that is not explicit."); const std::string verbose(message + " Such constructors should in general be explicit for type safety reasons. Using the explicit keyword in the constructor means some mistakes when using the class can be avoided."); reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, false); } void CheckClass::uninitVarError(const Token *tok, bool isprivate, const std::string &classname, const std::string &varname, bool inconclusive) { reportError(tok, Severity::warning, isprivate ? "uninitMemberVarPrivate" : "uninitMemberVar", "$symbol:" + classname + "::" + varname + "\nMember variable '$symbol' is not initialized in the constructor.", CWE398, inconclusive); } void CheckClass::operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive) { reportError(tok, Severity::warning, "operatorEqVarError", "$symbol:" + classname + "::" + varname + "\nMember variable '$symbol' is not assigned a value in '" + classname + "::operator='.", CWE398, inconclusive); } //--------------------------------------------------------------------------- // ClassCheck: Use initialization list instead of assignment //--------------------------------------------------------------------------- void CheckClass::initializationListUsage() { if (!mSettings->isEnabled(Settings::PERFORMANCE)) return; for (const Scope *scope : mSymbolDatabase->functionScopes) { // Check every constructor if (!scope->function || (!scope->function->isConstructor())) continue; const Scope* owner = scope->functionOf; for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%name% (")) // Assignments might depend on this function call or if/for/while/switch statement from now on. break; if (Token::Match(tok, "try|do {")) break; if (!Token::Match(tok, "%var% =") || tok->strAt(-1) == "*" || tok->strAt(-1) == ".") continue; const Variable* var = tok->variable(); if (!var || var->scope() != owner || var->isStatic()) continue; if (var->isPointer() || var->isReference() || var->isEnumType() || var->valueType()->type > ValueType::Type::ITERATOR) continue; // bailout: multi line lambda in rhs => do not warn if (findLambdaEndToken(tok->tokAt(2)) && tok->tokAt(2)->findExpressionStartEndTokens().second->linenr() > tok->tokAt(2)->linenr()) continue; // Access local var member in rhs => do not warn bool localmember = false; visitAstNodes(tok->next()->astOperand2(), [&](const Token *rhs) { if (rhs->str() == "." && rhs->astOperand1() && rhs->astOperand1()->variable() && rhs->astOperand1()->variable()->isLocal()) localmember = true; return ChildrenToVisit::op1_and_op2; }); if (localmember) continue; bool allowed = true; visitAstNodes(tok->next()->astOperand2(), [&](const Token *tok2) { const Variable* var2 = tok2->variable(); if (var2) { if (var2->scope() == owner && tok2->strAt(-1)!=".") { // Is there a dependency between two member variables? allowed = false; return ChildrenToVisit::done; } else if (var2->isArray() && var2->isLocal()) { // Can't initialize with a local array allowed = false; return ChildrenToVisit::done; } } else if (tok2->str() == "this") { // 'this' instance is not completely constructed in initialization list allowed = false; return ChildrenToVisit::done; } else if (Token::Match(tok2, "%name% (") && tok2->strAt(-1) != "." && isMemberFunc(owner, tok2)) { // Member function called? allowed = false; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); if (!allowed) continue; suggestInitializationList(tok, tok->str()); } } } void CheckClass::suggestInitializationList(const Token* tok, const std::string& varname) { reportError(tok, Severity::performance, "useInitializationList", "$symbol:" + varname + "\nVariable '$symbol' is assigned in constructor body. Consider performing initialization in initialization list.\n" "When an object of a class is created, the constructors of all member variables are called consecutively " "in the order the variables are declared, even if you don't explicitly write them to the initialization list. You " "could avoid assigning '$symbol' a value by passing the value to the constructor in the initialization list.", CWE398, false); } //--------------------------------------------------------------------------- // ClassCheck: Unused private functions //--------------------------------------------------------------------------- static bool checkFunctionUsage(const Function *privfunc, const Scope* scope) { if (!scope) return true; // Assume it is used, if scope is not seen for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->functionScope) { if (Token::Match(func->tokenDef, "%name% (")) { for (const Token *ftok = func->tokenDef->tokAt(2); ftok && ftok->str() != ")"; ftok = ftok->next()) { if (Token::Match(ftok, "= %name% [(,)]") && ftok->strAt(1) == privfunc->name()) return true; if (ftok->str() == "(") ftok = ftok->link(); } } for (const Token *ftok = func->functionScope->classDef->linkAt(1); ftok != func->functionScope->bodyEnd; ftok = ftok->next()) { if (ftok->function() == privfunc) return true; if (ftok->varId() == 0U && ftok->str() == privfunc->name()) // TODO: This condition should be redundant return true; } } else if ((func->type != Function::eCopyConstructor && func->type != Function::eOperatorEqual) || func->access != AccessControl::Private) // Assume it is used, if a function implementation isn't seen, but empty private copy constructors and assignment operators are OK return true; } const std::map::const_iterator end = scope->definedTypesMap.end(); for (std::map::const_iterator iter = scope->definedTypesMap.begin(); iter != end; ++ iter) { const Type *type = (*iter).second; if (type->enclosingScope == scope && checkFunctionUsage(privfunc, type->classScope)) return true; } for (const Variable &var : scope->varlist) { if (var.isStatic()) { const Token* tok = Token::findmatch(scope->bodyEnd, "%varid% =|(|{", var.declarationId()); if (tok) tok = tok->tokAt(2); while (tok && tok->str() != ";") { if (tok->function() == privfunc) return true; tok = tok->next(); } } } return false; // Unused in this scope } void CheckClass::privateFunctions() { if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // do not check borland classes with properties.. if (Token::findsimplematch(scope->bodyStart, "; __property ;", scope->bodyEnd)) continue; std::list privateFuncs; for (const Function &func : scope->functionList) { // Get private functions.. if (func.type == Function::eFunction && func.access == AccessControl::Private && !func.isOperator()) // TODO: There are smarter ways to check private operator usage privateFuncs.push_back(&func); } // Bailout for overridden virtual functions of base classes if (!scope->definedType->derivedFrom.empty()) { // Check virtual functions for (std::list::iterator it = privateFuncs.begin(); it != privateFuncs.end();) { if ((*it)->isImplicitlyVirtual(true)) // Give true as default value to be returned if we don't see all base classes privateFuncs.erase(it++); else ++it; } } while (!privateFuncs.empty()) { // Check that all private functions are used bool used = checkFunctionUsage(privateFuncs.front(), scope); // Usage in this class // Check in friend classes const std::vector& friendList = scope->definedType->friendList; for (int i = 0; i < friendList.size() && !used; i++) { if (friendList[i].type) used = checkFunctionUsage(privateFuncs.front(), friendList[i].type->classScope); else used = true; // Assume, it is used if we do not see friend class } if (!used) unusedPrivateFunctionError(privateFuncs.front()->tokenDef, scope->className, privateFuncs.front()->name()); privateFuncs.pop_front(); } } } void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname) { reportError(tok, Severity::style, "unusedPrivateFunction", "$symbol:" + classname + "::" + funcname + "\nUnused private function: '$symbol'", CWE398, false); } //--------------------------------------------------------------------------- // ClassCheck: Check that memset is not used on classes //--------------------------------------------------------------------------- static const Scope* findFunctionOf(const Scope* scope) { while (scope) { if (scope->type == Scope::eFunction) return scope->functionOf; scope = scope->nestedIn; } return nullptr; } void CheckClass::checkMemset() { const bool printWarnings = mSettings->isEnabled(Settings::WARNING); for (const Scope *scope : mSymbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "memset|memcpy|memmove (")) { const Token* arg1 = tok->tokAt(2); const Token* arg3 = arg1->nextArgument(); if (arg3) arg3 = arg3->nextArgument(); if (!arg3) // weird, shouldn't happen: memset etc should have // 3 arguments. continue; const Token *typeTok = nullptr; const Scope *type = nullptr; if (Token::Match(arg3, "sizeof ( %type% ) )")) typeTok = arg3->tokAt(2); else if (Token::Match(arg3, "sizeof ( %type% :: %type% ) )")) typeTok = arg3->tokAt(4); else if (Token::Match(arg3, "sizeof ( struct %type% ) )")) typeTok = arg3->tokAt(3); else if (Token::simpleMatch(arg3, "sizeof ( * this ) )") || Token::simpleMatch(arg1, "this ,")) { type = findFunctionOf(arg3->scope()); } else if (Token::Match(arg1, "&|*|%var%")) { int numIndirToVariableType = 0; // Offset to the actual type in terms of dereference/addressof for (;; arg1 = arg1->next()) { if (arg1->str() == "&") ++numIndirToVariableType; else if (arg1->str() == "*") --numIndirToVariableType; else break; } const Variable * const var = arg1->variable(); if (var && arg1->strAt(1) == ",") { if (var->isArrayOrPointer()) { const Token *endTok = var->typeEndToken(); while (Token::simpleMatch(endTok, "*")) { ++numIndirToVariableType; endTok = endTok->previous(); } } if (var->isArray()) numIndirToVariableType += int(var->dimensions().size()); if (numIndirToVariableType == 1) type = var->typeScope(); } } // No type defined => The tokens didn't match if (!typeTok && !type) continue; if (typeTok && typeTok->str() == "(") typeTok = typeTok->next(); if (!type && typeTok->type()) type = typeTok->type()->classScope; if (type) { const std::set parsedTypes; checkMemsetType(scope, tok, type, false, parsedTypes); } } else if (tok->variable() && tok->variable()->typeScope() && Token::Match(tok, "%var% = calloc|malloc|realloc|g_malloc|g_try_malloc|g_realloc|g_try_realloc (")) { const std::set parsedTypes; checkMemsetType(scope, tok->tokAt(2), tok->variable()->typeScope(), true, parsedTypes); if (printWarnings && tok->variable()->typeScope()->numConstructors > 0) mallocOnClassWarning(tok, tok->strAt(2), tok->variable()->typeScope()->classDef); } } } } void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes) { // If type has been checked there is no need to check it again if (parsedTypes.find(type) != parsedTypes.end()) return; parsedTypes.insert(type); const bool printPortability = mSettings->isEnabled(Settings::PORTABILITY); // recursively check all parent classes for (const Type::BaseInfo & i : type->definedType->derivedFrom) { const Type* derivedFrom = i.type; if (derivedFrom && derivedFrom->classScope) checkMemsetType(start, tok, derivedFrom->classScope, allocation, parsedTypes); } // Warn if type is a class that contains any virtual functions for (const Function &func : type->functionList) { if (func.hasVirtualSpecifier()) { if (allocation) mallocOnClassError(tok, tok->str(), type->classDef, "virtual function"); else memsetError(tok, tok->str(), "virtual function", type->classDef->str()); } } // Warn if type is a class or struct that contains any std::* variables for (const Variable &var : type->varlist) { if (var.isReference() && !var.isStatic()) { memsetErrorReference(tok, tok->str(), type->classDef->str()); continue; } // don't warn if variable static or const, pointer or array of pointers if (!var.isStatic() && !var.isConst() && !var.isPointer() && (!var.isArray() || var.typeEndToken()->str() != "*")) { const Token *tok1 = var.typeStartToken(); const Scope *typeScope = var.typeScope(); std::string typeName; if (Token::Match(tok1, "%type% ::")) { const Token *typeTok = tok1; while (Token::Match(typeTok, "%type% ::")) { typeName += typeTok->str() + "::"; typeTok = typeTok->tokAt(2); } typeName += typeTok->str(); } // check for std:: type if (var.isStlType() && typeName != "std::array" && !mSettings->library.podtype(typeName)) { if (allocation) mallocOnClassError(tok, tok->str(), type->classDef, "'" + typeName + "'"); else memsetError(tok, tok->str(), "'" + typeName + "'", type->classDef->str()); } // check for known type else if (typeScope && typeScope != type) checkMemsetType(start, tok, typeScope, allocation, parsedTypes); // check for float else if (printPortability && var.isFloatingType() && tok->str() == "memset") memsetErrorFloat(tok, type->classDef->str()); } } } void CheckClass::mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok) { std::list toks = { tok, classTok }; reportError(toks, Severity::warning, "mallocOnClassWarning", "$symbol:" + memfunc +"\n" "Memory for class instance allocated with $symbol(), but class provides constructors.\n" "Memory for class instance allocated with $symbol(), but class provides constructors. This is unsafe, " "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE762, false); } void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname) { std::list toks = { tok, classTok }; reportError(toks, Severity::error, "mallocOnClassError", "$symbol:" + memfunc +"\n" "$symbol:" + classname +"\n" "Memory for class instance allocated with " + memfunc + "(), but class contains a " + classname + ".\n" "Memory for class instance allocated with " + memfunc + "(), but class a " + classname + ". This is unsafe, " "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE665, false); } void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type) { reportError(tok, Severity::error, "memsetClass", "$symbol:" + memfunc +"\n" "$symbol:" + classname +"\n" "Using '" + memfunc + "' on " + type + " that contains a " + classname + ".\n" "Using '" + memfunc + "' on " + type + " that contains a " + classname + " is unsafe, because constructor, destructor " "and copy operator calls are omitted. These are necessary for this non-POD type to ensure that a valid object " "is created.", CWE762, false); } void CheckClass::memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type) { reportError(tok, Severity::error, "memsetClassReference", "$symbol:" + memfunc +"\n" "Using '" + memfunc + "' on " + type + " that contains a reference.", CWE665, false); } void CheckClass::memsetErrorFloat(const Token *tok, const std::string &type) { reportError(tok, Severity::portability, "memsetClassFloat", "Using memset() on " + type + " which contains a floating point number.\n" "Using memset() on " + type + " which contains a floating point number." " This is not portable because memset() sets each byte of a block of memory to a specific value and" " the actual representation of a floating-point value is implementation defined." " Note: In case of an IEEE754-1985 compatible implementation setting all bits to zero results in the value 0.0.", CWE758, false); } //--------------------------------------------------------------------------- // ClassCheck: "void operator=(" and "const type & operator=(" //--------------------------------------------------------------------------- void CheckClass::operatorEq() { if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eOperatorEqual && func->access == AccessControl::Public) { // skip "deleted" functions - cannot be called anyway if (func->isDelete()) continue; // use definition for check so we don't have to deal with qualification bool returnSelfRef = false; if (func->retDef->str() == scope->className) { if (Token::Match(func->retDef, "%type% &")) { returnSelfRef = true; } else { // We might have "Self&"" const Token * const tok = func->retDef->next(); if (tok && tok->str() == "<" && tok->link() && tok->link()->next() && tok->link()->next()->str() == "&") returnSelfRef = true; } } if (!returnSelfRef) { // make sure we really have a copy assignment operator const Token *paramTok = func->tokenDef->tokAt(2); if (Token::Match(paramTok, "const| %name% &")) { if (paramTok->str() == "const" && paramTok->strAt(1) == scope->className) operatorEqReturnError(func->retDef, scope->className); else if (paramTok->str() == scope->className) operatorEqReturnError(func->retDef, scope->className); } } } } } } void CheckClass::operatorEqReturnError(const Token *tok, const std::string &className) { reportError(tok, Severity::style, "operatorEq", "$symbol:" + className +"\n" "'$symbol::operator=' should return '$symbol &'.\n" "The $symbol::operator= does not conform to standard C/C++ behaviour. To conform to standard C/C++ behaviour, return a reference to self (such as: '$symbol &$symbol::operator=(..) { .. return *this; }'. For safety reasons it might be better to not fix this message. If you think that safety is always more important than conformance then please ignore/suppress this message. For more details about this topic, see the book \"Effective C++\" by Scott Meyers." , CWE398, false); } //--------------------------------------------------------------------------- // ClassCheck: "C& operator=(const C&) { ... return *this; }" // operator= should return a reference to *this //--------------------------------------------------------------------------- void CheckClass::operatorEqRetRefThis() { if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eOperatorEqual && func->hasBody()) { // make sure return signature is correct if (func->retType == func->nestedIn->definedType && func->tokenDef->strAt(-1) == "&") { checkReturnPtrThis(scope, &(*func), func->functionScope->bodyStart, func->functionScope->bodyEnd); } } } } } void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last) { std::set analyzedFunctions; checkReturnPtrThis(scope, func, tok, last, analyzedFunctions); } void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions) { bool foundReturn = false; const Token* const startTok = tok; for (; tok && tok != last; tok = tok->next()) { // check for return of reference to this if (tok->str() != "return") continue; foundReturn = true; const Token *retExpr = tok->astOperand1(); if (retExpr && retExpr->str() == "=") retExpr = retExpr->astOperand1(); if (retExpr && retExpr->isUnaryOp("*") && Token::simpleMatch(retExpr->astOperand1(), "this")) continue; std::string cast("( " + scope->className + " & )"); if (Token::simpleMatch(tok->next(), cast.c_str())) tok = tok->tokAt(4); // check if a function is called if (tok->strAt(2) == "(" && tok->linkAt(2)->next()->str() == ";") { // check if it is a member function for (std::list::const_iterator it = scope->functionList.begin(); it != scope->functionList.end(); ++it) { // check for a regular function with the same name and a body if (it->type == Function::eFunction && it->hasBody() && it->token->str() == tok->next()->str()) { // check for the proper return type if (it->tokenDef->previous()->str() == "&" && it->tokenDef->strAt(-2) == scope->className) { // make sure it's not a const function if (!it->isConst()) { /** @todo make sure argument types match */ // avoid endless recursions if (analyzedFunctions.find(&*it) == analyzedFunctions.end()) { analyzedFunctions.insert(&*it); checkReturnPtrThis(scope, &*it, it->arg->link()->next(), it->arg->link()->next()->link(), analyzedFunctions); } // just bail for now else return; } } } } } // check if *this is returned else if (!(Token::simpleMatch(tok->next(), "operator= (") || Token::simpleMatch(tok->next(), "this . operator= (") || (Token::Match(tok->next(), "%type% :: operator= (") && tok->next()->str() == scope->className))) operatorEqRetRefThisError(func->token); } if (foundReturn) { return; } if (startTok->next() == last) { if (Token::simpleMatch(func->argDef, std::string("( const " + scope->className + " &").c_str())) { // Typical wrong way to suppress default assignment operator by declaring it and leaving empty operatorEqMissingReturnStatementError(func->token, func->access == AccessControl::Public); } else { operatorEqMissingReturnStatementError(func->token, true); } return; } if (mSettings->library.isScopeNoReturn(last, nullptr)) { // Typical wrong way to prohibit default assignment operator // by always throwing an exception or calling a noreturn function operatorEqShouldBeLeftUnimplementedError(func->token); return; } operatorEqMissingReturnStatementError(func->token, func->access == AccessControl::Public); } void CheckClass::operatorEqRetRefThisError(const Token *tok) { reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance.", CWE398, false); } void CheckClass::operatorEqShouldBeLeftUnimplementedError(const Token *tok) { reportError(tok, Severity::style, "operatorEqShouldBeLeftUnimplemented", "'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.", CWE398, false); } void CheckClass::operatorEqMissingReturnStatementError(const Token *tok, bool error) { if (error) { reportError(tok, Severity::error, "operatorEqMissingReturnStatement", "No 'return' statement in non-void function causes undefined behavior.", CWE398, false); } else { operatorEqRetRefThisError(tok); } } //--------------------------------------------------------------------------- // ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }" // operator= should check for assignment to self // // For simple classes, an assignment to self check is only a potential optimization. // // For classes that allocate dynamic memory, assignment to self can be a real error // if it is deallocated and allocated again without being checked for. // // This check is not valid for classes with multiple inheritance because a // class can have multiple addresses so there is no trivial way to check for // assignment to self. //--------------------------------------------------------------------------- void CheckClass::operatorEqToSelf() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // skip classes with multiple inheritance if (scope->definedType->derivedFrom.size() > 1) continue; for (const Function &func : scope->functionList) { if (func.type == Function::eOperatorEqual && func.hasBody()) { // make sure that the operator takes an object of the same type as *this, otherwise we can't detect self-assignment checks if (func.argumentList.empty()) continue; const Token* typeTok = func.argumentList.front().typeEndToken(); while (typeTok->str() == "const" || typeTok->str() == "&" || typeTok->str() == "*") typeTok = typeTok->previous(); if (typeTok->str() != scope->className) continue; // make sure return signature is correct if (Token::Match(func.retDef, "%type% &") && func.retDef->str() == scope->className) { // find the parameter name const Token *rhs = func.argumentList.begin()->nameToken(); if (!hasAssignSelf(&func, rhs)) { if (hasAllocation(&func, scope)) operatorEqToSelfError(func.token); } } } } } } bool CheckClass::hasAllocation(const Function *func, const Scope* scope) const { // This function is called when no simple check was found for assignment // to self. We are currently looking for: // - deallocate member ; ... member = // - alloc member // That is not ideal because it can cause false negatives but its currently // necessary to prevent false positives. const Token *last = func->functionScope->bodyEnd; for (const Token *tok = func->functionScope->bodyStart; tok && (tok != last); tok = tok->next()) { if (Token::Match(tok, "%var% = malloc|realloc|calloc|new") && isMemberVar(scope, tok)) return true; // check for deallocating memory const Token *var; if (Token::Match(tok, "free ( %var%")) var = tok->tokAt(2); else if (Token::Match(tok, "delete [ ] %var%")) var = tok->tokAt(3); else if (Token::Match(tok, "delete %var%")) var = tok->next(); else continue; // Check for assignment to the deleted pointer (only if its a member of the class) if (isMemberVar(scope, var)) { for (const Token *tok1 = var->next(); tok1 && (tok1 != last); tok1 = tok1->next()) { if (Token::Match(tok1, "%varid% =", var->varId())) return true; } } } return false; } bool CheckClass::hasAssignSelf(const Function *func, const Token *rhs) { if (!rhs) return false; const Token *last = func->functionScope->bodyEnd; for (const Token *tok = func->functionScope->bodyStart; tok && tok != last; tok = tok->next()) { if (!Token::simpleMatch(tok, "if (")) continue; bool ret = false; visitAstNodes(tok->next()->astOperand2(), [&](const Token *tok2) { if (!Token::Match(tok2, "==|!=")) return ChildrenToVisit::op1_and_op2; if (Token::simpleMatch(tok2->astOperand1(), "this")) tok2 = tok2->astOperand2(); else if (Token::simpleMatch(tok2->astOperand2(), "this")) tok2 = tok2->astOperand1(); else return ChildrenToVisit::op1_and_op2; if (tok2 && tok2->isUnaryOp("&") && tok2->astOperand1()->str() == rhs->str()) ret = true; return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (ret) return ret; } return false; } void CheckClass::operatorEqToSelfError(const Token *tok) { reportError(tok, Severity::warning, "operatorEqToSelf", "'operator=' should check for assignment to self to avoid problems with dynamic memory.\n" "'operator=' should check for assignment to self to ensure that each block of dynamically " "allocated memory is owned and managed by only one instance of the class.", CWE398, false); } //--------------------------------------------------------------------------- // A destructor in a base class should be virtual //--------------------------------------------------------------------------- void CheckClass::virtualDestructor() { // This error should only be given if: // * base class doesn't have virtual destructor // * derived class has non-empty destructor (only c++03, in c++11 it's UB see paragraph 3 in [expr.delete]) // * base class is deleted // unless inconclusive in which case: // * base class has virtual members but doesn't have virtual destructor const bool printInconclusive = mSettings->inconclusive; std::list inconclusiveErrors; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // Skip base classes (unless inconclusive) if (scope->definedType->derivedFrom.empty()) { if (printInconclusive) { const Function *destructor = scope->getDestructor(); if (destructor && !destructor->hasVirtualSpecifier()) { for (const Function &func : scope->functionList) { if (func.hasVirtualSpecifier()) { inconclusiveErrors.push_back(destructor); break; } } } } continue; } // Check if destructor is empty and non-empty .. if (mSettings->standards.cpp <= Standards::CPP03) { // Find the destructor const Function *destructor = scope->getDestructor(); // Check for destructor with implementation if (!destructor || !destructor->hasBody()) continue; // Empty destructor if (destructor->token->linkAt(3) == destructor->token->tokAt(4)) continue; } const Token *derived = scope->classDef; const Token *derivedClass = derived->next(); // Iterate through each base class... for (const Type::BaseInfo & j : scope->definedType->derivedFrom) { // Check if base class is public and exists in database if (j.access != AccessControl::Private && j.type) { const Type *derivedFrom = j.type; const Scope *derivedFromScope = derivedFrom->classScope; if (!derivedFromScope) continue; // Check for this pattern: // 1. Base class pointer is given the address of derived class instance // 2. Base class pointer is deleted // // If this pattern is not seen then bailout the checking of these base/derived classes { // pointer variables of type 'Base *' std::set baseClassPointers; for (const Variable* var : mSymbolDatabase->variableList()) { if (var && var->isPointer() && var->type() == derivedFrom) baseClassPointers.insert(var->declarationId()); } // pointer variables of type 'Base *' that should not be deleted std::set dontDelete; // No deletion of derived class instance through base class pointer found => the code is ok bool ok = true; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %var% =") && baseClassPointers.find(tok->next()->varId()) != baseClassPointers.end()) { // new derived class.. if (Token::simpleMatch(tok->tokAt(3), ("new " + derivedClass->str()).c_str())) { dontDelete.insert(tok->next()->varId()); } } // Delete base class pointer that might point at derived class else if (Token::Match(tok, "delete %var% ;") && dontDelete.find(tok->next()->varId()) != dontDelete.end()) { ok = false; break; } } // No base class pointer that points at a derived class is deleted if (ok) continue; } // Find the destructor declaration for the base class. const Function *baseDestructor = derivedFromScope->getDestructor(); // Check that there is a destructor.. if (!baseDestructor) { if (derivedFrom->derivedFrom.empty()) { virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str(), false); } } else if (!baseDestructor->hasVirtualSpecifier()) { // TODO: This is just a temporary fix, better solution is needed. // Skip situations where base class has base classes of its own, because // some of the base classes might have virtual destructor. // Proper solution is to check all of the base classes. If base class is not // found or if one of the base classes has virtual destructor, error should not // be printed. See TODO test case "virtualDestructorInherited" if (derivedFrom->derivedFrom.empty()) { // Make sure that the destructor is public (protected or private // would not compile if inheritance is used in a way that would // cause the bug we are trying to find here.) if (baseDestructor->access == AccessControl::Public) { virtualDestructorError(baseDestructor->token, derivedFrom->name(), derivedClass->str(), false); // check for duplicate error and remove it if found const std::list::iterator found = find(inconclusiveErrors.begin(), inconclusiveErrors.end(), baseDestructor); if (found != inconclusiveErrors.end()) inconclusiveErrors.erase(found); } } } } } } for (const Function *func : inconclusiveErrors) virtualDestructorError(func->tokenDef, func->name(), emptyString, true); } void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive) { if (inconclusive) { if (mSettings->isEnabled(Settings::WARNING)) reportError(tok, Severity::warning, "virtualDestructor", "$symbol:" + Base + "\nClass '$symbol' which has virtual members does not have a virtual destructor.", CWE404, true); } else { reportError(tok, Severity::error, "virtualDestructor", "$symbol:" + Base +"\n" "$symbol:" + Derived +"\n" "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n" "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. " "If you destroy instances of the derived class by deleting a pointer that points to the base class, only " "the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class " "could leak. This can be avoided by adding a virtual destructor to the base class.", CWE404, false); } } //--------------------------------------------------------------------------- // warn for "this-x". The indented code may be "this->x" //--------------------------------------------------------------------------- void CheckClass::thisSubtraction() { if (!mSettings->isEnabled(Settings::WARNING)) return; const Token *tok = mTokenizer->tokens(); for (;;) { tok = Token::findmatch(tok, "this - %name%"); if (!tok) break; if (tok->strAt(-1) != "*") thisSubtractionError(tok); tok = tok->next(); } } void CheckClass::thisSubtractionError(const Token *tok) { reportError(tok, Severity::warning, "thisSubtraction", "Suspicious pointer subtraction. Did you intend to write '->'?", CWE398, false); } //--------------------------------------------------------------------------- // can member function be const? //--------------------------------------------------------------------------- void CheckClass::checkConst() { // This is an inconclusive check. False positives: #3322. if (!mSettings->inconclusive) return; if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { for (const Function &func : scope->functionList) { // does the function have a body? if (func.type != Function::eFunction || !func.hasBody()) continue; // don't warn for friend/static/virtual functions if (func.isFriend() || func.isStatic() || func.hasVirtualSpecifier()) continue; // get last token of return type const Token *previous = func.tokenDef->previous(); // does the function return a pointer or reference? if (Token::Match(previous, "*|&")) { if (func.retDef->str() != "const") continue; } else if (Token::Match(previous->previous(), "*|& >")) { const Token *temp = previous->previous(); bool foundConst = false; while (!Token::Match(temp->previous(), ";|}|{|public:|protected:|private:")) { temp = temp->previous(); if (temp->str() == "const") { foundConst = true; break; } } if (!foundConst) continue; } else if (func.isOperator() && Token::Match(previous, ";|{|}|public:|private:|protected:")) { // Operator without return type: conversion operator const std::string& opName = func.tokenDef->str(); if (opName.compare(8, 5, "const") != 0 && (endsWith(opName,'&') || endsWith(opName,'*'))) continue; } else if (mSettings->library.isSmartPointer(func.retDef)) { // Don't warn if a std::shared_ptr etc is returned continue; } else { // don't warn for unknown types.. // LPVOID, HDC, etc if (previous->str().size() > 2 && !previous->type() && previous->isUpperCaseName()) continue; } // check if base class function is virtual if (!scope->definedType->derivedFrom.empty() && func.isImplicitlyVirtual(true)) continue; bool memberAccessed = false; // if nothing non-const was found. write error.. if (!checkConstFunc(scope, &func, memberAccessed)) continue; if (func.isConst() && (memberAccessed || func.isOperator())) continue; std::string classname = scope->className; const Scope *nest = scope->nestedIn; while (nest && nest->type != Scope::eGlobal) { classname = std::string(nest->className + "::" + classname); nest = nest->nestedIn; } // get function name std::string functionName = (func.tokenDef->isName() ? "" : "operator") + func.tokenDef->str(); if (func.tokenDef->str() == "(") functionName += ")"; else if (func.tokenDef->str() == "[") functionName += "]"; if (func.isInline()) checkConstError(func.token, classname, functionName, !memberAccessed && !func.isOperator()); else // not inline checkConstError2(func.token, func.tokenDef, classname, functionName, !memberAccessed && !func.isOperator()); } } } bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) const { bool again = false; // try to find the member variable do { again = false; if (tok->str() == "this") { return true; } else if (Token::simpleMatch(tok->tokAt(-3), "( * this )")) { return true; } else if (Token::Match(tok->tokAt(-2), "%name% . %name%")) { tok = tok->tokAt(-2); again = true; } else if (Token::Match(tok->tokAt(-2), "] . %name%")) { tok = tok->linkAt(-2)->previous(); again = true; } else if (tok->str() == "]") { tok = tok->link()->previous(); again = true; } } while (again); for (const Variable &var : scope->varlist) { if (var.name() == tok->str()) { if (tok->varId() == 0) mSymbolDatabase->debugMessage(tok, "CheckClass::isMemberVar found used member variable \'" + tok->str() + "\' with varid 0"); return !var.isStatic(); } } // not found in this class if (!scope->definedType->derivedFrom.empty()) { // check each base class for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { // find the base class const Type *derivedFrom = i.type; // find the function in the base class if (derivedFrom && derivedFrom->classScope) { if (isMemberVar(derivedFrom->classScope, tok)) return true; } } } return false; } bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok) const { if (!tok->function()) { for (const Function &func : scope->functionList) { if (func.name() == tok->str()) { const Token* tok2 = tok->tokAt(2); int argsPassed = tok2->str() == ")" ? 0 : 1; for (;;) { tok2 = tok2->nextArgument(); if (tok2) argsPassed++; else break; } if (argsPassed == func.argCount() || (argsPassed < func.argCount() && argsPassed >= func.minArgCount())) return true; } } } else if (tok->function()->nestedIn == scope) return !tok->function()->isStatic(); // not found in this class if (!scope->definedType->derivedFrom.empty()) { // check each base class for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { // find the base class const Type *derivedFrom = i.type; // find the function in the base class if (derivedFrom && derivedFrom->classScope) { if (isMemberFunc(derivedFrom->classScope, tok)) return true; } } } return false; } bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok) const { if (!tok->function()) return false; else if (tok->function()->nestedIn == scope) return tok->function()->isConst(); // not found in this class if (!scope->definedType->derivedFrom.empty()) { // check each base class for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { // find the base class const Type *derivedFrom = i.type; // find the function in the base class if (derivedFrom && derivedFrom->classScope) { if (isConstMemberFunc(derivedFrom->classScope, tok)) return true; } } } return false; } namespace { // The container contains the STL types whose operator[] is not a const. const std::set stl_containers_not_const = { "map", "unordered_map" }; } bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool& memberAccessed) const { // if the function doesn't have any assignment nor function call, // it can be a const function.. for (const Token *tok1 = func->functionScope->bodyStart; tok1 && tok1 != func->functionScope->bodyEnd; tok1 = tok1->next()) { if (tok1->isName() && isMemberVar(scope, tok1)) { memberAccessed = true; const Variable* v = tok1->variable(); if (v && v->isMutable()) continue; if (tok1->str() == "this" && tok1->previous()->isAssignmentOp()) return false; const Token* lhs = tok1->previous(); if (lhs->str() == "&") { lhs = lhs->previous(); if (lhs->isAssignmentOp() && lhs->previous()->variable()) { if (lhs->previous()->variable()->typeStartToken()->strAt(-1) != "const" && lhs->previous()->variable()->isPointer()) return false; } } else if (lhs->str() == ":" && lhs->astParent() && lhs->astParent()->str() == "(" && tok1->strAt(1) == ")") { // range-based for-loop (C++11) // TODO: We could additionally check what is done with the elements to avoid false negatives. Here we just rely on "const" keyword being used. if (lhs->astParent()->strAt(1) != "const") return false; } else { if (lhs->isAssignmentOp()) { const Variable* lhsVar = lhs->previous()->variable(); if (lhsVar && !lhsVar->isConst() && lhsVar->isReference() && lhs == lhsVar->nameToken()->next()) return false; } } const Token* jumpBackToken = nullptr; const Token *lastVarTok = tok1; const Token *end = tok1; for (;;) { if (Token::Match(end->next(), ". %name%")) { end = end->tokAt(2); if (end->varId()) lastVarTok = end; } else if (end->strAt(1) == "[") { if (end->varId()) { const Variable *var = end->variable(); if (var && var->isStlType(stl_containers_not_const)) return false; } if (!jumpBackToken) jumpBackToken = end->next(); // Check inside the [] brackets end = end->linkAt(1); } else if (end->strAt(1) == ")") end = end->next(); else break; } if (end->strAt(1) == "(") { const Variable *var = lastVarTok->variable(); if (!var) return false; if (var->isStlType() // assume all std::*::size() and std::*::empty() are const && (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy"))) ; else if (!var->typeScope() || !isConstMemberFunc(var->typeScope(), end)) return false; } // Assignment else if (end->next()->isAssignmentOp()) return false; // Streaming else if (end->strAt(1) == "<<" && tok1->strAt(-1) != "<<") return false; else if (isLikelyStreamRead(true, tok1->previous())) return false; // ++/-- else if (end->next()->tokType() == Token::eIncDecOp || tok1->previous()->tokType() == Token::eIncDecOp) return false; const Token* start = tok1; while (tok1->strAt(-1) == ")") tok1 = tok1->linkAt(-1); if (start->strAt(-1) == "delete") return false; tok1 = jumpBackToken?jumpBackToken:end; // Jump back to first [ to check inside, or jump to end of expression } // streaming: << else if (Token::simpleMatch(tok1->previous(), ") <<") && isMemberVar(scope, tok1->tokAt(-2))) { const Variable* var = tok1->tokAt(-2)->variable(); if (!var || !var->isMutable()) return false; } // streaming: >> *this else if (Token::simpleMatch(tok1, ">> * this") && isLikelyStreamRead(true, tok1)) { return false; } // function call.. else if (Token::Match(tok1, "%name% (") && !tok1->isStandardType() && !Token::Match(tok1, "return|if|string|switch|while|catch|for")) { if (isMemberFunc(scope, tok1) && tok1->strAt(-1) != ".") { if (!isConstMemberFunc(scope, tok1)) return false; memberAccessed = true; } // Member variable given as parameter const Token *lpar = tok1->next(); if (Token::simpleMatch(lpar, "( ) (")) lpar = lpar->tokAt(2); for (const Token* tok2 = lpar->next(); tok2 && tok2 != tok1->next()->link(); tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->isName() && isMemberVar(scope, tok2)) { const Variable* var = tok2->variable(); if (!var || !var->isMutable()) return false; // TODO: Only bailout if function takes argument as non-const reference } } } else if (Token::simpleMatch(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) { return false; } } return true; } void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic) { checkConstError2(tok, nullptr, classname, funcname, suggestStatic); } void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic) { std::list toks; toks.push_back(tok1); if (tok2) toks.push_back(tok2); if (!suggestStatic) reportError(toks, Severity::style, "functionConst", "$symbol:" + classname + "::" + funcname +"\n" "Technically the member function '$symbol' can be const.\n" "The member function '$symbol' can be made a const " "function. Making this function 'const' should not cause compiler errors. " "Even though the function can be made const function technically it may not make " "sense conceptually. Think about your design and the task of the function first - is " "it a function that must not change object internal state?", CWE398, true); else reportError(toks, Severity::performance, "functionStatic", "$symbol:" + classname + "::" + funcname +"\n" "Technically the member function '$symbol' can be static (but you may consider moving to unnamed namespace).\n" "The member function '$symbol' can be made a static " "function. Making a function static can bring a performance benefit since no 'this' instance is " "passed to the function. This change should not cause compiler errors but it does not " "necessarily make sense conceptually. Think about your design and the task of the function first - " "is it a function that must not access members of class instances? And maybe it is more appropriate " "to move this function to a unnamed namespace.", CWE398, true); } //--------------------------------------------------------------------------- // ClassCheck: Check that initializer list is in declared order. //--------------------------------------------------------------------------- namespace { // avoid one-definition-rule violation struct VarInfo { VarInfo(const Variable *_var, const Token *_tok) : var(_var), tok(_tok) { } const Variable *var; const Token *tok; }; } void CheckClass::initializerListOrder() { if (!mSettings->isEnabled(Settings::STYLE)) return; // This check is not inconclusive. However it only determines if the initialization // order is incorrect. It does not determine if being out of order causes // a real error. Out of order is not necessarily an error but you can never // have an error if the list is in order so this enforces defensive programming. if (!mSettings->inconclusive) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { // iterate through all member functions looking for constructors for (std::list::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->isConstructor() && func->hasBody()) { // check for initializer list const Token *tok = func->arg->link()->next(); if (tok->str() == ":") { std::vector vars; tok = tok->next(); // find all variable initializations in list while (tok && tok != func->functionScope->bodyStart) { if (Token::Match(tok, "%name% (|{")) { const Variable *var = scope->getVariable(tok->str()); if (var) vars.emplace_back(var, tok); if (Token::Match(tok->tokAt(2), "%name% =")) { var = scope->getVariable(tok->strAt(2)); if (var) vars.emplace_back(var, tok->tokAt(2)); } tok = tok->next()->link()->next(); } else tok = tok->next(); } // need at least 2 members to have out of order initialization for (int j = 1; j < vars.size(); j++) { // check for out of order initialization if (vars[j].var->index() < vars[j - 1].var->index()) initializerListError(vars[j].tok,vars[j].var->nameToken(), scope->className, vars[j].var->name()); } } } } } } void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname) { std::list toks = { tok1, tok2 }; reportError(toks, Severity::style, "initializerList", "$symbol:" + classname + "::" + varname +"\n" "Member variable '$symbol' is in the wrong place in the initializer list.\n" "Member variable '$symbol' is in the wrong place in the initializer list. " "Members are initialized in the order they are declared, not in the " "order they are in the initializer list. Keeping the initializer list " "in the same order that the members were declared prevents order dependent " "initialization errors.", CWE398, true); } //--------------------------------------------------------------------------- // Check for self initialization in initialization list //--------------------------------------------------------------------------- void CheckClass::checkSelfInitialization() { for (const Scope *scope : mSymbolDatabase->functionScopes) { const Function* function = scope->function; if (!function || !function->isConstructor()) continue; const Token* tok = function->arg->link()->next(); if (tok->str() != ":") continue; for (; tok != scope->bodyStart; tok = tok->next()) { if (Token::Match(tok, "[:,] %var% (|{ %var% )|}") && tok->next()->varId() == tok->tokAt(3)->varId()) { selfInitializationError(tok, tok->strAt(1)); } } } } void CheckClass::selfInitializationError(const Token* tok, const std::string& varname) { reportError(tok, Severity::error, "selfInitialization", "$symbol:" + varname + "\nMember variable '$symbol' is initialized by itself.", CWE665, false); } //--------------------------------------------------------------------------- // Check for virtual function calls in constructor/destructor //--------------------------------------------------------------------------- void CheckClass::checkVirtualFunctionCallInConstructor() { if (! mSettings->isEnabled(Settings::WARNING)) return; std::map > virtualFunctionCallsMap; for (const Scope *scope : mSymbolDatabase->functionScopes) { if (scope->function == nullptr || !scope->function->hasBody() || !(scope->function->isConstructor() || scope->function->isDestructor())) continue; const std::list & virtualFunctionCalls = getVirtualFunctionCalls(*scope->function, virtualFunctionCallsMap); for (const Token *callToken : virtualFunctionCalls) { std::list callstack(1, callToken); getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack); if (callstack.empty()) continue; if (callstack.back()->function()->isPure()) pureVirtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str()); else virtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str()); } } } const std::list & CheckClass::getVirtualFunctionCalls(const Function & function, std::map > & virtualFunctionCallsMap) { const std::map >::const_iterator found = virtualFunctionCallsMap.find(&function); if (found != virtualFunctionCallsMap.end()) return found->second; virtualFunctionCallsMap[&function] = std::list(); std::list & virtualFunctionCalls = virtualFunctionCallsMap.find(&function)->second; if (!function.hasBody()) return virtualFunctionCalls; for (const Token *tok = function.arg->link(); tok != function.functionScope->bodyEnd; tok = tok->next()) { if (function.type != Function::eConstructor && function.type != Function::eCopyConstructor && function.type != Function::eMoveConstructor && function.type != Function::eDestructor) { if ((Token::simpleMatch(tok, ") {") && tok->link() && Token::Match(tok->link()->previous(), "if|switch")) || Token::simpleMatch(tok, "else {")) { // Assume pure virtual function call is prevented by "if|else|switch" condition tok = tok->linkAt(1); continue; } } if (tok->scope()->type == Scope::eLambda) tok = tok->scope()->bodyEnd->next(); const Function * callFunction = tok->function(); if (!callFunction || function.nestedIn != callFunction->nestedIn || (tok->previous() && tok->previous()->str() == ".")) continue; if (tok->previous() && tok->previous()->str() == "(") { const Token * prev = tok->previous(); if (prev->previous() && (mSettings->library.ignorefunction(tok->str()) || mSettings->library.ignorefunction(prev->previous()->str()))) continue; } if (callFunction->isImplicitlyVirtual()) { if (!callFunction->isPure() && Token::simpleMatch(tok->previous(), "::")) continue; virtualFunctionCalls.push_back(tok); continue; } const std::list & virtualFunctionCallsOfTok = getVirtualFunctionCalls(*callFunction, virtualFunctionCallsMap); if (!virtualFunctionCallsOfTok.empty()) virtualFunctionCalls.push_back(tok); } return virtualFunctionCalls; } void CheckClass::getFirstVirtualFunctionCallStack( std::map > & virtualFunctionCallsMap, const Token * callToken, std::list & pureFuncStack) { const Function *callFunction = callToken->function(); if (callFunction->isImplicitlyVirtual() && (!callFunction->isPure() || !callFunction->hasBody())) { pureFuncStack.push_back(callFunction->tokenDef); return; } std::map >::const_iterator found = virtualFunctionCallsMap.find(callFunction); if (found == virtualFunctionCallsMap.end() || found->second.empty()) { pureFuncStack.clear(); return; } const Token * firstCall = *found->second.begin(); pureFuncStack.push_back(firstCall); getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, firstCall, pureFuncStack); } void CheckClass::virtualFunctionCallInConstructorError( const Function * scopeFunction, const std::list & tokStack, const std::string &funcname) { const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor"; ErrorPath errorPath; int lineNumber = 1; for (const Token *tok : tokStack) errorPath.emplace_back(tok, "Calling " + tok->str()); if (!errorPath.empty()) { lineNumber = errorPath.front().first->linenr(); errorPath.back().second = funcname + " is a virtual function"; } std::string constructorName; if (scopeFunction) { const Token *endToken = scopeFunction->argDef->link()->next(); if (scopeFunction->type == Function::Type::eDestructor) constructorName = "~"; for (const Token *tok = scopeFunction->tokenDef; tok != endToken; tok = tok->next()) { if (!constructorName.empty() && Token::Match(tok->previous(), "%name%|%num% %name%|%num%")) constructorName += ' '; constructorName += tok->str(); if (tok->str() == ")") break; } } reportError(errorPath, Severity::warning, "virtualCallInConstructor", "Virtual function '" + funcname + "' is called from " + scopeFunctionTypeName + " '" + constructorName + "' at line " + MathLib::toString(lineNumber) + ". Dynamic binding is not used.", CWE(0U), false); } void CheckClass::pureVirtualFunctionCallInConstructorError( const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname) { const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor"; ErrorPath errorPath; for (const Token *tok : tokStack) errorPath.emplace_back(tok, "Calling " + tok->str()); if (!errorPath.empty()) errorPath.back().second = purefuncname + " is a pure virtual function without body"; reportError(errorPath, Severity::warning, "pureVirtualCall", "$symbol:" + purefuncname +"\n" "Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ".\n" "Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ". The call will fail during runtime.", CWE(0U), false); } //--------------------------------------------------------------------------- // Check for members hiding inherited members with the same name //--------------------------------------------------------------------------- void CheckClass::checkDuplInheritedMembers() { if (!mSettings->isEnabled(Settings::WARNING)) return; // Iterate over all classes for (const Type &classIt : mSymbolDatabase->typeList) { // Iterate over the parent classes for (const Type::BaseInfo &parentClassIt : classIt.derivedFrom) { // Check if there is info about the 'Base' class if (!parentClassIt.type || !parentClassIt.type->classScope) continue; // Check if they have a member variable in common for (const Variable &classVarIt : classIt.classScope->varlist) { for (const Variable &parentClassVarIt : parentClassIt.type->classScope->varlist) { if (classVarIt.name() == parentClassVarIt.name() && !parentClassVarIt.isPrivate()) { // Check if the class and its parent have a common variable duplInheritedMembersError(classVarIt.nameToken(), parentClassVarIt.nameToken(), classIt.name(), parentClassIt.type->name(), classVarIt.name(), classIt.classScope->type == Scope::eStruct, parentClassIt.type->classScope->type == Scope::eStruct); } } } } } } void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &variableName, bool derivedIsStruct, bool baseIsStruct) { ErrorPath errorPath; errorPath.emplace_back(tok2, "Parent variable '" + baseName + "::" + variableName + "'"); errorPath.emplace_back(tok1, "Derived variable '" + derivedName + "::" + variableName + "'"); const std::string symbols = "$symbol:" + derivedName + "\n$symbol:" + variableName + "\n$symbol:" + baseName; const std::string message = "The " + std::string(derivedIsStruct ? "struct" : "class") + " '" + derivedName + "' defines member variable with name '" + variableName + "' also defined in its parent " + std::string(baseIsStruct ? "struct" : "class") + " '" + baseName + "'."; reportError(errorPath, Severity::warning, "duplInheritedMember", symbols + '\n' + message, CWE398, false); } //--------------------------------------------------------------------------- // Check that copy constructor and operator defined together //--------------------------------------------------------------------------- enum class CtorType { NO, WITHOUT_BODY, WITH_BODY }; void CheckClass::checkCopyCtorAndEqOperator() { // This is disabled because of #8388 // The message must be clarified. How is the behaviour different? return; if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { bool hasNonStaticVars = false; for (std::list::const_iterator var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { if (!var->isStatic()) { hasNonStaticVars = true; break; } } if (!hasNonStaticVars) continue; CtorType copyCtors = CtorType::NO; bool moveCtor = false; CtorType assignmentOperators = CtorType::NO; for (const Function &func : scope->functionList) { if (copyCtors == CtorType::NO && func.type == Function::eCopyConstructor) { copyCtors = func.hasBody() ? CtorType::WITH_BODY : CtorType::WITHOUT_BODY; } if (assignmentOperators == CtorType::NO && func.type == Function::eOperatorEqual) { const Variable * variable = func.getArgumentVar(0); if (variable && variable->type() && variable->type()->classScope == scope) { assignmentOperators = func.hasBody() ? CtorType::WITH_BODY : CtorType::WITHOUT_BODY; } } if (func.type == Function::eMoveConstructor) { moveCtor = true; break; } } if (moveCtor) continue; // No method defined if (copyCtors != CtorType::WITH_BODY && assignmentOperators != CtorType::WITH_BODY) continue; // both methods are defined if (copyCtors != CtorType::NO && assignmentOperators != CtorType::NO) continue; copyCtorAndEqOperatorError(scope->classDef, scope->className, scope->type == Scope::eStruct, copyCtors == CtorType::WITH_BODY); } } void CheckClass::copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor) { const std::string message = "$symbol:" + classname + "\n" "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' has '" + getFunctionTypeName(hasCopyCtor ? Function::eCopyConstructor : Function::eOperatorEqual) + "' but lack of '" + getFunctionTypeName(hasCopyCtor ? Function::eOperatorEqual : Function::eCopyConstructor) + "'."; reportError(tok, Severity::warning, "copyCtorAndEqOperator", message); } void CheckClass::checkOverride() { if (!mSettings->isEnabled(Settings::STYLE)) return; if (mSettings->standards.cpp < Standards::CPP11) return; for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) { if (!classScope->definedType || classScope->definedType->derivedFrom.empty()) continue; for (const Function &func : classScope->functionList) { if (func.hasOverrideSpecifier() || func.hasFinalSpecifier()) continue; const Function *baseFunc = func.getOverriddenFunction(); if (baseFunc) overrideError(baseFunc, &func); } } } void CheckClass::overrideError(const Function *funcInBase, const Function *funcInDerived) { const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : ""; const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function"; ErrorPath errorPath; if (funcInBase && funcInDerived) { errorPath.push_back(ErrorPathItem(funcInBase->tokenDef, "Virtual " + funcType + " in base class")); errorPath.push_back(ErrorPathItem(funcInDerived->tokenDef, char(std::toupper(funcType[0])) + funcType.substr(1) + " in derived class")); } reportError(errorPath, Severity::style, "missingOverride", "$symbol:" + functionName + "\n" "The " + funcType + " '$symbol' overrides a " + funcType + " in a base class but is not marked with a 'override' specifier.", CWE(0U) /* Unknown CWE! */, false); } void CheckClass::checkUnsafeClassRefMember() { if (!mSettings->safeChecks.classes || !mSettings->isEnabled(Settings::WARNING)) return; for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) { for (const Function &func : classScope->functionList) { if (!func.hasBody() || !func.isConstructor()) continue; const Token *initList = func.constructorMemberInitialization(); while (Token::Match(initList, "[:,] %name% (")) { if (Token::Match(initList->tokAt(2), "( %var% )")) { const Variable * const memberVar = initList->next()->variable(); const Variable * const argVar = initList->tokAt(3)->variable(); if (memberVar && argVar && memberVar->isConst() && memberVar->isReference() && argVar->isArgument() && argVar->isConst() && argVar->isReference()) unsafeClassRefMemberError(initList->next(), classScope->className + "::" + memberVar->name()); } initList = initList->linkAt(2)->next(); } } } } void CheckClass::unsafeClassRefMemberError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "unsafeClassRefMember", "$symbol:" + varname + "\n" "Unsafe class: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues.\n" "Unsafe class checking: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues. If you pass a local variable or temporary value in this constructor argument, be extra careful. If the argument is always some global object that is never destroyed then this is safe usage. However it would be defensive to make the member '$symbol' a non-reference variable or a smart pointer.", CWE(0), false); } cppcheck-1.90/lib/checkclass.h000066400000000000000000000405421357737443600162750ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkclassH #define checkclassH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "tokenize.h" #include #include #include #include #include class ErrorLogger; class Function; class Scope; class Settings; class SymbolDatabase; class Token; /// @addtogroup Checks /// @{ /** @brief %Check classes. Uninitialized member variables, non-conforming operators, missing virtual destructor, etc */ class CPPCHECKLIB CheckClass : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckClass() : Check(myName()), mSymbolDatabase(nullptr) { } /** @brief This constructor is used when running checks. */ CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger); /** @brief Run checks on the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (tokenizer->isC()) return; CheckClass checkClass(tokenizer, settings, errorLogger); // can't be a simplified check .. the 'sizeof' is used. checkClass.checkMemset(); checkClass.constructors(); checkClass.operatorEq(); checkClass.privateFunctions(); checkClass.operatorEqRetRefThis(); checkClass.thisSubtraction(); checkClass.operatorEqToSelf(); checkClass.initializerListOrder(); checkClass.initializationListUsage(); checkClass.checkSelfInitialization(); checkClass.virtualDestructor(); checkClass.checkConst(); checkClass.copyconstructors(); checkClass.checkVirtualFunctionCallInConstructor(); checkClass.checkDuplInheritedMembers(); checkClass.checkExplicitConstructors(); checkClass.checkCopyCtorAndEqOperator(); checkClass.checkOverride(); checkClass.checkUnsafeClassRefMember(); } /** @brief %Check that all class constructors are ok */ void constructors(); /** @brief %Check that constructors with single parameter are explicit, * if they has to be.*/ void checkExplicitConstructors(); /** @brief %Check that all private functions are called */ void privateFunctions(); /** * @brief %Check that the memsets are valid. * The 'memset' function can do dangerous things if used wrong. If it * is used on STL containers for instance it will clear all its data * and then the STL container may leak memory or worse have an invalid state. * It can also overwrite the virtual table. * Important: The checking doesn't work on simplified tokens list. */ void checkMemset(); void checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes); /** @brief 'operator=' should return something and it should not be const. */ void operatorEq(); /** @brief 'operator=' should return reference to *this */ void operatorEqRetRefThis(); // Warning upon no "return *this;" /** @brief 'operator=' should check for assignment to self */ void operatorEqToSelf(); // Warning upon no check for assignment to self /** @brief The destructor in a base class should be virtual */ void virtualDestructor(); /** @brief warn for "this-x". The indented code may be "this->x" */ void thisSubtraction(); /** @brief can member function be const? */ void checkConst(); /** @brief Check initializer list order */ void initializerListOrder(); /** @brief Suggest using initialization list */ void initializationListUsage(); /** @brief Check for initialization of a member with itself */ void checkSelfInitialization(); void copyconstructors(); /** @brief call of virtual function in constructor/destructor */ void checkVirtualFunctionCallInConstructor(); /** @brief Check duplicated inherited members */ void checkDuplInheritedMembers(); /** @brief Check that copy constructor and operator defined together */ void checkCopyCtorAndEqOperator(); /** @brief Check that the override keyword is used when overriding virtual functions */ void checkOverride(); /** @brief Unsafe class check - const reference member */ void checkUnsafeClassRefMember(); private: const SymbolDatabase *mSymbolDatabase; // Reporting errors.. void noConstructorError(const Token *tok, const std::string &classname, bool isStruct); void noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct); //void copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& var_name); void copyConstructorShallowCopyError(const Token *tok, const std::string& varname); void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); void noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc); void uninitVarError(const Token *tok, bool isprivate, const std::string &classname, const std::string &varname, bool inconclusive); void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive); void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname); void memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type); void memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type); void memsetErrorFloat(const Token *tok, const std::string &type); void mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname); void mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok); void operatorEqReturnError(const Token *tok, const std::string &className); void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive); void thisSubtractionError(const Token *tok); void operatorEqRetRefThisError(const Token *tok); void operatorEqShouldBeLeftUnimplementedError(const Token *tok); void operatorEqMissingReturnStatementError(const Token *tok, bool error); void operatorEqToSelfError(const Token *tok); void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname); void suggestInitializationList(const Token *tok, const std::string& varname); void selfInitializationError(const Token* tok, const std::string& varname); void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); void virtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &funcname); void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &variableName, bool derivedIsStruct, bool baseIsStruct); void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor); void overrideError(const Function *funcInBase, const Function *funcInDerived); void unsafeClassRefMemberError(const Token *tok, const std::string &varname); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckClass c(nullptr, settings, errorLogger); c.noConstructorError(nullptr, "classname", false); c.noExplicitConstructorError(nullptr, "classname", false); //c.copyConstructorMallocError(nullptr, 0, "var"); c.copyConstructorShallowCopyError(nullptr, "var"); c.noCopyConstructorError(nullptr, false, nullptr, false); c.noOperatorEqError(nullptr, false, nullptr, false); c.noDestructorError(nullptr, false, nullptr); c.uninitVarError(nullptr, false, "classname", "varname", false); c.uninitVarError(nullptr, true, "classname", "varnamepriv", false); c.operatorEqVarError(nullptr, "classname", emptyString, false); c.unusedPrivateFunctionError(nullptr, "classname", "funcname"); c.memsetError(nullptr, "memfunc", "classname", "class"); c.memsetErrorReference(nullptr, "memfunc", "class"); c.memsetErrorFloat(nullptr, "class"); c.mallocOnClassWarning(nullptr, "malloc", nullptr); c.mallocOnClassError(nullptr, "malloc", nullptr, "std::string"); c.operatorEqReturnError(nullptr, "class"); c.virtualDestructorError(nullptr, "Base", "Derived", false); c.thisSubtractionError(nullptr); c.operatorEqRetRefThisError(nullptr); c.operatorEqMissingReturnStatementError(nullptr, true); c.operatorEqShouldBeLeftUnimplementedError(nullptr); c.operatorEqToSelfError(nullptr); c.checkConstError(nullptr, "class", "function", false); c.checkConstError(nullptr, "class", "function", true); c.initializerListError(nullptr, nullptr, "class", "variable"); c.suggestInitializationList(nullptr, "variable"); c.selfInitializationError(nullptr, "var"); c.duplInheritedMembersError(nullptr, nullptr, "class", "class", "variable", false, false); c.copyCtorAndEqOperatorError(nullptr, "class", false, false); c.pureVirtualFunctionCallInConstructorError(nullptr, std::list(), "f"); c.virtualFunctionCallInConstructorError(nullptr, std::list(), "f"); c.overrideError(nullptr, nullptr); c.unsafeClassRefMemberError(nullptr, "UnsafeClass::var"); } static std::string myName() { return "Class"; } std::string classInfo() const OVERRIDE { return "Check the code for each class.\n" "- Missing constructors and copy constructors\n" //"- Missing allocation of memory in copy constructor\n" "- Constructors which should be explicit\n" "- Are all variables initialized by the constructors?\n" "- Are all variables assigned by 'operator='?\n" "- Warn if memset, memcpy etc are used on a class\n" "- Warn if memory for classes is allocated with malloc()\n" "- If it's a base class, check that the destructor is virtual\n" "- Are there unused private functions?\n" "- 'operator=' should return reference to self\n" "- 'operator=' should check for assignment to self\n" "- Constness for member functions\n" "- Order of initializations\n" "- Suggest usage of initialization list\n" "- Initialization of a member with itself\n" "- Suspicious subtraction from 'this'\n" "- Call of pure virtual function in constructor/destructor\n" "- Duplicated inherited data members\n" // disabled for now "- If 'copy constructor' defined, 'operator=' also should be defined and vice versa\n" "- Check that arbitrary usage of public interface does not result in division by zero\n" "- Check that the 'override' keyword is used when overriding virtual functions\n"; } // operatorEqRetRefThis helper functions void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last); void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions); // operatorEqToSelf helper functions bool hasAllocation(const Function *func, const Scope* scope) const; static bool hasAssignSelf(const Function *func, const Token *rhs); // checkConst helper functions bool isMemberVar(const Scope *scope, const Token *tok) const; bool isMemberFunc(const Scope *scope, const Token *tok) const; bool isConstMemberFunc(const Scope *scope, const Token *tok) const; bool checkConstFunc(const Scope *scope, const Function *func, bool& memberAccessed) const; // constructors helper function /** @brief Information about a member variable. Used when checking for uninitialized variables */ struct Usage { Usage() : assign(false), init(false) { } /** @brief has this variable been assigned? */ bool assign; /** @brief has this variable been initialized? */ bool init; }; static bool isBaseClassFunc(const Token *tok, const Scope *scope); /** * @brief assign a variable in the varlist * @param varid id of variable to mark assigned * @param scope pointer to variable Scope * @param usage reference to usage vector */ static void assignVar(nonneg int varid, const Scope *scope, std::vector &usage); /** * @brief initialize a variable in the varlist * @param varid id of variable to mark initialized * @param scope pointer to variable Scope * @param usage reference to usage vector */ static void initVar(nonneg int varid, const Scope *scope, std::vector &usage); /** * @brief set all variables in list assigned * @param usage reference to usage vector */ static void assignAllVar(std::vector &usage); /** * @brief set all variables in list not assigned and not initialized * @param usage reference to usage vector */ static void clearAllVar(std::vector &usage); /** * @brief parse a scope for a constructor or member function and set the "init" flags in the provided varlist * @param func reference to the function that should be checked * @param callstack the function doesn't look into recursive function calls. * @param scope pointer to variable Scope * @param usage reference to usage vector */ void initializeVarList(const Function &func, std::list &callstack, const Scope *scope, std::vector &usage); /** * @brief gives a list of tokens where virtual functions are called directly or indirectly * @param function function to be checked * @param virtualFunctionCallsMap map of results for already checked functions * @return list of tokens where pure virtual functions are called */ const std::list & getVirtualFunctionCalls( const Function & function, std::map > & virtualFunctionCallsMap); /** * @brief looks for the first virtual function call stack * @param virtualFunctionCallsMap map of results obtained from getVirtualFunctionCalls * @param callToken token where pure virtual function is called directly or indirectly * @param[in,out] pureFuncStack list to append the stack */ void getFirstVirtualFunctionCallStack( std::map > & virtualFunctionCallsMap, const Token *callToken, std::list & pureFuncStack); static bool canNotCopy(const Scope *scope); static bool canNotMove(const Scope *scope); }; /// @} //--------------------------------------------------------------------------- #endif // checkclassH cppcheck-1.90/lib/checkcondition.cpp000066400000000000000000002123431357737443600175110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Check for condition mismatches //--------------------------------------------------------------------------- #include "checkcondition.h" #include "astutils.h" #include "errorlogger.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include #include #include #include #include // CWE ids used static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE570(570U); // Expression is Always False static const struct CWE CWE571(571U); // Expression is Always True //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckCondition instance; } bool CheckCondition::diag(const Token* tok, bool insert) { if (!tok) return false; if (mCondDiags.find(tok) == mCondDiags.end()) { if (insert) mCondDiags.insert(tok); return false; } return true; } bool CheckCondition::isAliased(const std::set &vars) const { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "= & %var% ;") && vars.find(tok->tokAt(2)->varId()) != vars.end()) return true; } return false; } void CheckCondition::assignIf() { if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "=") continue; if (Token::Match(tok->tokAt(-2), "[;{}] %var% =")) { const Variable *var = tok->previous()->variable(); if (var == nullptr) continue; char bitop = '\0'; MathLib::bigint num = 0; if (Token::Match(tok->next(), "%num% [&|]")) { bitop = tok->strAt(2).at(0); num = MathLib::toLongNumber(tok->next()->str()); } else { const Token *endToken = Token::findsimplematch(tok, ";"); // Casting address if (endToken && Token::Match(endToken->tokAt(-4), "* ) & %any% ;")) endToken = nullptr; if (endToken && Token::Match(endToken->tokAt(-2), "[&|] %num% ;")) { bitop = endToken->strAt(-2).at(0); num = MathLib::toLongNumber(endToken->previous()->str()); } } if (bitop == '\0') continue; if (num < 0 && bitop == '|') continue; assignIfParseScope(tok, tok->tokAt(4), var->declarationId(), var->isLocal(), bitop, num); } } } static bool isParameterChanged(const Token *partok) { bool addressOf = Token::Match(partok, "[(,] &"); int argumentNumber = 0; const Token *ftok; for (ftok = partok; ftok && ftok->str() != "("; ftok = ftok->previous()) { if (ftok->str() == ")") ftok = ftok->link(); else if (argumentNumber == 0U && ftok->str() == "&") addressOf = true; else if (ftok->str() == ",") argumentNumber++; } ftok = ftok ? ftok->previous() : nullptr; if (!(ftok && ftok->function())) return true; const Variable *par = ftok->function()->getArgumentVar(argumentNumber); if (!par) return true; if (par->isConst()) return false; if (addressOf || par->isReference() || par->isPointer()) return true; return false; } /** parse scopes recursively */ bool CheckCondition::assignIfParseScope(const Token * const assignTok, const Token * const startTok, const nonneg int varid, const bool islocal, const char bitop, const MathLib::bigint num) { bool ret = false; for (const Token *tok2 = startTok; tok2; tok2 = tok2->next()) { if ((bitop == '&') && Token::Match(tok2->tokAt(2), "%varid% %cop% %num% ;", varid) && tok2->strAt(3) == std::string(1U, bitop)) { const MathLib::bigint num2 = MathLib::toLongNumber(tok2->strAt(4)); if (0 == (num & num2)) mismatchingBitAndError(assignTok, num, tok2, num2); } if (Token::Match(tok2, "%varid% =", varid)) { return true; } if (bitop == '&' && Token::Match(tok2, "%varid% &= %num% ;", varid)) { const MathLib::bigint num2 = MathLib::toLongNumber(tok2->strAt(2)); if (0 == (num & num2)) mismatchingBitAndError(assignTok, num, tok2, num2); } if (Token::Match(tok2, "++|-- %varid%", varid) || Token::Match(tok2, "%varid% ++|--", varid)) return true; if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid) && isParameterChanged(tok2)) return true; if (tok2->str() == "}") return false; if (Token::Match(tok2, "break|continue|return")) ret = true; if (ret && tok2->str() == ";") return false; if (!islocal && Token::Match(tok2, "%name% (") && !Token::simpleMatch(tok2->next()->link(), ") {")) return true; if (Token::Match(tok2, "if|while (")) { if (!islocal && tok2->str() == "while") continue; if (tok2->str() == "while") { // is variable changed in loop? const Token *bodyStart = tok2->linkAt(1)->next(); const Token *bodyEnd = bodyStart ? bodyStart->link() : nullptr; if (!bodyEnd || bodyEnd->str() != "}" || isVariableChanged(bodyStart, bodyEnd, varid, !islocal, mSettings, mTokenizer->isCPP())) continue; } // parse condition const Token * const end = tok2->next()->link(); for (; tok2 != end; tok2 = tok2->next()) { if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid)) { return true; } if (Token::Match(tok2,"&&|%oror%|( %varid% ==|!= %num% &&|%oror%|)", varid)) { const Token *vartok = tok2->next(); const MathLib::bigint num2 = MathLib::toLongNumber(vartok->strAt(2)); if ((num & num2) != ((bitop=='&') ? num2 : num)) { const std::string& op(vartok->strAt(1)); const bool alwaysTrue = op == "!="; const std::string condition(vartok->str() + op + vartok->strAt(2)); assignIfError(assignTok, tok2, condition, alwaysTrue); } } if (Token::Match(tok2, "%varid% %op%", varid) && tok2->next()->isAssignmentOp()) { return true; } } const bool ret1 = assignIfParseScope(assignTok, end->tokAt(2), varid, islocal, bitop, num); bool ret2 = false; if (Token::simpleMatch(end->next()->link(), "} else {")) ret2 = assignIfParseScope(assignTok, end->next()->link()->tokAt(3), varid, islocal, bitop, num); if (ret1 || ret2) return true; } } return false; } void CheckCondition::assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result) { if (tok2 && diag(tok2->tokAt(2))) return; std::list locations = { tok1, tok2 }; reportError(locations, Severity::style, "assignIfError", "Mismatching assignment and comparison, comparison '" + condition + "' is always " + std::string(result ? "true" : "false") + ".", CWE398, false); } void CheckCondition::mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2) { std::list locations = { tok1, tok2 }; std::ostringstream msg; msg << "Mismatching bitmasks. Result is always 0 (" << "X = Y & 0x" << std::hex << num1 << "; Z = X & 0x" << std::hex << num2 << "; => Z=0)."; reportError(locations, Severity::style, "mismatchingBitAnd", msg.str(), CWE398, false); } static void getnumchildren(const Token *tok, std::list &numchildren) { if (tok->astOperand1() && tok->astOperand1()->isNumber()) numchildren.push_back(MathLib::toLongNumber(tok->astOperand1()->str())); else if (tok->astOperand1() && tok->str() == tok->astOperand1()->str()) getnumchildren(tok->astOperand1(), numchildren); if (tok->astOperand2() && tok->astOperand2()->isNumber()) numchildren.push_back(MathLib::toLongNumber(tok->astOperand2()->str())); else if (tok->astOperand2() && tok->str() == tok->astOperand2()->str()) getnumchildren(tok->astOperand2(), numchildren); } /* Return whether tok is in the body for a function returning a boolean. */ static bool inBooleanFunction(const Token *tok) { const Scope *scope = tok ? tok->scope() : nullptr; while (scope && scope->isLocal()) scope = scope->nestedIn; if (scope && scope->type == Scope::eFunction) { const Function *func = scope->function; if (func) { const Token *ret = func->retDef; while (Token::Match(ret, "static|const")) ret = ret->next(); return Token::Match(ret, "bool|_Bool"); } } return false; } void CheckCondition::checkBadBitmaskCheck() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "|" && tok->astOperand1() && tok->astOperand2() && tok->astParent()) { const Token* parent = tok->astParent(); const bool isBoolean = Token::Match(parent, "&&|%oror%") || (parent->str() == "?" && parent->astOperand1() == tok) || (parent->str() == "=" && parent->astOperand2() == tok && parent->astOperand1() && parent->astOperand1()->variable() && Token::Match(parent->astOperand1()->variable()->typeStartToken(), "bool|_Bool")) || (parent->str() == "(" && Token::Match(parent->astOperand1(), "if|while")) || (parent->str() == "return" && parent->astOperand1() == tok && inBooleanFunction(tok)); const bool isTrue = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue != 0) || (tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue != 0); if (isBoolean && isTrue) badBitmaskCheckError(tok); } } } void CheckCondition::badBitmaskCheckError(const Token *tok) { reportError(tok, Severity::warning, "badBitmaskCheck", "Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?", CWE571, false); } void CheckCondition::comparison() { if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->isComparisonOp()) continue; const Token *expr1 = tok->astOperand1(); const Token *expr2 = tok->astOperand2(); if (!expr1 || !expr2) continue; if (expr1->isNumber()) std::swap(expr1,expr2); if (!expr2->isNumber()) continue; const MathLib::bigint num2 = MathLib::toLongNumber(expr2->str()); if (num2 < 0) continue; if (!Token::Match(expr1,"[&|]")) continue; std::list numbers; getnumchildren(expr1, numbers); for (const MathLib::bigint num1 : numbers) { if (num1 < 0) continue; if (Token::Match(tok, "==|!=")) { if ((expr1->str() == "&" && (num1 & num2) != num2) || (expr1->str() == "|" && (num1 | num2) != num2)) { const std::string& op(tok->str()); comparisonError(expr1, expr1->str(), num1, op, num2, op=="==" ? false : true); } } else if (expr1->str() == "&") { const bool or_equal = Token::Match(tok, ">=|<="); const std::string& op(tok->str()); if ((Token::Match(tok, ">=|<")) && (num1 < num2)) { comparisonError(expr1, expr1->str(), num1, op, num2, or_equal ? false : true); } else if ((Token::Match(tok, "<=|>")) && (num1 <= num2)) { comparisonError(expr1, expr1->str(), num1, op, num2, or_equal ? true : false); } } else if (expr1->str() == "|") { if ((expr1->astOperand1()->valueType()) && (expr1->astOperand1()->valueType()->sign == ValueType::Sign::UNSIGNED)) { const bool or_equal = Token::Match(tok, ">=|<="); const std::string& op(tok->str()); if ((Token::Match(tok, ">=|<")) && (num1 >= num2)) { //"(a | 0x07) >= 7U" is always true for unsigned a //"(a | 0x07) < 7U" is always false for unsigned a comparisonError(expr1, expr1->str(), num1, op, num2, or_equal ? true : false); } else if ((Token::Match(tok, "<=|>")) && (num1 > num2)) { //"(a | 0x08) <= 7U" is always false for unsigned a //"(a | 0x07) > 6U" is always true for unsigned a comparisonError(expr1, expr1->str(), num1, op, num2, or_equal ? false : true); } } } } } } void CheckCondition::comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result) { std::ostringstream expression; expression << std::hex << "(X " << bitop << " 0x" << value1 << ") " << op << " 0x" << value2; const std::string errmsg("Expression '" + expression.str() + "' is always " + (result?"true":"false") + ".\n" "The expression '" + expression.str() + "' is always " + (result?"true":"false") + ". Check carefully constants and operators used, these errors might be hard to " "spot sometimes. In case of complex expression it might help to split it to " "separate expressions."); reportError(tok, Severity::style, "comparisonError", errmsg, CWE398, false); } bool CheckCondition::isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const { if (!cond1 || !cond2) return false; // same expressions if (isSameExpression(mTokenizer->isCPP(), true, cond1, cond2, mSettings->library, pure, false)) return true; // bitwise overlap for example 'x&7' and 'x==1' if (cond1->str() == "&" && cond1->astOperand1() && cond2->astOperand2()) { const Token *expr1 = cond1->astOperand1(); const Token *num1 = cond1->astOperand2(); if (!num1) // unary operator& return false; if (!num1->isNumber()) std::swap(expr1,num1); if (!num1->isNumber() || MathLib::isNegative(num1->str())) return false; if (!Token::Match(cond2, "&|==") || !cond2->astOperand1() || !cond2->astOperand2()) return false; const Token *expr2 = cond2->astOperand1(); const Token *num2 = cond2->astOperand2(); if (!num2->isNumber()) std::swap(expr2,num2); if (!num2->isNumber() || MathLib::isNegative(num2->str())) return false; if (!isSameExpression(mTokenizer->isCPP(), true, expr1, expr2, mSettings->library, pure, false)) return false; const MathLib::bigint value1 = MathLib::toLongNumber(num1->str()); const MathLib::bigint value2 = MathLib::toLongNumber(num2->str()); if (cond2->str() == "&") return ((value1 & value2) == value2); return ((value1 & value2) > 0); } return false; } void CheckCondition::duplicateCondition() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf) continue; const Token *cond1 = scope.classDef->next()->astOperand2(); if (!cond1) continue; if (cond1->hasKnownIntValue()) continue; const Token *tok2 = scope.classDef->next(); if (!tok2) continue; tok2 = tok2->link(); if (!Token::simpleMatch(tok2, ") {")) continue; tok2 = tok2->linkAt(1); if (!Token::simpleMatch(tok2, "} if (")) continue; const Token *cond2 = tok2->tokAt(2)->astOperand2(); if (!cond2) continue; bool modified = false; visitAstNodes(cond1, [&](const Token *tok3) { if (tok3->varId() > 0 && isVariableChanged(scope.classDef->next(), cond2, tok3->varId(), false, mSettings, mTokenizer->isCPP())) { modified = true; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); ErrorPath errorPath; if (!modified && isSameExpression(mTokenizer->isCPP(), true, cond1, cond2, mSettings->library, true, true, &errorPath)) duplicateConditionError(cond1, cond2, errorPath); } } void CheckCondition::duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; errorPath.emplace_back(tok1, "First condition"); errorPath.emplace_back(tok2, "Second condition"); std::string msg = "The if condition is the same as the previous if condition"; reportError(errorPath, Severity::style, "duplicateCondition", msg, CWE398, false); } void CheckCondition::multiCondition() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf) continue; const Token * const cond1 = scope.classDef->next()->astOperand2(); if (!cond1) continue; const Token * tok2 = scope.classDef->next(); // Check each 'else if' for (;;) { tok2 = tok2->link(); if (!Token::simpleMatch(tok2, ") {")) break; tok2 = tok2->linkAt(1); if (!Token::simpleMatch(tok2, "} else { if (")) break; tok2 = tok2->tokAt(4); if (tok2->astOperand2() && !cond1->hasKnownIntValue() && !tok2->astOperand2()->hasKnownIntValue()) { ErrorPath errorPath; if (isOverlappingCond(cond1, tok2->astOperand2(), true)) overlappingElseIfConditionError(tok2, cond1->linenr()); else if (isOppositeCond(true, mTokenizer->isCPP(), cond1, tok2->astOperand2(), mSettings->library, true, true, &errorPath)) oppositeElseIfConditionError(cond1, tok2, errorPath); } } } } void CheckCondition::overlappingElseIfConditionError(const Token *tok, nonneg int line1) { if (diag(tok)) return; std::ostringstream errmsg; errmsg << "Expression is always false because 'else if' condition matches previous condition at line " << line1 << "."; reportError(tok, Severity::style, "multiCondition", errmsg.str(), CWE398, false); } void CheckCondition::oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath) { if (diag(ifCond) & diag(elseIfCond)) return; std::ostringstream errmsg; errmsg << "Expression is always true because 'else if' condition is opposite to previous condition at line " << ifCond->linenr() << "."; errorPath.emplace_back(ifCond, "first condition"); errorPath.emplace_back(elseIfCond, "else if condition is opposite to first condition"); reportError(errorPath, Severity::style, "multiCondition", errmsg.str(), CWE398, false); } //--------------------------------------------------------------------------- // - Opposite inner conditions => always false // - (TODO) Same/Overlapping inner condition => always true // - same condition after early exit => always false //--------------------------------------------------------------------------- static bool isNonConstFunctionCall(const Token *ftok, const Library &library) { if (library.isFunctionConst(ftok)) return false; const Token *obj = ftok->next()->astOperand1(); while (obj && obj->str() == ".") obj = obj->astOperand1(); if (!obj) return true; else if (obj->variable() && obj->variable()->isConst()) return false; else if (ftok->function() && ftok->function()->isConst()) return false; return true; } void CheckCondition::multiCondition2() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { const Token *condTok = nullptr; if (scope.type == Scope::eIf || scope.type == Scope::eWhile) condTok = scope.classDef->next()->astOperand2(); else if (scope.type == Scope::eFor) { condTok = scope.classDef->next()->astOperand2(); if (!condTok || condTok->str() != ";") continue; condTok = condTok->astOperand2(); if (!condTok || condTok->str() != ";") continue; condTok = condTok->astOperand1(); } if (!condTok) continue; const Token * const cond1 = condTok; if (!Token::simpleMatch(scope.classDef->linkAt(1), ") {")) continue; bool nonConstFunctionCall = false; bool nonlocal = false; // nonlocal variable used in condition std::set vars; // variables used in condition visitAstNodes(condTok, [&](const Token *cond) { if (Token::Match(cond, "%name% (")) { nonConstFunctionCall = isNonConstFunctionCall(cond, mSettings->library); if (nonConstFunctionCall) return ChildrenToVisit::done; } if (cond->varId()) { vars.insert(cond->varId()); const Variable *var = cond->variable(); if (!nonlocal && var) { if (!(var->isLocal() || var->isArgument())) nonlocal = true; else if ((var->isPointer() || var->isReference()) && !Token::Match(cond->astParent(), "%oror%|&&|!")) // TODO: if var is pointer check what it points at nonlocal = true; } } else if (!nonlocal && cond->isName()) { // varid is 0. this is possibly a nonlocal variable.. nonlocal = Token::Match(cond->astParent(), "%cop%|(|[") || Token::Match(cond, "%name% .") || (mTokenizer->isCPP() && cond->str() == "this"); } else { return ChildrenToVisit::op1_and_op2; } return ChildrenToVisit::none; }); if (nonConstFunctionCall) continue; // parse until second condition is reached.. enum MULTICONDITIONTYPE { INNER, AFTER }; const Token *tok; // Parse inner condition first and then early return condition std::vector types = {MULTICONDITIONTYPE::INNER}; if (Token::Match(scope.bodyStart, "{ return|throw|continue|break")) types.push_back(MULTICONDITIONTYPE::AFTER); for (MULTICONDITIONTYPE type:types) { if (type == MULTICONDITIONTYPE::AFTER) { tok = scope.bodyEnd->next(); } else { tok = scope.bodyStart; } const Token * const endToken = tok->scope()->bodyEnd; for (; tok && tok != endToken; tok = tok->next()) { if (Token::Match(tok, "if|return")) { const Token * condStartToken = tok->str() == "if" ? tok->next() : tok; const Token * condEndToken = tok->str() == "if" ? condStartToken->link() : Token::findsimplematch(condStartToken, ";"); // Does condition modify tracked variables? if (const Token *op = Token::findmatch(tok, "++|--", condEndToken)) { bool bailout = false; while (op) { if (vars.find(op->astOperand1()->varId()) != vars.end()) { bailout = true; break; } if (nonlocal && op->astOperand1()->varId() == 0) { bailout = true; break; } op = Token::findmatch(op->next(), "++|--", condEndToken); } if (bailout) break; } // Condition.. const Token *cond2 = tok->str() == "if" ? condStartToken->astOperand2() : condStartToken->astOperand1(); const bool isReturnVar = (tok->str() == "return" && !Token::Match(cond2, "%cop%")); ErrorPath errorPath; if (type == MULTICONDITIONTYPE::INNER) { std::stack tokens1; tokens1.push(cond1); while (!tokens1.empty()) { const Token *firstCondition = tokens1.top(); tokens1.pop(); if (!firstCondition) continue; if (firstCondition->str() == "&&") { tokens1.push(firstCondition->astOperand1()); tokens1.push(firstCondition->astOperand2()); } else if (!firstCondition->hasKnownIntValue()) { if (!isReturnVar && isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true, &errorPath)) { if (!isAliased(vars)) oppositeInnerConditionError(firstCondition, cond2, errorPath); } else if (!isReturnVar && isSameExpression(mTokenizer->isCPP(), true, firstCondition, cond2, mSettings->library, true, true, &errorPath)) { identicalInnerConditionError(firstCondition, cond2, errorPath); } } } } else { std::stack tokens2; tokens2.push(cond2); while (!tokens2.empty()) { const Token *secondCondition = tokens2.top(); tokens2.pop(); if (!secondCondition) continue; if (secondCondition->str() == "||" || secondCondition->str() == "&&") { tokens2.push(secondCondition->astOperand1()); tokens2.push(secondCondition->astOperand2()); } else if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) && isSameExpression(mTokenizer->isCPP(), true, cond1, secondCondition, mSettings->library, true, true, &errorPath)) { if (!isAliased(vars)) identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath); } } } } if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, mSettings->library)) // non const function call -> bailout if there are nonlocal variables break; if (Token::Match(tok, "case|break|continue|return|throw") && tok->scope() == endToken->scope()) break; if (Token::Match(tok, "[;{}] %name% :")) break; // bailout if loop is seen. // TODO: handle loops better. if (Token::Match(tok, "for|while|do")) { const Token *tok1 = tok->next(); const Token *tok2; if (Token::simpleMatch(tok, "do {")) { if (!Token::simpleMatch(tok->linkAt(1), "} while (")) break; tok2 = tok->linkAt(1)->linkAt(2); } else if (Token::Match(tok, "if|while (")) { tok2 = tok->linkAt(1); if (Token::simpleMatch(tok2, ") {")) tok2 = tok2->linkAt(1); if (!tok2) break; } else { // Incomplete code break; } bool changed = false; for (int varid : vars) { if (isVariableChanged(tok1, tok2, varid, nonlocal, mSettings, mTokenizer->isCPP())) { changed = true; break; } } if (changed) break; } if ((tok->varId() && vars.find(tok->varId()) != vars.end()) || (!tok->varId() && nonlocal)) { if (Token::Match(tok, "%name% %assign%|++|--")) break; if (Token::Match(tok->astParent(), "*|.|[")) { const Token *parent = tok; while (Token::Match(parent->astParent(), ".|[") || (parent->astParent() && parent->astParent()->isUnaryOp("*"))) parent = parent->astParent(); if (Token::Match(parent->astParent(), "%assign%|++|--")) break; } if (mTokenizer->isCPP() && Token::Match(tok, "%name% <<") && (!tok->valueType() || !tok->valueType()->isIntegral())) break; if (isLikelyStreamRead(mTokenizer->isCPP(), tok->next()) || isLikelyStreamRead(mTokenizer->isCPP(), tok->previous())) break; if (Token::Match(tok, "%name% [")) { const Token *tok2 = tok->linkAt(1); while (Token::simpleMatch(tok2, "] [")) tok2 = tok2->linkAt(1); if (Token::Match(tok2, "] %assign%|++|--")) break; } if (Token::Match(tok->previous(), "++|--|& %name%")) break; if (tok->variable() && !tok->variable()->isConst() && Token::Match(tok, "%name% . %name% (")) { const Function* function = tok->tokAt(2)->function(); if (!function || !function->isConst()) break; } if (Token::Match(tok->previous(), "[(,] %name% [,)]") && isParameterChanged(tok)) break; } } } } } static std::string innerSmtString(const Token * tok) { if (!tok) return "if"; if (!tok->astTop()) return "if"; const Token * top = tok->astTop(); if (top->str() == "(" && top->astOperand1()) return top->astOperand1()->str(); return top->str(); } void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; const std::string s1(tok1 ? tok1->expressionString() : "x"); const std::string s2(tok2 ? tok2->expressionString() : "!x"); const std::string innerSmt = innerSmtString(tok2); errorPath.emplace_back(ErrorPathItem(tok1, "outer condition: " + s1)); errorPath.emplace_back(ErrorPathItem(tok2, "opposite inner condition: " + s2)); const std::string msg("Opposite inner '" + innerSmt + "' condition leads to a dead code block.\n" "Opposite inner '" + innerSmt + "' condition leads to a dead code block (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); reportError(errorPath, Severity::warning, "oppositeInnerCondition", msg, CWE398, false); } void CheckCondition::identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; const std::string s1(tok1 ? tok1->expressionString() : "x"); const std::string s2(tok2 ? tok2->expressionString() : "x"); const std::string innerSmt = innerSmtString(tok2); errorPath.emplace_back(ErrorPathItem(tok1, "outer condition: " + s1)); errorPath.emplace_back(ErrorPathItem(tok2, "identical inner condition: " + s2)); const std::string msg("Identical inner '" + innerSmt + "' condition is always true.\n" "Identical inner '" + innerSmt + "' condition is always true (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); reportError(errorPath, Severity::warning, "identicalInnerCondition", msg, CWE398, false); } void CheckCondition::identicalConditionAfterEarlyExitError(const Token *cond1, const Token* cond2, ErrorPath errorPath) { if (diag(cond1) & diag(cond2)) return; const bool isReturnValue = cond2 && Token::simpleMatch(cond2->astParent(), "return"); const std::string cond(cond1 ? cond1->expressionString() : "x"); const std::string value = (cond2 && cond2->valueType() && cond2->valueType()->type == ValueType::Type::BOOL) ? "false" : "0"; errorPath.emplace_back(ErrorPathItem(cond1, "If condition '" + cond + "' is true, the function will return/exit")); errorPath.emplace_back(ErrorPathItem(cond2, (isReturnValue ? "Returning identical expression '" : "Testing identical condition '") + cond + "'")); reportError(errorPath, Severity::warning, "identicalConditionAfterEarlyExit", isReturnValue ? ("Identical condition and return expression '" + cond + "', return value is always " + value) : ("Identical condition '" + cond + "', second condition is always false"), CWE398, false); } //--------------------------------------------------------------------------- // if ((x != 1) || (x != 3)) // expression always true // if ((x == 1) && (x == 3)) // expression always false // if ((x < 1) && (x > 3)) // expression always false // if ((x > 3) || (x < 10)) // expression always true // if ((x > 5) && (x != 1)) // second comparison always true // // Check for suspect logic for an expression consisting of 2 comparison // expressions with a shared variable and constants and a logical operator // between them. // // Suggest a different logical operator when the logical operator between // the comparisons is probably wrong. // // Inform that second comparison is always true when first comparison is true. //--------------------------------------------------------------------------- static std::string invertOperatorForOperandSwap(std::string s) { if (s[0] == '<') s[0] = '>'; else if (s[0] == '>') s[0] = '<'; return s; } template static bool checkIntRelation(const std::string &op, const T value1, const T value2) { return (op == "==" && value1 == value2) || (op == "!=" && value1 != value2) || (op == ">" && value1 > value2) || (op == ">=" && value1 >= value2) || (op == "<" && value1 < value2) || (op == "<=" && value1 <= value2); } static bool checkFloatRelation(const std::string &op, const double value1, const double value2) { return (op == ">" && value1 > value2) || (op == ">=" && value1 >= value2) || (op == "<" && value1 < value2) || (op == "<=" && value1 <= value2); } template T getvalue3(const T value1, const T value2) { const T min = std::min(value1, value2); if (min== std::numeric_limits::max()) return min; else return min+1; // see #5895 } template<> double getvalue3(const double value1, const double value2) { return (value1 + value2) / 2.0f; } template static inline T getvalue(const int test, const T value1, const T value2) { // test: // 1 => return value that is less than both value1 and value2 // 2 => return value1 // 3 => return value that is between value1 and value2 // 4 => return value2 // 5 => return value that is larger than both value1 and value2 switch (test) { case 1: return std::numeric_limits::lowest(); case 2: return value1; case 3: return getvalue3(value1, value2); case 4: return value2; case 5: return std::numeric_limits::max(); }; return 0; } static bool parseComparison(const Token *comp, bool *not1, std::string *op, std::string *value, const Token **expr, bool* inconclusive) { *not1 = false; while (comp && comp->str() == "!") { *not1 = !(*not1); comp = comp->astOperand1(); } if (!comp) return false; const Token* op1 = comp->astOperand1(); const Token* op2 = comp->astOperand2(); if (!comp->isComparisonOp() || !op1 || !op2) { *op = "!="; *value = "0"; *expr = comp; } else if (op1->isLiteral()) { if (op1->isExpandedMacro()) return false; *op = invertOperatorForOperandSwap(comp->str()); if (op1->enumerator() && op1->enumerator()->value_known) *value = MathLib::toString(op1->enumerator()->value); else *value = op1->str(); *expr = op2; } else if (comp->astOperand2()->isLiteral()) { if (op2->isExpandedMacro()) return false; *op = comp->str(); if (op2->enumerator() && op2->enumerator()->value_known) *value = MathLib::toString(op2->enumerator()->value); else *value = op2->str(); *expr = op1; } else { *op = "!="; *value = "0"; *expr = comp; } *inconclusive = *inconclusive || ((*value)[0] == '\'' && !(*op == "!=" || *op == "==")); // Only float and int values are currently handled if (!MathLib::isInt(*value) && !MathLib::isFloat(*value) && (*value)[0] != '\'') return false; return true; } static std::string conditionString(bool not1, const Token *expr1, const std::string &op, const std::string &value1) { if (expr1->astParent()->isComparisonOp()) return std::string(not1 ? "!(" : "") + (expr1->isName() ? expr1->str() : std::string("EXPR")) + " " + op + " " + value1 + (not1 ? ")" : ""); return std::string(not1 ? "!" : "") + (expr1->isName() ? expr1->str() : std::string("EXPR")); } static std::string conditionString(const Token * tok) { if (!tok) return ""; if (tok->isComparisonOp()) { bool inconclusive = false; bool not_; std::string op, value; const Token *expr; if (parseComparison(tok, ¬_, &op, &value, &expr, &inconclusive) && expr->isName()) { return conditionString(not_, expr, op, value); } } if (Token::Match(tok, "%cop%|&&|%oror%")) { if (tok->astOperand2()) return conditionString(tok->astOperand1()) + " " + tok->str() + " " + conditionString(tok->astOperand2()); return tok->str() + "(" + conditionString(tok->astOperand1()) + ")"; } return tok->expressionString(); } void CheckCondition::checkIncorrectLogicOperator() { const bool printStyle = mSettings->isEnabled(Settings::STYLE); const bool printWarning = mSettings->isEnabled(Settings::WARNING); if (!printWarning && !printStyle) return; const bool printInconclusive = mSettings->inconclusive; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2()) continue; // 'A && (!A || B)' is equivalent to 'A && B' // 'A || (!A && B)' is equivalent to 'A || B' if (printStyle && ((tok->str() == "||" && tok->astOperand2()->str() == "&&") || (tok->str() == "&&" && tok->astOperand2()->str() == "||"))) { const Token* tok2 = tok->astOperand2()->astOperand1(); if (isOppositeCond(true, mTokenizer->isCPP(), tok->astOperand1(), tok2, mSettings->library, true, false)) { std::string expr1(tok->astOperand1()->expressionString()); std::string expr2(tok->astOperand2()->astOperand1()->expressionString()); std::string expr3(tok->astOperand2()->astOperand2()->expressionString()); // make copy for later because the original string might get overwritten const std::string expr1VerboseMsg = expr1; const std::string expr2VerboseMsg = expr2; const std::string expr3VerboseMsg = expr3; if (expr1.length() + expr2.length() + expr3.length() > 50U) { if (expr1[0] == '!' && expr2[0] != '!') { expr1 = "!A"; expr2 = "A"; } else { expr1 = "A"; expr2 = "!A"; } expr3 = "B"; } const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")"; const std::string cond2 = expr1 + " " + tok->str() + " " + expr3; const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg; const std::string cond2VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr3VerboseMsg; // for the --verbose message, transform the actual condition and print it const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n" "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'."; redundantConditionError(tok, msg, false); continue; } } // Comparison #1 (LHS) const Token *comp1 = tok->astOperand1(); if (comp1 && comp1->str() == tok->str()) comp1 = comp1->astOperand2(); // Comparison #2 (RHS) const Token *comp2 = tok->astOperand2(); bool inconclusive = false; bool parseable = true; // Parse LHS bool not1; std::string op1, value1; const Token *expr1 = nullptr; parseable &= (parseComparison(comp1, ¬1, &op1, &value1, &expr1, &inconclusive)); // Parse RHS bool not2; std::string op2, value2; const Token *expr2 = nullptr; parseable &= (parseComparison(comp2, ¬2, &op2, &value2, &expr2, &inconclusive)); if (inconclusive && !printInconclusive) continue; const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2); ErrorPath errorPath; // Opposite comparisons around || or && => always true or always false if (!isfloat && isOppositeCond(tok->str() == "||", mTokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true, &errorPath)) { const bool alwaysTrue(tok->str() == "||"); incorrectLogicOperatorError(tok, conditionString(tok), alwaysTrue, inconclusive, errorPath); continue; } if (!parseable) continue; if (isSameExpression(mTokenizer->isCPP(), true, comp1, comp2, mSettings->library, true, true)) continue; // same expressions => only report that there are same expressions if (!isSameExpression(mTokenizer->isCPP(), true, expr1, expr2, mSettings->library, true, true)) continue; // don't check floating point equality comparisons. that is bad // and deserves different warnings. if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!=")) continue; const double d1 = (isfloat) ? MathLib::toDoubleNumber(value1) : 0; const double d2 = (isfloat) ? MathLib::toDoubleNumber(value2) : 0; const MathLib::bigint i1 = (isfloat) ? 0 : MathLib::toLongNumber(value1); const MathLib::bigint i2 = (isfloat) ? 0 : MathLib::toLongNumber(value2); const bool useUnsignedInt = (std::numeric_limits::max()==i1)||(std::numeric_limits::max()==i2); const MathLib::biguint u1 = (useUnsignedInt) ? MathLib::toLongNumber(value1) : 0; const MathLib::biguint u2 = (useUnsignedInt) ? MathLib::toLongNumber(value2) : 0; // evaluate if expression is always true/false bool alwaysTrue = true, alwaysFalse = true; bool firstTrue = true, secondTrue = true; for (int test = 1; test <= 5; ++test) { // test: // 1 => testvalue is less than both value1 and value2 // 2 => testvalue is value1 // 3 => testvalue is between value1 and value2 // 4 => testvalue value2 // 5 => testvalue is larger than both value1 and value2 bool result1, result2; if (isfloat) { const double testvalue = getvalue(test, d1, d2); result1 = checkFloatRelation(op1, testvalue, d1); result2 = checkFloatRelation(op2, testvalue, d2); } else if (useUnsignedInt) { const MathLib::biguint testvalue = getvalue(test, u1, u2); result1 = checkIntRelation(op1, testvalue, u1); result2 = checkIntRelation(op2, testvalue, u2); } else { const MathLib::bigint testvalue = getvalue(test, i1, i2); result1 = checkIntRelation(op1, testvalue, i1); result2 = checkIntRelation(op2, testvalue, i2); } if (not1) result1 = !result1; if (not2) result2 = !result2; if (tok->str() == "&&") { alwaysTrue &= (result1 && result2); alwaysFalse &= !(result1 && result2); } else { alwaysTrue &= (result1 || result2); alwaysFalse &= !(result1 || result2); } firstTrue &= !(!result1 && result2); secondTrue &= !(result1 && !result2); } const std::string cond1str = conditionString(not1, expr1, op1, value1); const std::string cond2str = conditionString(not2, expr2, op2, value2); if (printWarning && (alwaysTrue || alwaysFalse)) { const std::string text = cond1str + " " + tok->str() + " " + cond2str; incorrectLogicOperatorError(tok, text, alwaysTrue, inconclusive, errorPath); } else if (printStyle && secondTrue) { const std::string text = "If '" + cond1str + "', the comparison '" + cond2str + "' is always true."; redundantConditionError(tok, text, inconclusive); } else if (printStyle && firstTrue) { //const std::string text = "The comparison " + cond1str + " is always " + // (firstTrue ? "true" : "false") + " when " + // cond2str + "."; const std::string text = "If '" + cond2str + "', the comparison '" + cond1str + "' is always true."; redundantConditionError(tok, text, inconclusive); } } } } void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors) { errors.emplace_back(tok, ""); if (always) reportError(errors, Severity::warning, "incorrectLogicOperator", "Logical disjunction always evaluates to true: " + condition + ".\n" "Logical disjunction always evaluates to true: " + condition + ". " "Are these conditions necessary? Did you intend to use && instead? Are the numbers correct? Are you comparing the correct variables?", CWE571, inconclusive); else reportError(errors, Severity::warning, "incorrectLogicOperator", "Logical conjunction always evaluates to false: " + condition + ".\n" "Logical conjunction always evaluates to false: " + condition + ". " "Are these conditions necessary? Did you intend to use || instead? Are the numbers correct? Are you comparing the correct variables?", CWE570, inconclusive); } void CheckCondition::redundantConditionError(const Token *tok, const std::string &text, bool inconclusive) { reportError(tok, Severity::style, "redundantCondition", "Redundant condition: " + text, CWE398, inconclusive); } //----------------------------------------------------------------------------- // Detect "(var % val1) > val2" where val2 is >= val1. //----------------------------------------------------------------------------- void CheckCondition::checkModuloAlwaysTrueFalse() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp()) continue; const Token *num, *modulo; if (Token::simpleMatch(tok->astOperand1(), "%") && Token::Match(tok->astOperand2(), "%num%")) { modulo = tok->astOperand1(); num = tok->astOperand2(); } else if (Token::Match(tok->astOperand1(), "%num%") && Token::simpleMatch(tok->astOperand2(), "%")) { num = tok->astOperand1(); modulo = tok->astOperand2(); } else { continue; } if (Token::Match(modulo->astOperand2(), "%num%") && MathLib::isLessEqual(modulo->astOperand2()->str(), num->str())) moduloAlwaysTrueFalseError(tok, modulo->astOperand2()->str()); } } } void CheckCondition::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal) { reportError(tok, Severity::warning, "moduloAlwaysTrueFalse", "Comparison of modulo result is predetermined, because it is always less than " + maxVal + ".", CWE398, false); } static int countPar(const Token *tok1, const Token *tok2) { int par = 0; for (const Token *tok = tok1; tok && tok != tok2; tok = tok->next()) { if (tok->str() == "(") ++par; else if (tok->str() == ")") --par; else if (tok->str() == ";") return -1; } return par; } //--------------------------------------------------------------------------- // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))' // Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))' //--------------------------------------------------------------------------- void CheckCondition::clarifyCondition() { if (!mSettings->isEnabled(Settings::STYLE)) return; const bool isC = mTokenizer->isC(); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "( %name% [=&|^]")) { for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") tok2 = tok2->link(); else if (tok2->isComparisonOp()) { // This might be a template if (!isC && tok2->link()) break; if (Token::simpleMatch(tok2->astParent(), "?")) break; clarifyConditionError(tok, tok->strAt(2) == "=", false); break; } else if (!tok2->isName() && !tok2->isNumber() && tok2->str() != ".") break; } } else if (tok->tokType() == Token::eBitOp && !tok->isUnaryOp("&")) { if (tok->astOperand2() && tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2()) continue; // using boolean result in bitwise operation ! x [&|^] const ValueType* vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType* vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; if (vt1 && vt1->type == ValueType::BOOL && !Token::Match(tok->astOperand1(), "%name%|(|[|::|.") && countPar(tok->astOperand1(), tok) == 0) clarifyConditionError(tok, false, true); else if (vt2 && vt2->type == ValueType::BOOL && !Token::Match(tok->astOperand2(), "%name%|(|[|::|.") && countPar(tok, tok->astOperand2()) == 0) clarifyConditionError(tok, false, true); } } } } void CheckCondition::clarifyConditionError(const Token *tok, bool assign, bool boolop) { std::string errmsg; if (assign) errmsg = "Suspicious condition (assignment + comparison); Clarify expression with parentheses."; else if (boolop) errmsg = "Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" "Suspicious expression. Boolean result is used in bitwise operation. The operator '!' " "and the comparison operators have higher precedence than bitwise operators. " "It is recommended that the expression is clarified with parentheses."; else errmsg = "Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "Suspicious condition. Comparison operators have higher precedence than bitwise operators. " "Please clarify the condition with parentheses."; reportError(tok, Severity::style, "clarifyCondition", errmsg, CWE398, false); } void CheckCondition::alwaysTrueFalse() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->link()) // don't write false positives when templates are used continue; if (!tok->hasKnownIntValue()) continue; { // is this a condition.. const Token *parent = tok->astParent(); while (Token::Match(parent, "%oror%|&&")) parent = parent->astParent(); if (!parent) continue; const Token *condition = nullptr; if (parent->str() == "?" && precedes(tok, parent)) condition = parent->astOperand1(); else if (Token::Match(parent->previous(), "if|while (")) condition = parent->astOperand2(); else if (parent->str() == ";" && parent->astParent() && parent->astParent()->astParent() && Token::simpleMatch(parent->astParent()->astParent()->previous(), "for (")) condition = parent->astOperand1(); else continue; (void)condition; } // Skip already diagnosed values if (diag(tok, false)) continue; if (Token::Match(tok, "%num%|%bool%|%char%")) continue; if (Token::Match(tok, "! %num%|%bool%|%char%")) continue; if (Token::Match(tok, "%oror%|&&|:")) continue; if (Token::Match(tok, "%comp%") && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true)) continue; const bool constIfWhileExpression = tok->astParent() && Token::Match(tok->astTop()->astOperand1(), "if|while") && !tok->astTop()->astOperand1()->isConstexpr() && (Token::Match(tok->astParent(), "%oror%|&&") || Token::Match(tok->astParent()->astOperand1(), "if|while")); const bool constValExpr = tok->isNumber() && Token::Match(tok->astParent(),"%oror%|&&|?"); // just one number in boolean expression const bool compExpr = Token::Match(tok, "%comp%|!"); // a compare expression const bool ternaryExpression = Token::simpleMatch(tok->astParent(), "?"); if (!(constIfWhileExpression || constValExpr || compExpr || ternaryExpression)) continue; // Don't warn when there are expanded macros.. bool isExpandedMacro = false; std::stack tokens; tokens.push(tok); while (!tokens.empty()) { const Token *tok2 = tokens.top(); tokens.pop(); if (!tok2) continue; tokens.push(tok2->astOperand1()); tokens.push(tok2->astOperand2()); if (tok2->isExpandedMacro()) { isExpandedMacro = true; break; } } if (isExpandedMacro) continue; for (const Token *parent = tok; parent; parent = parent->astParent()) { if (parent->isExpandedMacro()) { isExpandedMacro = true; break; } } if (isExpandedMacro) continue; // don't warn when condition checks sizeof result bool hasSizeof = false; tokens.push(tok); while (!tokens.empty()) { const Token *tok2 = tokens.top(); tokens.pop(); if (!tok2) continue; if (tok2->isNumber()) continue; if (Token::simpleMatch(tok2->previous(), "sizeof (")) { hasSizeof = true; continue; } if (tok2->isComparisonOp() || tok2->isArithmeticalOp()) { tokens.push(tok2->astOperand1()); tokens.push(tok2->astOperand2()); } else break; } if (tokens.empty() && hasSizeof) continue; alwaysTrueFalseError(tok, &tok->values().front()); } } } void CheckCondition::alwaysTrueFalseError(const Token *tok, const ValueFlow::Value *value) { const bool alwaysTrue = value && (value->intvalue != 0); const std::string expr = tok ? tok->expressionString() : std::string("x"); const std::string errmsg = "Condition '" + expr + "' is always " + (alwaysTrue ? "true" : "false"); const ErrorPath errorPath = getErrorPath(tok, value, errmsg); reportError(errorPath, Severity::style, "knownConditionTrueFalse", errmsg, (alwaysTrue ? CWE571 : CWE570), false); } void CheckCondition::checkInvalidTestForOverflow() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) continue; const Token *calcToken, *exprToken; bool result; if (Token::Match(tok, "<|>=") && tok->astOperand1()->str() == "+") { calcToken = tok->astOperand1(); exprToken = tok->astOperand2(); result = (tok->str() == ">="); } else if (Token::Match(tok, ">|<=") && tok->astOperand2()->str() == "+") { calcToken = tok->astOperand2(); exprToken = tok->astOperand1(); result = (tok->str() == "<="); } else continue; // Only warn for signed integer overflows and pointer overflows. if (!(calcToken->valueType() && (calcToken->valueType()->pointer || calcToken->valueType()->sign == ValueType::Sign::SIGNED))) continue; if (!(exprToken->valueType() && (exprToken->valueType()->pointer || exprToken->valueType()->sign == ValueType::Sign::SIGNED))) continue; const Token *termToken; if (isSameExpression(mTokenizer->isCPP(), true, exprToken, calcToken->astOperand1(), mSettings->library, true, false)) termToken = calcToken->astOperand2(); else if (isSameExpression(mTokenizer->isCPP(), true, exprToken, calcToken->astOperand2(), mSettings->library, true, false)) termToken = calcToken->astOperand1(); else continue; if (!termToken) continue; // Only warn when termToken is always positive if (termToken->valueType() && termToken->valueType()->sign == ValueType::Sign::UNSIGNED) invalidTestForOverflow(tok, result); else if (termToken->isNumber() && MathLib::isPositive(termToken->str())) invalidTestForOverflow(tok, result); } } } void CheckCondition::invalidTestForOverflow(const Token* tok, bool result) { const std::string errmsg = "Invalid test for overflow '" + (tok ? tok->expressionString() : std::string("x + u < x")) + "'. Condition is always " + std::string(result ? "true" : "false") + " unless there is overflow, and overflow is undefined behaviour."; reportError(tok, Severity::warning, "invalidTestForOverflow", errmsg, (result ? CWE571 : CWE570), false); } void CheckCondition::checkPointerAdditionResultNotNull() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) continue; // Macros might have pointless safety checks if (tok->isExpandedMacro()) continue; const Token *calcToken, *exprToken; if (tok->astOperand1()->str() == "+") { calcToken = tok->astOperand1(); exprToken = tok->astOperand2(); } else if (tok->astOperand2()->str() == "+") { calcToken = tok->astOperand2(); exprToken = tok->astOperand1(); } else continue; // pointer comparison against NULL (ptr+12==0) if (calcToken->hasKnownIntValue()) continue; if (!calcToken->valueType() || calcToken->valueType()->pointer==0) continue; if (!exprToken->hasKnownIntValue() || !exprToken->getValue(0)) continue; pointerAdditionResultNotNullError(tok, calcToken); } } } void CheckCondition::pointerAdditionResultNotNullError(const Token *tok, const Token *calc) { const std::string s = calc ? calc->expressionString() : "ptr+1"; reportError(tok, Severity::warning, "pointerAdditionResultNotNull", "Comparison is wrong. Result of '" + s + "' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour."); } void CheckCondition::checkDuplicateConditionalAssign() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "if (")) continue; if (!Token::simpleMatch(tok->next()->link(), ") {")) continue; const Token *blockTok = tok->next()->link()->next(); const Token *condTok = tok->next()->astOperand2(); if (!Token::Match(condTok, "==|!=")) continue; if (condTok->str() == "!=" && Token::simpleMatch(blockTok->link(), "} else {")) continue; if (!blockTok->next()) continue; const Token *assignTok = blockTok->next()->astTop(); if (!Token::simpleMatch(assignTok, "=")) continue; if (nextAfterAstRightmostLeaf(assignTok) != blockTok->link()->previous()) continue; if (!isSameExpression( mTokenizer->isCPP(), true, condTok->astOperand1(), assignTok->astOperand1(), mSettings->library, true, true)) continue; if (!isSameExpression( mTokenizer->isCPP(), true, condTok->astOperand2(), assignTok->astOperand2(), mSettings->library, true, true)) continue; duplicateConditionalAssignError(condTok, assignTok); } } } void CheckCondition::duplicateConditionalAssignError(const Token *condTok, const Token* assignTok) { ErrorPath errors; std::string msg = "Duplicate expression for the condition and assignment."; if (condTok && assignTok) { if (condTok->str() == "==") { msg = "Assignment '" + assignTok->expressionString() + "' is redundant with condition '" + condTok->expressionString() + "'."; errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "'"); errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "' is redundant"); } else { msg = "The statement 'if (" + condTok->expressionString() + ") " + assignTok->expressionString() + "' is logically equivalent to '" + assignTok->expressionString() + "'."; errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "'"); errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "' is redundant"); } } reportError( errors, Severity::style, "duplicateConditionalAssign", msg, CWE398, false); } cppcheck-1.90/lib/checkcondition.h000066400000000000000000000211361357737443600171540ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkconditionH #define checkconditionH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "mathlib.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** * @brief Check for condition mismatches */ class CPPCHECKLIB CheckCondition : public Check { public: /** This constructor is used when registering the CheckAssignIf */ CheckCondition() : Check(myName()) { } /** This constructor is used when running checks. */ CheckCondition(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckCondition checkCondition(tokenizer, settings, errorLogger); checkCondition.multiCondition(); checkCondition.clarifyCondition(); // not simplified because ifAssign checkCondition.multiCondition2(); checkCondition.checkIncorrectLogicOperator(); checkCondition.checkInvalidTestForOverflow(); checkCondition.duplicateCondition(); checkCondition.checkPointerAdditionResultNotNull(); checkCondition.checkDuplicateConditionalAssign(); checkCondition.assignIf(); checkCondition.alwaysTrueFalse(); checkCondition.checkBadBitmaskCheck(); checkCondition.comparison(); checkCondition.checkModuloAlwaysTrueFalse(); } /** mismatching assignment / comparison */ void assignIf(); /** parse scopes recursively */ bool assignIfParseScope(const Token * const assignTok, const Token * const startTok, const nonneg int varid, const bool islocal, const char bitop, const MathLib::bigint num); /** check bitmask using | instead of & */ void checkBadBitmaskCheck(); /** mismatching lhs and rhs in comparison */ void comparison(); void duplicateCondition(); /** match 'if' and 'else if' conditions */ void multiCondition(); /** * multiconditions #2 * - Opposite inner conditions => always false * - (TODO) Same/Overlapping inner condition => always true * - same condition after early exit => always false **/ void multiCondition2(); /** @brief %Check for testing for mutual exclusion over ||*/ void checkIncorrectLogicOperator(); /** @brief %Check for suspicious usage of modulo (e.g. "if(var % 4 == 4)") */ void checkModuloAlwaysTrueFalse(); /** @brief Suspicious condition (assignment+comparison) */ void clarifyCondition(); /** @brief Condition is always true/false */ void alwaysTrueFalse(); /** @brief %Check for invalid test for overflow 'x+100 < x' */ void checkInvalidTestForOverflow(); /** @brief Check if pointer addition result is NULL '(ptr + 1) == NULL' */ void checkPointerAdditionResultNotNull(); void checkDuplicateConditionalAssign(); private: // The conditions that have been diagnosed std::set mCondDiags; bool diag(const Token* tok, bool insert=true); bool isAliased(const std::set &vars) const; bool isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const; void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result); void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2); void badBitmaskCheckError(const Token *tok); void comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result); void duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath); void overlappingElseIfConditionError(const Token *tok, nonneg int line1); void oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath); void oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); void identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2, ErrorPath errorPath); void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors); void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive); void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal); void clarifyConditionError(const Token *tok, bool assign, bool boolop); void alwaysTrueFalseError(const Token *tok, const ValueFlow::Value *value); void invalidTestForOverflow(const Token* tok, bool result); void pointerAdditionResultNotNullError(const Token *tok, const Token *calc); void duplicateConditionalAssignError(const Token *condTok, const Token* assignTok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckCondition c(nullptr, settings, errorLogger); ErrorPath errorPath; c.assignIfError(nullptr, nullptr, emptyString, false); c.badBitmaskCheckError(nullptr); c.comparisonError(nullptr, "&", 6, "==", 1, false); c.duplicateConditionError(nullptr, nullptr, errorPath); c.overlappingElseIfConditionError(nullptr, 1); c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1); c.oppositeInnerConditionError(nullptr, nullptr, errorPath); c.identicalInnerConditionError(nullptr, nullptr, errorPath); c.identicalConditionAfterEarlyExitError(nullptr, nullptr, errorPath); c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false, errorPath); c.redundantConditionError(nullptr, "If x > 11 the condition x > 10 is always true.", false); c.moduloAlwaysTrueFalseError(nullptr, "1"); c.clarifyConditionError(nullptr, true, false); c.alwaysTrueFalseError(nullptr, nullptr); c.invalidTestForOverflow(nullptr, false); c.pointerAdditionResultNotNullError(nullptr, nullptr); c.duplicateConditionalAssignError(nullptr, nullptr); } static std::string myName() { return "Condition"; } std::string classInfo() const OVERRIDE { return "Match conditions with assignments and other conditions:\n" "- Mismatching assignment and comparison => comparison is always true/false\n" "- Mismatching lhs and rhs in comparison => comparison is always true/false\n" "- Detect usage of | where & should be used\n" "- Duplicate condition and assignment\n" "- Detect matching 'if' and 'else if' conditions\n" "- Mismatching bitand (a &= 0xf0; a &= 1; => a = 0)\n" "- Opposite inner condition is always false\n" "- Identical condition after early exit is always false\n" "- Condition that is always true/false\n" "- Mutual exclusion over || always evaluating to true\n" "- Comparisons of modulo results that are always true/false.\n" "- Known variable values => condition is always true/false\n" "- Invalid test for overflow (for example 'ptr+u < ptr'). Condition is always false unless there is overflow, and overflow is undefined behaviour.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkconditionH cppcheck-1.90/lib/checkexceptionsafety.cpp000066400000000000000000000263341357737443600207400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkexceptionsafety.h" #include "settings.h" #include "symboldatabase.h" #include #include #include //--------------------------------------------------------------------------- // Register CheckExceptionSafety.. namespace { CheckExceptionSafety instance; } //--------------------------------------------------------------------------- void CheckExceptionSafety::destructors() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // Perform check.. for (const Scope * scope : symbolDatabase->functionScopes) { const Function * function = scope->function; if (!function) continue; // only looking for destructors if (function->type == Function::eDestructor) { // Inspect this destructor. for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // Skip try blocks if (Token::simpleMatch(tok, "try {")) { tok = tok->next()->link(); } // Skip uncaught exceptions else if (Token::simpleMatch(tok, "if ( ! std :: uncaught_exception ( ) ) {")) { tok = tok->next()->link(); // end of if ( ... ) tok = tok->next()->link(); // end of { ... } } // throw found within a destructor else if (tok->str() == "throw") { destructorsError(tok, scope->className); break; } } } } } void CheckExceptionSafety::deallocThrow() { if (!mSettings->isEnabled(Settings::WARNING)) return; const bool printInconclusive = mSettings->inconclusive; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // Deallocate a global/member pointer and then throw exception // the pointer will be a dead pointer for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // only looking for delete now if (tok->str() != "delete") continue; // Check if this is something similar with: "delete p;" tok = tok->next(); if (Token::simpleMatch(tok, "[ ]")) tok = tok->tokAt(2); if (!tok || tok == scope->bodyEnd) break; if (!Token::Match(tok, "%var% ;")) continue; // we only look for global variables const Variable *var = tok->variable(); if (!var || !(var->isGlobal() || var->isStatic())) continue; const unsigned int varid(tok->varId()); // Token where throw occurs const Token *throwToken = nullptr; // is there a throw after the deallocation? const Token* const end2 = tok->scope()->bodyEnd; for (const Token *tok2 = tok; tok2 != end2; tok2 = tok2->next()) { // Throw after delete -> Dead pointer if (tok2->str() == "throw") { if (printInconclusive) { // For inconclusive checking, throw directly. deallocThrowError(tok2, tok->str()); break; } throwToken = tok2; } // Variable is assigned -> Bail out else if (Token::Match(tok2, "%varid% =", varid)) { if (throwToken) // For non-inconclusive checking, wait until we find an assignment to it. Otherwise we assume it is safe to leave a dead pointer. deallocThrowError(throwToken, tok2->str()); break; } // Variable passed to function. Assume it becomes assigned -> Bail out else if (Token::Match(tok2, "[,(] &| %varid% [,)]", varid)) // TODO: No bailout if passed by value or as const reference break; } } } } //--------------------------------------------------------------------------- // catch(const exception & err) // { // throw err; // <- should be just "throw;" // } //--------------------------------------------------------------------------- void CheckExceptionSafety::checkRethrowCopy() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eCatch) continue; const unsigned int varid = scope.bodyStart->tokAt(-2)->varId(); if (varid) { for (const Token* tok = scope.bodyStart->next(); tok && tok != scope.bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "catch (") && tok->next()->link() && tok->next()->link()->next()) { // Don't check inner catch - it is handled in another iteration of outer loop. tok = tok->next()->link()->next()->link(); if (!tok) break; } else if (Token::Match(tok, "throw %varid% ;", varid)) rethrowCopyError(tok, tok->strAt(1)); } } } } //--------------------------------------------------------------------------- // try {} catch (std::exception err) {} <- Should be "std::exception& err" //--------------------------------------------------------------------------- void CheckExceptionSafety::checkCatchExceptionByValue() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eCatch) continue; // Find a pass-by-value declaration in the catch(), excluding basic types // e.g. catch (std::exception err) const Variable *var = scope.bodyStart->tokAt(-2)->variable(); if (var && var->isClass() && !var->isPointer() && !var->isReference()) catchExceptionByValueError(scope.classDef); } } static const Token * functionThrowsRecursive(const Function * function, std::set & recursive) { // check for recursion and bail if found if (!recursive.insert(function).second) return nullptr; if (!function->functionScope) return nullptr; for (const Token *tok = function->functionScope->bodyStart->next(); tok != function->functionScope->bodyEnd; tok = tok->next()) { if (tok->str() == "try") { // just bail for now break; } if (tok->str() == "throw") { return tok; } else if (tok->function()) { const Function * called = tok->function(); // check if called function has an exception specification if (called->isThrow() && called->throwArg) { return tok; } else if (called->isNoExcept() && called->noexceptArg && called->noexceptArg->str() != "true") { return tok; } else if (functionThrowsRecursive(called, recursive)) { return tok; } } } return nullptr; } static const Token * functionThrows(const Function * function) { std::set recursive; return functionThrowsRecursive(function, recursive); } //-------------------------------------------------------------------------- // void func() noexcept { throw x; } // void func() throw() { throw x; } // void func() __attribute__((nothrow)); void func() { throw x; } //-------------------------------------------------------------------------- void CheckExceptionSafety::nothrowThrows() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { const Function* function = scope->function; if (!function) continue; // check noexcept and noexcept(true) functions if (function->isNoExcept() && (!function->noexceptArg || function->noexceptArg->str() == "true")) { const Token *throws = functionThrows(function); if (throws) noexceptThrowError(throws); } // check throw() functions else if (function->isThrow() && !function->throwArg) { const Token *throws = functionThrows(function); if (throws) noexceptThrowError(throws); } // check __attribute__((nothrow)) or __declspec(nothrow) functions else if (function->isAttributeNothrow()) { const Token *throws = functionThrows(function); if (throws) noexceptThrowError(throws); } } } //-------------------------------------------------------------------------- // void func() { functionWithExceptionSpecification(); } //-------------------------------------------------------------------------- void CheckExceptionSafety::unhandledExceptionSpecification() { if (!mSettings->isEnabled(Settings::STYLE) || !mSettings->inconclusive) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { // only check functions without exception epecification if (scope->function && !scope->function->isThrow() && scope->className != "main" && scope->className != "wmain" && scope->className != "_tmain" && scope->className != "WinMain") { for (const Token *tok = scope->function->functionScope->bodyStart->next(); tok != scope->function->functionScope->bodyEnd; tok = tok->next()) { if (tok->str() == "try") { break; } else if (tok->function()) { const Function * called = tok->function(); // check if called function has an exception specification if (called->isThrow() && called->throwArg) { unhandledExceptionSpecificationError(tok, called->tokenDef, scope->function->name()); break; } } } } } } cppcheck-1.90/lib/checkexceptionsafety.h000066400000000000000000000163341357737443600204040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkexceptionsafetyH #define checkexceptionsafetyH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errorlogger.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include #include class Settings; // CWE ID used: static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE703(703U); // Improper Check or Handling of Exceptional Conditions /// @addtogroup Checks /// @{ /** * @brief %Check exception safety (exceptions shouldn't cause leaks nor corrupt data) * * The problem with these checks is that Cppcheck can't determine what the valid * values are for variables. But in some cases (dead pointers) it can be determined * that certain variable values are corrupt. */ class CPPCHECKLIB CheckExceptionSafety : public Check { public: /** This constructor is used when registering the CheckClass */ CheckExceptionSafety() : Check(myName()) { } /** This constructor is used when running checks. */ CheckExceptionSafety(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (tokenizer->isC()) return; CheckExceptionSafety checkExceptionSafety(tokenizer, settings, errorLogger); checkExceptionSafety.destructors(); checkExceptionSafety.deallocThrow(); checkExceptionSafety.checkRethrowCopy(); checkExceptionSafety.checkCatchExceptionByValue(); checkExceptionSafety.nothrowThrows(); checkExceptionSafety.unhandledExceptionSpecification(); } /** Don't throw exceptions in destructors */ void destructors(); /** deallocating memory and then throw (dead pointer) */ void deallocThrow(); /** Don't rethrow a copy of the caught exception; use a bare throw instead */ void checkRethrowCopy(); /** @brief %Check for exceptions that are caught by value instead of by reference */ void checkCatchExceptionByValue(); /** @brief %Check for functions that throw that shouldn't */ void nothrowThrows(); /** @brief %Check for unhandled exception specification */ void unhandledExceptionSpecification(); private: /** Don't throw exceptions in destructors */ void destructorsError(const Token * const tok, const std::string &className) { reportError(tok, Severity::warning, "exceptThrowInDestructor", "Class " + className + " is not safe, destructor throws exception\n" "The class " + className + " is not safe because its destructor " "throws an exception. If " + className + " is used and an exception " "is thrown that is caught in an outer scope the program will terminate.", CWE398, false); } void deallocThrowError(const Token * const tok, const std::string &varname) { reportError(tok, Severity::warning, "exceptDeallocThrow", "Exception thrown in invalid state, '" + varname + "' points at deallocated memory.", CWE398, false); } void rethrowCopyError(const Token * const tok, const std::string &varname) { reportError(tok, Severity::style, "exceptRethrowCopy", "Throwing a copy of the caught exception instead of rethrowing the original exception.\n" "Rethrowing an exception with 'throw " + varname + ";' creates an unnecessary copy of '" + varname + "'. " "To rethrow the caught exception without unnecessary copying or slicing, use a bare 'throw;'.", CWE398, false); } void catchExceptionByValueError(const Token *tok) { reportError(tok, Severity::style, "catchExceptionByValue", "Exception should be caught by reference.\n" "The exception is caught by value. It could be caught " "as a (const) reference which is usually recommended in C++.", CWE398, false); } void noexceptThrowError(const Token * const tok) { reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, false); } /** Missing exception specification */ void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) { const std::string str1(tok1 ? tok1->str() : "foo"); const std::list locationList = { tok1, tok2 }; reportError(locationList, Severity::style, "unhandledExceptionSpecification", "Unhandled exception specification when calling function " + str1 + "().\n" "Unhandled exception specification when calling function " + str1 + "(). " "Either use a try/catch around the function call, or add a exception specification for " + funcname + "() also.", CWE703, true); } /** Generate all possible errors (for --errorlist) */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckExceptionSafety c(nullptr, settings, errorLogger); c.destructorsError(nullptr, "Class"); c.deallocThrowError(nullptr, "p"); c.rethrowCopyError(nullptr, "varname"); c.catchExceptionByValueError(nullptr); c.noexceptThrowError(nullptr); c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname"); } /** Short description of class (for --doc) */ static std::string myName() { return "Exception Safety"; } /** wiki formatted description of the class (for --doc) */ std::string classInfo() const OVERRIDE { return "Checking exception safety\n" "- Throwing exceptions in destructors\n" "- Throwing exception during invalid state\n" "- Throwing a copy of a caught exception instead of rethrowing the original exception\n" "- Exception caught by value instead of by reference\n" "- Throwing exception in noexcept, nothrow(), __attribute__((nothrow)) or __declspec(nothrow) function\n" "- Unhandled exception specification when calling function foo()\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkexceptionsafetyH cppcheck-1.90/lib/checkfunctions.cpp000066400000000000000000000546361357737443600175440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Check functions //--------------------------------------------------------------------------- #include "checkfunctions.h" #include "astutils.h" #include "mathlib.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckFunctions instance; } static const CWE CWE252(252U); // Unchecked Return Value static const CWE CWE477(477U); // Use of Obsolete Functions static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const CWE CWE686(686U); // Function Call With Incorrect Argument Type static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument void CheckFunctions::checkProhibitedFunctions() { const bool checkAlloca = mSettings->isEnabled(Settings::WARNING) && ((mSettings->standards.c >= Standards::C99 && mTokenizer->isC()) || mSettings->standards.cpp >= Standards::CPP11); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% (") && tok->varId() == 0) continue; // alloca() is special as it depends on the code being C or C++, so it is not in Library if (checkAlloca && Token::simpleMatch(tok, "alloca (") && (!tok->function() || tok->function()->nestedIn->type == Scope::eGlobal)) { if (mTokenizer->isC()) { if (mSettings->standards.c > Standards::C89) reportError(tok, Severity::warning, "allocaCalled", "$symbol:alloca\n" "Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n" "The obsolete function 'alloca' is called. In C99 and later it is recommended to use a variable length array or " "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons " "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca)."); } else reportError(tok, Severity::warning, "allocaCalled", "$symbol:alloca\n" "Obsolete function 'alloca' called.\n" "The obsolete function 'alloca' is called. In C++11 and later it is recommended to use std::array<> or " "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons " "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca)."); } else { if (tok->function() && tok->function()->hasBody()) continue; const Library::WarnInfo* wi = mSettings->library.getWarnInfo(tok); if (wi) { if (mSettings->isEnabled(wi->severity) && mSettings->standards.c >= wi->standards.c && mSettings->standards.cpp >= wi->standards.cpp) { reportError(tok, wi->severity, tok->str() + "Called", wi->message, CWE477, false); } } } } } } //--------------------------------------------------------------------------- // Check and //--------------------------------------------------------------------------- void CheckFunctions::invalidFunctionUsage() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% ( !!)")) continue; const Token * const functionToken = tok; const std::vector arguments = getArguments(tok); for (int argnr = 1; argnr <= arguments.size(); ++argnr) { const Token * const argtok = arguments[argnr-1]; // check ... const ValueFlow::Value *invalidValue = argtok->getInvalidValue(functionToken,argnr,mSettings); if (invalidValue) { invalidFunctionArgError(argtok, functionToken->next()->astOperand1()->expressionString(), argnr, invalidValue, mSettings->library.validarg(functionToken, argnr)); } if (astIsBool(argtok)) { // check if (mSettings->library.isboolargbad(functionToken, argnr)) invalidFunctionArgBoolError(argtok, functionToken->str(), argnr); // Are the values 0 and 1 valid? else if (!mSettings->library.isIntArgValid(functionToken, argnr, 0)) invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); else if (!mSettings->library.isIntArgValid(functionToken, argnr, 1)) invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); } if (mSettings->library.isargstrz(functionToken, argnr)) { if (Token::Match(argtok, "& %var% !![") && argtok->next() && argtok->next()->valueType()) { const ValueType * valueType = argtok->next()->valueType(); const Variable * variable = argtok->next()->variable(); if (valueType->type == ValueType::Type::CHAR && !variable->isArray() && !variable->isGlobal() && (!argtok->next()->hasKnownValue() || argtok->next()->getValue(0) == nullptr)) { invalidFunctionArgStrError(argtok, functionToken->str(), argnr); } } } } } } } void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; if (invalidValue && invalidValue->condition) errmsg << ValueFlow::eitherTheConditionIsRedundant(invalidValue->condition) << " or $symbol() argument nr " << argnr << " can have invalid value."; else errmsg << "Invalid $symbol() argument nr " << argnr << '.'; if (invalidValue) errmsg << " The value is " << std::setprecision(10) << (invalidValue->isIntValue() ? invalidValue->intvalue : invalidValue->floatValue) << " but the valid values are '" << validstr << "'."; else errmsg << " The value is 0 or 1 (boolean) but the valid values are '" << validstr << "'."; if (invalidValue) reportError(getErrorPath(tok, invalidValue, "Invalid argument"), invalidValue->errorSeverity() ? Severity::error : Severity::warning, "invalidFunctionArg", errmsg.str(), CWE628, invalidValue->isInconclusive()); else reportError(tok, Severity::error, "invalidFunctionArg", errmsg.str(), CWE628, false); } void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; errmsg << "Invalid $symbol() argument nr " << argnr << ". A non-boolean value is required."; reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, false); } void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; errmsg << "Invalid $symbol() argument nr " << argnr << ". A nul-terminated string is required."; reportError(tok, Severity::error, "invalidFunctionArgStr", errmsg.str(), CWE628, false); } //--------------------------------------------------------------------------- // Check for ignored return values. //--------------------------------------------------------------------------- void CheckFunctions::checkIgnoredReturnValue() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // skip c++11 initialization, ({...}) if (Token::Match(tok, "%var%|(|,|return {")) tok = tok->linkAt(1); else if (Token::Match(tok, "[(<]") && tok->link()) tok = tok->link(); if (tok->varId() || !Token::Match(tok, "%name% (")) continue; if (tok->next()->astParent()) continue; if (!tok->scope()->isExecutable()) { tok = tok->scope()->bodyEnd; continue; } if ((!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && (mSettings->library.isUseRetVal(tok) || (tok->function() && tok->function()->isAttributeNodiscard())) && !WRONG_DATA(!tok->next()->astOperand1(), tok)) { ignoredReturnValueError(tok, tok->next()->astOperand1()->expressionString()); } } } } void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function) { reportError(tok, Severity::warning, "ignoredReturnValue", "$symbol:" + function + "\nReturn value of function $symbol() is not used.", CWE252, false); } //--------------------------------------------------------------------------- // Detect passing wrong values to functions like atan(0, x); //--------------------------------------------------------------------------- void CheckFunctions::checkMathFunctions() { const bool styleC99 = mSettings->isEnabled(Settings::STYLE) && mSettings->standards.c != Standards::C89 && mSettings->standards.cpp != Standards::CPP03; const bool printWarnings = mSettings->isEnabled(Settings::WARNING); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->varId()) continue; if (printWarnings && Token::Match(tok, "%name% ( !!)")) { if (tok->strAt(-1) != "." && Token::Match(tok, "log|logf|logl|log10|log10f|log10l|log2|log2f|log2l ( %num% )")) { const std::string& number = tok->strAt(2); if ((MathLib::isInt(number) && MathLib::toLongNumber(number) <= 0) || (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= 0.)) mathfunctionCallWarning(tok); } else if (Token::Match(tok, "log1p|log1pf|log1pl ( %num% )")) { const std::string& number = tok->strAt(2); if ((MathLib::isInt(number) && MathLib::toLongNumber(number) <= -1) || (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= -1.)) mathfunctionCallWarning(tok); } // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined else if (Token::Match(tok, "atan2|atan2f|atan2l ( %num% , %num% )")) { if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNullValue(tok->strAt(4))) mathfunctionCallWarning(tok, 2); } // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). else if (Token::Match(tok, "fmod|fmodf|fmodl (")) { const Token* nextArg = tok->tokAt(2)->nextArgument(); if (nextArg && nextArg->isNumber() && MathLib::isNullValue(nextArg->str())) mathfunctionCallWarning(tok, 2); } // pow ( x , y) If x is zero, and y is negative --> division by zero else if (Token::Match(tok, "pow|powf|powl ( %num% , %num% )")) { if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNegative(tok->strAt(4))) mathfunctionCallWarning(tok, 2); } } if (styleC99) { if (Token::Match(tok, "%num% - erf (") && Tokenizer::isOneNumber(tok->str()) && tok->next()->astOperand2() == tok->tokAt(3)) { mathfunctionCallWarning(tok, "1 - erf(x)", "erfc(x)"); } else if (Token::simpleMatch(tok, "exp (") && Token::Match(tok->linkAt(1), ") - %num%") && Tokenizer::isOneNumber(tok->linkAt(1)->strAt(2)) && tok->linkAt(1)->next()->astOperand1() == tok->next()) { mathfunctionCallWarning(tok, "exp(x) - 1", "expm1(x)"); } else if (Token::simpleMatch(tok, "log (") && tok->next()->astOperand2()) { const Token* plus = tok->next()->astOperand2(); if (plus->str() == "+" && ((plus->astOperand1() && Tokenizer::isOneNumber(plus->astOperand1()->str())) || (plus->astOperand2() && Tokenizer::isOneNumber(plus->astOperand2()->str())))) mathfunctionCallWarning(tok, "log(1 + x)", "log1p(x)"); } } } } } void CheckFunctions::mathfunctionCallWarning(const Token *tok, const nonneg int numParam) { if (tok) { if (numParam == 1) reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing value " + tok->strAt(2) + " to $symbol() leads to implementation-defined result.", CWE758, false); else if (numParam == 2) reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to $symbol() leads to implementation-defined result.", CWE758, false); } else reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.", CWE758, false); } void CheckFunctions::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp) { reportError(tok, Severity::style, "unpreciseMathCall", "Expression '" + oldexp + "' can be replaced by '" + newexp + "' to avoid loss of precision.", CWE758, false); } //--------------------------------------------------------------------------- // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted //--------------------------------------------------------------------------- void CheckFunctions::memsetZeroBytes() { // FIXME: // Replace this with library configuration. // For instance: // // // if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "memset|wmemset (") && (numberOfArguments(tok)==3)) { const std::vector &arguments = getArguments(tok); if (WRONG_DATA(arguments.size() != 3U, tok)) continue; const Token* lastParamTok = arguments[2]; if (lastParamTok->str() == "0") memsetZeroBytesError(tok); } } } } void CheckFunctions::memsetZeroBytesError(const Token *tok) { const std::string summary("memset() called to fill 0 bytes."); const std::string verbose(summary + " The second and third arguments might be inverted." " The function memset ( void * ptr, int value, size_t num ) sets the" " first num bytes of the block of memory pointed by ptr to the specified value."); reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose, CWE687, false); } void CheckFunctions::memsetInvalid2ndParam() { // FIXME: // Replace this with library configuration. // For instance: // // // // const bool printPortability = mSettings->isEnabled(Settings::PORTABILITY); const bool printWarning = mSettings->isEnabled(Settings::WARNING); if (!printWarning && !printPortability) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { if (!Token::simpleMatch(tok, "memset (")) continue; const std::vector args = getArguments(tok); if (args.size() != 3) continue; // Second parameter is zero literal, i.e. 0.0f const Token * const secondParamTok = args[1]; if (Token::Match(secondParamTok, "%num% ,") && MathLib::isNullValue(secondParamTok->str())) continue; // Check if second parameter is a float variable or a float literal != 0.0f if (printPortability && astIsFloat(secondParamTok,false)) { memsetFloatError(secondParamTok, secondParamTok->expressionString()); } if (printWarning && secondParamTok->isNumber()) { // Check if the second parameter is a literal and is out of range const long long int value = MathLib::toLongNumber(secondParamTok->str()); const long long sCharMin = mSettings->signedCharMin(); const long long uCharMax = mSettings->unsignedCharMax(); if (value < sCharMin || value > uCharMax) memsetValueOutOfRangeError(secondParamTok, secondParamTok->str()); } } } } void CheckFunctions::memsetFloatError(const Token *tok, const std::string &var_value) { const std::string message("The 2nd memset() argument '" + var_value + "' is a float, its representation is implementation defined."); const std::string verbose(message + " memset() is used to set each byte of a block of memory to a specific value and" " the actual representation of a floating-point value is implementation defined."); reportError(tok, Severity::portability, "memsetFloat", message + "\n" + verbose, CWE688, false); } void CheckFunctions::memsetValueOutOfRangeError(const Token *tok, const std::string &value) { const std::string message("The 2nd memset() argument '" + value + "' doesn't fit into an 'unsigned char'."); const std::string verbose(message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value."); reportError(tok, Severity::warning, "memsetValueOutOfRange", message + "\n" + verbose, CWE686, false); } //--------------------------------------------------------------------------- // --check-library => warn for unconfigured functions //--------------------------------------------------------------------------- void CheckFunctions::checkLibraryMatchFunctions() { if (!mSettings->checkLibrary || !mSettings->isEnabled(Settings::INFORMATION)) return; bool insideNew = false; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->scope() || !tok->scope()->isExecutable()) continue; if (tok->str() == "new") insideNew = true; else if (tok->str() == ";") insideNew = false; else if (insideNew) continue; if (!Token::Match(tok, "%name% (") || Token::Match(tok, "asm|sizeof|catch")) continue; if (tok->varId() != 0 || tok->type() || tok->isStandardType() || tok->isControlFlowKeyword()) continue; if (tok->linkAt(1)->strAt(1) == "(") continue; if (!mSettings->library.isNotLibraryFunction(tok)) continue; const std::string &functionName = mSettings->library.getFunctionName(tok); if (functionName.empty() || mSettings->library.functions.find(functionName) != mSettings->library.functions.end()) continue; reportError(tok, Severity::information, "checkLibraryFunction", "--check-library: There is no matching configuration for function " + functionName + "()"); } } cppcheck-1.90/lib/checkfunctions.h000066400000000000000000000132611357737443600171760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkfunctionsH #define checkfunctionsH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errorlogger.h" #include "library.h" #include "settings.h" #include #include #include class Token; class Tokenizer; namespace ValueFlow { class Value; } // namespace ValueFlow /// @addtogroup Checks /// @{ /** * @brief Check for bad function usage */ class CPPCHECKLIB CheckFunctions : public Check { public: /** This constructor is used when registering the CheckFunctions */ CheckFunctions() : Check(myName()) { } /** This constructor is used when running checks. */ CheckFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckFunctions checkFunctions(tokenizer, settings, errorLogger); // Checks checkFunctions.checkIgnoredReturnValue(); // --check-library : functions with nonmatching configuration checkFunctions.checkLibraryMatchFunctions(); checkFunctions.checkProhibitedFunctions(); checkFunctions.invalidFunctionUsage(); checkFunctions.checkMathFunctions(); checkFunctions.memsetZeroBytes(); checkFunctions.memsetInvalid2ndParam(); } /** Check for functions that should not be used */ void checkProhibitedFunctions(); /** * @brief Invalid function usage (invalid input value / overlapping data) * * %Check that given function parameters are valid according to the standard * - wrong radix given for strtol/strtoul * - overlapping data when using sprintf/snprintf * - wrong input value according to library */ void invalidFunctionUsage(); /** @brief %Check for ignored return values. */ void checkIgnoredReturnValue(); /** @brief %Check for parameters given to math function that do not make sense*/ void checkMathFunctions(); /** @brief %Check for filling zero bytes with memset() */ void memsetZeroBytes(); /** @brief %Check for invalid 2nd parameter of memset() */ void memsetInvalid2ndParam(); /** @brief --check-library: warn for unconfigured function calls */ void checkLibraryMatchFunctions(); private: void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr); void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr); void invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr); void ignoredReturnValueError(const Token* tok, const std::string& function); void mathfunctionCallWarning(const Token *tok, const nonneg int numParam = 1); void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp); void memsetZeroBytesError(const Token *tok); void memsetFloatError(const Token *tok, const std::string &var_value); void memsetValueOutOfRangeError(const Token *tok, const std::string &value); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckFunctions c(nullptr, settings, errorLogger); for (std::map::const_iterator i = settings->library.functionwarn.cbegin(); i != settings->library.functionwarn.cend(); ++i) { c.reportError(nullptr, Severity::style, i->first+"Called", i->second.message); } c.invalidFunctionArgError(nullptr, "func_name", 1, nullptr,"1:4"); c.invalidFunctionArgBoolError(nullptr, "func_name", 1); c.invalidFunctionArgStrError(nullptr, "func_name", 1); c.ignoredReturnValueError(nullptr, "malloc"); c.mathfunctionCallWarning(nullptr); c.mathfunctionCallWarning(nullptr, "1 - erf(x)", "erfc(x)"); c.memsetZeroBytesError(nullptr); c.memsetFloatError(nullptr, "varname"); c.memsetValueOutOfRangeError(nullptr, "varname"); } static std::string myName() { return "Check function usage"; } std::string classInfo() const OVERRIDE { return "Check function usage:\n" "- return value of certain functions not used\n" "- invalid input values for functions\n" "- Warn if a function is called whose usage is discouraged\n" "- memset() third argument is zero\n" "- memset() with a value out of range as the 2nd parameter\n" "- memset() with a float as the 2nd parameter\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkfunctionsH cppcheck-1.90/lib/checkinternal.cpp000066400000000000000000000424061357737443600173400ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef CHECK_INTERNAL #include "checkinternal.h" #include "astutils.h" #include "symboldatabase.h" #include "utils.h" #include #include // Register this check class (by creating a static instance of it). // Disabled in release builds namespace { CheckInternal instance; } void CheckInternal::checkTokenMatchPatterns() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) continue; const std::string& funcname = tok->strAt(2); // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); if (pattern.empty()) { simplePatternError(tok, pattern, funcname); continue; } if (pattern.find("||") != std::string::npos || pattern.find(" | ") != std::string::npos || pattern[0] == '|' || (pattern[pattern.length() - 1] == '|' && pattern[pattern.length() - 2] == ' ')) orInComplexPattern(tok, pattern, funcname); // Check for signs of complex patterns if (pattern.find_first_of("[|") != std::string::npos) continue; else if (pattern.find("!!") != std::string::npos) continue; bool complex = false; size_t index = pattern.find('%'); while (index != std::string::npos) { if (pattern.length() <= index + 2) { complex = true; break; } if (pattern[index + 1] == 'o' && pattern[index + 2] == 'r') // %or% or %oror% index = pattern.find('%', index + 1); else { complex = true; break; } index = pattern.find('%', index + 1); } if (!complex) simplePatternError(tok, pattern, funcname); } } } void CheckInternal::checkRedundantTokCheck() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "&& Token :: simpleMatch|Match|findsimplematch|findmatch (")) { // in code like // if (tok->previous() && Token::match(tok->previous(), "bla")) {} // the first tok->previous() check is redundant const Token *astOp1 = tok->astOperand1(); const Token *astOp2 = getArguments(tok->tokAt(3))[0]; if (Token::simpleMatch(astOp1, "&&")) { astOp1 = astOp1->astOperand2(); } if (astOp1->expressionString() == astOp2->expressionString()) { checkRedundantTokCheckError(astOp2); } // if (!tok || !Token::match(tok, "foo")) } else if (Token::Match(tok, "%oror% ! Token :: simpleMatch|Match|findsimplematch|findmatch (")) { const Token *negTok = tok->next()->astParent()->astOperand1(); if (Token::simpleMatch(negTok, "||")) { negTok = negTok->astOperand2(); } // the first tok condition is negated if (Token::simpleMatch(negTok, "!")) { const Token *astOp1 = negTok->astOperand1(); const Token *astOp2 = getArguments(tok->tokAt(4))[0]; if (astOp1->expressionString() == astOp2->expressionString()) { checkRedundantTokCheckError(astOp2); } } } } } void CheckInternal::checkRedundantTokCheckError(const Token* tok) { reportError(tok, Severity::style, "redundantTokCheck", "Unnecessary check of \"" + (tok? tok->expressionString(): emptyString) + "\", match-function already checks if it is null."); } void CheckInternal::checkTokenSimpleMatchPatterns() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: simpleMatch (") && !Token::simpleMatch(tok, "Token :: findsimplematch (")) continue; const std::string& funcname = tok->strAt(2); // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); if (pattern.empty()) { complexPatternError(tok, pattern, funcname); continue; } // Check for [xyz] usage - but exclude standalone square brackets unsigned int char_count = 0; for (std::string::size_type pos = 0; pos < pattern.size(); ++pos) { char c = pattern[pos]; if (c == ' ') { char_count = 0; } else if (c == ']') { if (char_count > 0) { complexPatternError(tok, pattern, funcname); continue; } } else { ++char_count; } } // Check | usage: Count characters before the symbol char_count = 0; for (std::string::size_type pos = 0; pos < pattern.size(); ++pos) { const char c = pattern[pos]; if (c == ' ') { char_count = 0; } else if (c == '|') { if (char_count > 0) { complexPatternError(tok, pattern, funcname); continue; } } else { ++char_count; } } // Check for real errors if (pattern.length() > 1) { for (size_t j = 0; j < pattern.length() - 1; j++) { if (pattern[j] == '%' && pattern[j + 1] != ' ') complexPatternError(tok, pattern, funcname); else if (pattern[j] == '!' && pattern[j + 1] == '!') complexPatternError(tok, pattern, funcname); } } } } } namespace { const std::set knownPatterns = { "%any%" , "%assign%" , "%bool%" , "%char%" , "%comp%" , "%num%" , "%op%" , "%cop%" , "%or%" , "%oror%" , "%str%" , "%type%" , "%name%" , "%var%" , "%varid%" }; } void CheckInternal::checkMissingPercentCharacter() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) continue; const std::string& funcname = tok->strAt(2); // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); std::set::const_iterator knownPattern, knownPatternsEnd = knownPatterns.end(); for (knownPattern = knownPatterns.begin(); knownPattern != knownPatternsEnd; ++knownPattern) { const std::string brokenPattern = (*knownPattern).substr(0, (*knownPattern).size() - 1); std::string::size_type pos = 0; while ((pos = pattern.find(brokenPattern, pos)) != std::string::npos) { // Check if it's the full pattern if (pattern.find(*knownPattern, pos) != pos) { // Known whitelist of substrings if ((brokenPattern == "%var" && pattern.find("%varid%", pos) == pos) || (brokenPattern == "%or" && pattern.find("%oror%", pos) == pos)) { ++pos; continue; } missingPercentCharacterError(tok, pattern, funcname); } ++pos; } } } } } void CheckInternal::checkUnknownPattern() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) continue; // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); bool inBrackets = false; for (std::string::size_type j = 0; j < pattern.length() - 1; j++) { if (pattern[j] == '[' && (j == 0 || pattern[j - 1] == ' ')) inBrackets = true; else if (pattern[j] == ']') inBrackets = false; else if (pattern[j] == '%' && pattern[j + 1] != ' ' && pattern[j + 1] != '|' && !inBrackets) { const std::string::size_type end = pattern.find('%', j + 1); if (end != std::string::npos) { const std::string s = pattern.substr(j, end - j + 1); if (knownPatterns.find(s) == knownPatterns.end()) unknownPatternError(tok, s); } } } } } } void CheckInternal::checkRedundantNextPrevious() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() != ".") continue; tok = tok->next(); if (Token::Match(tok, "previous ( ) . next|tokAt|strAt|linkAt (") || Token::Match(tok, "next ( ) . previous|tokAt|strAt|linkAt (") || (Token::simpleMatch(tok, "tokAt (") && Token::Match(tok->linkAt(1), ") . previous|next|tokAt|strAt|linkAt|str|link ("))) { const std::string& func1 = tok->str(); const std::string& func2 = tok->linkAt(1)->strAt(2); if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->linkAt(1)->strAt(4) != ")") continue; redundantNextPreviousError(tok, func1, func2); } else if (Token::Match(tok, "next|previous ( ) . next|previous ( ) . next|previous|linkAt|strAt|link|str (")) { const std::string& func1 = tok->str(); const std::string& func2 = tok->strAt(8); if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->strAt(10) != ")") continue; redundantNextPreviousError(tok, func1, func2); } } } } void CheckInternal::checkExtraWhitespace() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "Token :: simpleMatch|findsimplematch|Match|findmatch (")) continue; const std::string& funcname = tok->strAt(2); // Get pattern string const Token *patternTok = tok->tokAt(4)->nextArgument(); if (!patternTok || patternTok->tokType() != Token::eString) continue; const std::string pattern = patternTok->strValue(); if (!pattern.empty() && (pattern[0] == ' ' || *pattern.rbegin() == ' ')) extraWhitespaceError(tok, pattern, funcname); // two whitespaces or more if (pattern.find(" ") != std::string::npos) extraWhitespaceError(tok, pattern, funcname); } } } void CheckInternal::checkStlUsage() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, ". emplace (")) reportError(tok, Severity::error, "internalStlUsage", "The 'emplace' function shall be avoided for now. It is not available e.g. in Slackware 14.0. 'emplace_back' is fine."); //if (Token::simpleMatch(tok, ". back ( )") && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->container && Token::simpleMatch(tok->astOperand1()->valueType()->container, "std :: string")) // reportError(tok, Severity::error, "internalStlUsage", "The 'std::string::back()' function shall be avoided for now."); } } } void CheckInternal::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "multiComparePatternError", "Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%name%,%oror%) inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::warning, "simplePatternError", "Found simple pattern inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } void CheckInternal::complexPatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "complexPatternError", "Found complex pattern inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } void CheckInternal::missingPercentCharacterError(const Token* tok, const std::string& pattern, const std::string& funcname) { reportError(tok, Severity::error, "missingPercentCharacter", "Missing percent end character in Token::" + funcname + "() pattern: \"" + pattern + "\"" ); } void CheckInternal::unknownPatternError(const Token* tok, const std::string& pattern) { reportError(tok, Severity::error, "unknownPattern", "Unknown pattern used: \"" + pattern + "\""); } void CheckInternal::redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2) { reportError(tok, Severity::style, "redundantNextPrevious", "Call to 'Token::" + func1 + "()' followed by 'Token::" + func2 + "()' can be simplified."); } void CheckInternal::orInComplexPattern(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "orInComplexPattern", "Token::" + funcname + "() pattern \"" + pattern + "\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\"."); } void CheckInternal::extraWhitespaceError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::warning, "extraWhitespaceError", "Found extra whitespace inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } #endif // #ifdef CHECK_INTERNAL cppcheck-1.90/lib/checkinternal.h000066400000000000000000000121441357737443600170010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkinternalH #define checkinternalH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include /// @addtogroup Checks /// @{ /** @brief %Check Internal cppcheck API usage */ class CPPCHECKLIB CheckInternal : public Check { public: /** This constructor is used when registering the CheckClass */ CheckInternal() : Check(myName()) { } /** This constructor is used when running checks. */ CheckInternal(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (!settings->isEnabled(Settings::INTERNAL)) return; CheckInternal checkInternal(tokenizer, settings, errorLogger); checkInternal.checkTokenMatchPatterns(); checkInternal.checkTokenSimpleMatchPatterns(); checkInternal.checkMissingPercentCharacter(); checkInternal.checkUnknownPattern(); checkInternal.checkRedundantNextPrevious(); checkInternal.checkExtraWhitespace(); checkInternal.checkRedundantTokCheck(); checkInternal.checkStlUsage(); } /** @brief %Check if a simple pattern is used inside Token::Match or Token::findmatch */ void checkTokenMatchPatterns(); /** @brief %Check if a complex pattern is used inside Token::simpleMatch or Token::findsimplematch */ void checkTokenSimpleMatchPatterns(); /** @brief %Check for missing % end character in Token::Match pattern */ void checkMissingPercentCharacter(); /** @brief %Check for unknown (invalid) complex patterns like "%typ%" */ void checkUnknownPattern(); /** @brief %Check for inefficient usage of Token::next(), Token::previous() and Token::tokAt() */ void checkRedundantNextPrevious(); /** @brief %Check if there is whitespace at the beginning or at the end of a pattern */ void checkExtraWhitespace(); /** @brief %Check if there is a redundant check for none-nullness of parameter before Match functions, such as (tok && Token::Match(tok, "foo")) */ void checkRedundantTokCheck(); /** @brief Try to avoid some new functions that are not fully supported in Linux */ void checkStlUsage(); private: void multiComparePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname); void unknownPatternError(const Token* tok, const std::string& pattern); void redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2); void orInComplexPattern(const Token *tok, const std::string &pattern, const std::string &funcname); void extraWhitespaceError(const Token *tok, const std::string &pattern, const std::string &funcname); void checkRedundantTokCheckError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckInternal c(nullptr, settings, errorLogger); c.multiComparePatternError(nullptr, ";|%type%", "Match"); c.simplePatternError(nullptr, "class {", "Match"); c.complexPatternError(nullptr, "%type% ( )", "Match"); c.missingPercentCharacterError(nullptr, "%num", "Match"); c.unknownPatternError(nullptr, "%typ"); c.redundantNextPreviousError(nullptr, "previous", "next"); c.orInComplexPattern(nullptr, "||", "Match"); c.extraWhitespaceError(nullptr, "%str% ", "Match"); c.checkRedundantTokCheckError(nullptr); } static std::string myName() { return "cppcheck internal API usage"; } std::string classInfo() const OVERRIDE { // Don't include these checks on the WIKI where people can read what // checks there are. These checks are not intended for users. return ""; } }; /// @} //--------------------------------------------------------------------------- #endif // checkinternalH cppcheck-1.90/lib/checkio.cpp000066400000000000000000003254641357737443600161430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkio.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include #include //--------------------------------------------------------------------------- // Register CheckIO.. namespace { CheckIO instance; } // CVE ID used: static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime static const CWE CWE685(685U); // Function Call With Incorrect Number of Arguments static const CWE CWE686(686U); // Function Call With Incorrect Argument Type static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value static const CWE CWE704(704U); // Incorrect Type Conversion or Cast static const CWE CWE910(910U); // Use of Expired File Descriptor //--------------------------------------------------------------------------- // std::cout << std::cout; //--------------------------------------------------------------------------- void CheckIO::checkCoutCerrMisusage() { if (mTokenizer->isC()) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "std :: cout|cerr !!.") && tok->next()->astParent() && tok->next()->astParent()->astOperand1() == tok->next()) { const Token* tok2 = tok->next(); while (tok2->astParent() && tok2->astParent()->str() == "<<") { tok2 = tok2->astParent(); if (tok2->astOperand2() && Token::Match(tok2->astOperand2()->previous(), "std :: cout|cerr")) coutCerrMisusageError(tok, tok2->astOperand2()->strAt(1)); } } } } } void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName) { reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.", CWE398, false); } //--------------------------------------------------------------------------- // fflush(stdin) <- fflush only applies to output streams in ANSI C // fread(); fwrite(); <- consecutive read/write statements require repositioning in between // fopen("","r"); fwrite(); <- write to read-only file (or vice versa) // fclose(); fread(); <- Use closed file //--------------------------------------------------------------------------- enum OpenMode { CLOSED, READ_MODE, WRITE_MODE, RW_MODE, UNKNOWN_OM }; static OpenMode getMode(const std::string& str) { if (str.find('+', 1) != std::string::npos) return RW_MODE; else if (str.find('w') != std::string::npos || str.find('a') != std::string::npos) return WRITE_MODE; else if (str.find('r') != std::string::npos) return READ_MODE; return UNKNOWN_OM; } struct Filepointer { OpenMode mode; nonneg int mode_indent; enum Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation; nonneg int op_indent; enum AppendMode { UNKNOWN_AM, APPEND, APPEND_EX }; AppendMode append_mode; explicit Filepointer(OpenMode mode_ = UNKNOWN_OM) : mode(mode_), mode_indent(0), lastOperation(NONE), op_indent(0), append_mode(UNKNOWN_AM) { } }; namespace { const std::set whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" }; } void CheckIO::checkFileUsage() { const bool windows = mSettings->isWindowsPlatform(); const bool printPortability = mSettings->isEnabled(Settings::PORTABILITY); const bool printWarnings = mSettings->isEnabled(Settings::WARNING); std::map filepointers; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->declarationId() || var->isArray() || !Token::simpleMatch(var->typeStartToken(), "FILE *")) continue; if (var->isLocal()) { if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor" filepointers.insert(std::make_pair(var->declarationId(), Filepointer(UNKNOWN_OM))); else filepointers.insert(std::make_pair(var->declarationId(), Filepointer(CLOSED))); } else { filepointers.insert(std::make_pair(var->declarationId(), Filepointer(UNKNOWN_OM))); // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode } } for (const Scope * scope : symbolDatabase->functionScopes) { int indent = 0; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "{") indent++; else if (tok->str() == "}") { indent--; for (std::map::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { if (indent < i->second.mode_indent) { i->second.mode_indent = 0; i->second.mode = UNKNOWN_OM; } if (indent < i->second.op_indent) { i->second.op_indent = 0; i->second.lastOperation = Filepointer::UNKNOWN_OP; } } } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break" || mSettings->library.isnoreturn(tok)) { // Reset upon return, continue or break for (std::map::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { i->second.mode_indent = 0; i->second.mode = UNKNOWN_OM; i->second.op_indent = 0; i->second.lastOperation = Filepointer::UNKNOWN_OP; } } else if (Token::Match(tok, "%var% =") && (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile" && (windows ? (tok->str() != "_wfopen" && tok->str() != "_wfreopen") : true))) { std::map::iterator i = filepointers.find(tok->varId()); if (i != filepointers.end()) { i->second.mode = UNKNOWN_OM; i->second.lastOperation = Filepointer::UNKNOWN_OP; } } else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) { std::string mode; const Token* fileTok = nullptr; Filepointer::Operation operation = Filepointer::NONE; if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" || (windows && (tok->str() == "_wfopen" || tok->str() == "_wfreopen"))) && tok->strAt(-1) == "=") { if (tok->str() != "tmpfile") { const Token* modeTok = tok->tokAt(2)->nextArgument(); if (modeTok && modeTok->tokType() == Token::eString) mode = modeTok->strValue(); } else mode = "wb+"; fileTok = tok->tokAt(-2); operation = Filepointer::OPEN; } else if (windows && Token::Match(tok, "fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %name%")) { const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument(); if (modeTok && modeTok->tokType() == Token::eString) mode = modeTok->strValue(); fileTok = tok->tokAt(3); operation = Filepointer::OPEN; } else if ((tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") || (windows && tok->str() == "_fseeki64")) { fileTok = tok->tokAt(2); if (printPortability && fileTok && tok->str() == "fflush") { if (fileTok->str() == "stdin") fflushOnInputStreamError(tok, fileTok->str()); else { const Filepointer& f = filepointers[fileTok->varId()]; if (f.mode == READ_MODE) fflushOnInputStreamError(tok, fileTok->str()); } } operation = Filepointer::POSITIONING; } else if (tok->str() == "fgetc" || tok->str() == "fgetwc" || tok->str() == "fgets" || tok->str() == "fgetws" || tok->str() == "fread" || tok->str() == "fscanf" || tok->str() == "fwscanf" || tok->str() == "getc" || (windows && (tok->str() == "fscanf_s" || tok->str() == "fwscanf_s"))) { if (tok->str().find("scanf") != std::string::npos) fileTok = tok->tokAt(2); else fileTok = tok->linkAt(1)->previous(); operation = Filepointer::READ; } else if (tok->str() == "fputc" || tok->str() == "fputwc" || tok->str() == "fputs" || tok->str() == "fputws" || tok->str() == "fwrite" || tok->str() == "fprintf" || tok->str() == "fwprintf" || tok->str() == "putcc" || (windows && (tok->str() == "fprintf_s" || tok->str() == "fwprintf_s"))) { if (tok->str().find("printf") != std::string::npos) fileTok = tok->tokAt(2); else fileTok = tok->linkAt(1)->previous(); operation = Filepointer::WRITE; } else if (tok->str() == "fclose") { fileTok = tok->tokAt(2); operation = Filepointer::CLOSE; } else if (whitelist.find(tok->str()) != whitelist.end()) { fileTok = tok->tokAt(2); if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok) fileTok = fileTok->nextArgument(); operation = Filepointer::UNIMPORTANT; } else if (!Token::Match(tok, "if|for|while|catch|switch") && !mSettings->library.isFunctionConst(tok->str(), true)) { const Token* const end2 = tok->linkAt(1); if (scope->functionOf && scope->functionOf->isClassOrStruct() && !scope->function->isStatic() && ((tok->strAt(-1) != "::" && tok->strAt(-1) != ".") || tok->strAt(-2) == "this")) { if (!tok->function() || (tok->function()->nestedIn && tok->function()->nestedIn->isClassOrStruct())) { for (std::map::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { const Variable* var = symbolDatabase->getVariableFromVarId(i->first); if (!var || !(var->isLocal() || var->isGlobal() || var->isStatic())) { i->second.mode = UNKNOWN_OM; i->second.mode_indent = 0; i->second.op_indent = indent; i->second.lastOperation = Filepointer::UNKNOWN_OP; } } continue; } } for (const Token* tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { if (tok2->varId() && filepointers.find(tok2->varId()) != filepointers.end()) { fileTok = tok2; operation = Filepointer::UNKNOWN_OP; // Assume that repositioning was last operation and that the file is opened now break; } } } while (Token::Match(fileTok, "%name% .")) fileTok = fileTok->tokAt(2); if (!fileTok || !fileTok->varId() || fileTok->strAt(1) == "[") continue; if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(UNKNOWN_OM))); } Filepointer& f = filepointers[fileTok->varId()]; switch (operation) { case Filepointer::OPEN: f.mode = getMode(mode); if (mode.find('a') != std::string::npos) { if (f.mode == RW_MODE) f.append_mode = Filepointer::APPEND_EX; else f.append_mode = Filepointer::APPEND; } else f.append_mode = Filepointer::UNKNOWN_AM; f.mode_indent = indent; break; case Filepointer::POSITIONING: if (f.mode == CLOSED) useClosedFileError(tok); else if (f.append_mode == Filepointer::APPEND && tok->str() != "fflush" && printWarnings) seekOnAppendedFileError(tok); break; case Filepointer::READ: if (f.mode == CLOSED) useClosedFileError(tok); else if (f.mode == WRITE_MODE) readWriteOnlyFileError(tok); else if (f.lastOperation == Filepointer::WRITE) ioWithoutPositioningError(tok); break; case Filepointer::WRITE: if (f.mode == CLOSED) useClosedFileError(tok); else if (f.mode == READ_MODE) writeReadOnlyFileError(tok); else if (f.lastOperation == Filepointer::READ) ioWithoutPositioningError(tok); break; case Filepointer::CLOSE: if (f.mode == CLOSED) useClosedFileError(tok); else f.mode = CLOSED; f.mode_indent = indent; break; case Filepointer::UNIMPORTANT: if (f.mode == CLOSED) useClosedFileError(tok); break; case Filepointer::UNKNOWN_OP: f.mode = UNKNOWN_OM; f.mode_indent = 0; break; default: break; } if (operation != Filepointer::NONE && operation != Filepointer::UNIMPORTANT) { f.op_indent = indent; f.lastOperation = operation; } } } for (std::map::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { i->second.op_indent = 0; i->second.mode = UNKNOWN_OM; i->second.lastOperation = Filepointer::UNKNOWN_OP; } } } void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname) { reportError(tok, Severity::portability, "fflushOnInputStream", "fflush() called on input stream '" + varname + "' may result in undefined behaviour on non-linux systems.", CWE398, false); } void CheckIO::ioWithoutPositioningError(const Token *tok) { reportError(tok, Severity::error, "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.", CWE664, false); } void CheckIO::readWriteOnlyFileError(const Token *tok) { reportError(tok, Severity::error, "readWriteOnlyFile", "Read operation on a file that was opened only for writing.", CWE664, false); } void CheckIO::writeReadOnlyFileError(const Token *tok) { reportError(tok, Severity::error, "writeReadOnlyFile", "Write operation on a file that was opened only for reading.", CWE664, false); } void CheckIO::useClosedFileError(const Token *tok) { reportError(tok, Severity::error, "useClosedFile", "Used file that is not opened.", CWE910, false); } void CheckIO::seekOnAppendedFileError(const Token *tok) { reportError(tok, Severity::warning, "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, false); } //--------------------------------------------------------------------------- // scanf without field width limits can crash with huge input data //--------------------------------------------------------------------------- void CheckIO::invalidScanf() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const Token *formatToken = nullptr; if (Token::Match(tok, "scanf|vscanf ( %str% ,")) formatToken = tok->tokAt(2); else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) { const Token* nextArg = tok->tokAt(2)->nextArgument(); if (nextArg && nextArg->tokType() == Token::eString) formatToken = nextArg; else continue; } else continue; bool format = false; // scan the string backwards, so we do not need to keep states const std::string &formatstr(formatToken->str()); for (std::size_t i = 1; i < formatstr.length(); i++) { if (formatstr[i] == '%') format = !format; else if (!format) continue; else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') { format = false; } else if (std::isalpha((unsigned char)formatstr[i]) || formatstr[i] == '[') { if (formatstr[i] == 's' || formatstr[i] == '[' || formatstr[i] == 'S' || (formatstr[i] == 'l' && formatstr[i+1] == 's')) // #3490 - field width limits are only necessary for string input invalidScanfError(tok); format = false; } } } } } void CheckIO::invalidScanfError(const Token *tok) { const std::string fname = (tok ? tok->str() : std::string("scanf")); reportError(tok, Severity::warning, "invalidscanf", fname + "() without field width limits can crash with huge input data.\n" + fname + "() without field width limits can crash with huge input data. Add a field width " "specifier to fix this problem.\n" "\n" "Sample program that can crash:\n" "\n" "#include \n" "int main()\n" "{\n" " char c[5];\n" " scanf(\"%s\", c);\n" " return 0;\n" "}\n" "\n" "Typing in 5 or more characters may make the program crash. The correct usage " "here is 'scanf(\"%4s\", c);', as the maximum field width does not include the " "terminating null byte.\n" "Source: http://linux.die.net/man/3/scanf\n" "Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c", CWE119, false); } //--------------------------------------------------------------------------- // printf("%u", "xyz"); // Wrong argument type // printf("%u%s", 1); // Too few arguments // printf("", 1); // Too much arguments //--------------------------------------------------------------------------- static bool findFormat(nonneg int arg, const Token *firstArg, const Token **formatStringTok, const Token **formatArgTok) { const Token* argTok = firstArg; for (int i = 0; i < arg && argTok; ++i) argTok = argTok->nextArgument(); if (Token::Match(argTok, "%str% [,)]")) { *formatArgTok = argTok->nextArgument(); *formatStringTok = argTok; return true; } else if (Token::Match(argTok, "%var% [,)]") && argTok->variable() && Token::Match(argTok->variable()->typeStartToken(), "char|wchar_t") && (argTok->variable()->isPointer() || (argTok->variable()->dimensions().size() == 1 && argTok->variable()->dimensionKnown(0) && argTok->variable()->dimension(0) != 0))) { *formatArgTok = argTok->nextArgument(); if (!argTok->values().empty()) { std::list::const_iterator value = std::find_if( argTok->values().begin(), argTok->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue)); if (value != argTok->values().end() && value->isTokValue() && value->tokvalue && value->tokvalue->tokType() == Token::eString) { *formatStringTok = value->tokvalue; } } return true; } return false; } // Utility function returning whether iToTest equals iTypename or iOptionalPrefix+iTypename static inline bool typesMatch(const std::string& iToTest, const std::string& iTypename, const std::string& iOptionalPrefix = "std::") { return (iToTest == iTypename) || (iToTest == iOptionalPrefix + iTypename); } void CheckIO::checkWrongPrintfScanfArguments() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); const bool isWindows = mSettings->isWindowsPlatform(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isName()) continue; const Token* argListTok = nullptr; // Points to first va_list argument const Token* formatStringTok = nullptr; // Points to format string token bool scan = false; bool scanf_s = false; int formatStringArgNo = -1; if (tok->strAt(1) == "(" && mSettings->library.formatstr_function(tok)) { formatStringArgNo = mSettings->library.formatstr_argno(tok); scan = mSettings->library.formatstr_scan(tok); scanf_s = mSettings->library.formatstr_secure(tok); } if (formatStringArgNo >= 0) { // formatstring found in library. Find format string and first argument belonging to format string. if (!findFormat(formatStringArgNo, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } else if (Token::simpleMatch(tok, "swprintf (")) { if (Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) { // Find third parameter and format string if (!findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } else { // Find fourth parameter and format string if (!findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) continue; } } else if (isWindows && Token::Match(tok, "sprintf_s|swprintf_s (")) { // template int sprintf_s(char (&buffer)[size], const char *format, ...); if (findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok)) { if (!formatStringTok) continue; } // int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...); else if (findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) { if (!formatStringTok) continue; } } else if (isWindows && Token::Match(tok, "_snprintf_s|_snwprintf_s (")) { // template int _snprintf_s(char (&buffer)[size], size_t count, const char *format, ...); if (findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) { if (!formatStringTok) continue; } // int _snprintf_s(char *buffer, size_t sizeOfBuffer, size_t count, const char *format, ...); else if (findFormat(3, tok->tokAt(2), &formatStringTok, &argListTok)) { if (!formatStringTok) continue; } } else { continue; } if (!formatStringTok) continue; checkFormatString(tok, formatStringTok, argListTok, scan, scanf_s); } } } void CheckIO::checkFormatString(const Token * const tok, const Token * const formatStringTok, const Token * argListTok, const bool scan, const bool scanf_s) { const bool isWindows = mSettings->isWindowsPlatform(); const bool printWarning = mSettings->isEnabled(Settings::WARNING); const std::string &formatString = formatStringTok->str(); // Count format string parameters.. int numFormat = 0; int numSecure = 0; bool percent = false; const Token* argListTok2 = argListTok; std::set parameterPositionsUsed; for (std::string::const_iterator i = formatString.begin(); i != formatString.end(); ++i) { if (*i == '%') { percent = !percent; } else if (percent && *i == '[') { while (i != formatString.end()) { if (*i == ']') { numFormat++; if (argListTok) argListTok = argListTok->nextArgument(); percent = false; break; } ++i; } if (scanf_s) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } if (i == formatString.end()) break; } else if (percent) { percent = false; bool _continue = false; bool skip = false; std::string width; int parameterPosition = 0; bool hasParameterPosition = false; while (i != formatString.end() && *i != '[' && !std::isalpha((unsigned char)*i)) { if (*i == '*') { skip = true; if (scan) _continue = true; else { numFormat++; if (argListTok) argListTok = argListTok->nextArgument(); } } else if (std::isdigit(*i)) { width += *i; } else if (*i == '$') { parameterPosition = std::atoi(width.c_str()); hasParameterPosition = true; width.clear(); } ++i; } if (i != formatString.end() && *i == '[') { while (i != formatString.end()) { if (*i == ']') { if (!skip) { numFormat++; if (argListTok) argListTok = argListTok->nextArgument(); } break; } ++i; } if (scanf_s && !skip) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } _continue = true; } if (i == formatString.end()) break; if (_continue) continue; if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions. ++numFormat; // Handle parameter positions (POSIX extension) - Ticket #4900 if (hasParameterPosition) { if (parameterPositionsUsed.find(parameterPosition) == parameterPositionsUsed.end()) parameterPositionsUsed.insert(parameterPosition); else // Parameter already referenced, hence don't consider it a new format --numFormat; } // Perform type checks ArgumentInfo argInfo(argListTok, mSettings, mTokenizer->isCPP()); if (argInfo.typeToken && !argInfo.isLibraryType(mSettings)) { if (scan) { std::string specifier; bool done = false; while (!done) { switch (*i) { case 's': specifier += *i; if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) { if (!width.empty()) { const int numWidth = std::atoi(width.c_str()); if (numWidth != (argInfo.variableInfo->dimension(0) - 1)) invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, 's'); } } if (argListTok && argListTok->tokType() != Token::eString && argInfo.isKnownType() && argInfo.isArrayOrPointer() && (!Token::Match(argInfo.typeToken, "char|wchar_t") || argInfo.typeToken->strAt(-1) == "const")) { if (!(argInfo.isArrayOrPointer() && argInfo.element && !argInfo.typeToken->isStandardType())) invalidScanfArgTypeError_s(tok, numFormat, specifier, &argInfo); } if (scanf_s) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } done = true; break; case 'c': if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) { if (!width.empty()) { const int numWidth = std::atoi(width.c_str()); if (numWidth > argInfo.variableInfo->dimension(0)) invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, 'c'); } } if (scanf_s) { numSecure++; if (argListTok) { argListTok = argListTok->nextArgument(); } } done = true; break; case 'x': case 'X': case 'u': case 'o': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "char|short|int|long")) { if (argInfo.typeToken->isStandardType() || !argInfo.element) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (!argInfo.typeToken->isUnsigned() || !argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const") { invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (argInfo.typeToken->str() != "char") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (argInfo.typeToken->str() != "short") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'j': if (!typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'z': if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; case 'L': if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; default: if (argInfo.typeToken->str() != "int") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); break; } } } done = true; break; case 'n': case 'd': case 'i': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "char|short|int|long")) { if (argInfo.typeToken->isStandardType() || !argInfo.element) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (argInfo.typeToken->isUnsigned() || !argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const") { invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (argInfo.typeToken->str() != "char") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (argInfo.typeToken->str() != "short") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "intmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || typesMatch(argInfo.typeToken->originalName(), "intmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'j': if (!typesMatch(argInfo.typeToken->originalName(), "intmax_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'z': if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") || (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T")))) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; case 'L': if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; default: if (argInfo.typeToken->str() != "int") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "intmax_t") invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); break; } } } done = true; break; case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "float|double")) { if (argInfo.typeToken->isStandardType()) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else if (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const") { invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'l': if (argInfo.typeToken->str() != "double" || argInfo.typeToken->isLong()) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); break; case 'L': if (argInfo.typeToken->str() != "double" || !argInfo.typeToken->isLong()) invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); break; default: if (argInfo.typeToken->str() != "float") invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); break; } } } done = true; break; case 'I': if ((i+1 != formatString.end() && *(i+1) == '6' && i+2 != formatString.end() && *(i+2) == '4') || (i+1 != formatString.end() && *(i+1) == '3' && i+2 != formatString.end() && *(i+2) == '2')) { specifier += *i++; specifier += *i++; if ((i+1) != formatString.end() && !isalpha(*(i+1))) { specifier += *i; invalidLengthModifierError(tok, numFormat, specifier); done = true; } else { specifier += *i++; } } else { if ((i+1) != formatString.end() && !isalpha(*(i+1))) { specifier += *i; invalidLengthModifierError(tok, numFormat, specifier); done = true; } else { specifier += *i++; } } break; case 'h': case 'l': if (i+1 != formatString.end() && *(i+1) == *i) specifier += *i++; // fallthrough case 'j': case 'q': case 't': case 'z': case 'L': // Expect an alphabetical character after these specifiers if ((i + 1) != formatString.end() && !isalpha(*(i+1))) { specifier += *i; invalidLengthModifierError(tok, numFormat, specifier); done = true; } else { specifier += *i++; } break; default: done = true; break; } } } else if (printWarning) { std::string specifier; bool done = false; while (!done) { switch (*i) { case 's': if (argListTok->tokType() != Token::eString && argInfo.isKnownType() && !argInfo.isArrayOrPointer()) { if (!Token::Match(argInfo.typeToken, "char|wchar_t")) { if (!argInfo.element) invalidPrintfArgTypeError_s(tok, numFormat, &argInfo); } } done = true; break; case 'n': if ((argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")) || argListTok->tokType() == Token::eString) invalidPrintfArgTypeError_n(tok, numFormat, &argInfo); done = true; break; case 'c': case 'x': case 'X': case 'o': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'j': if (argInfo.typeToken->originalName() != "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'z': if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!(typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "WPARAM" || argInfo.typeToken->originalName() == "UINT_PTR" || argInfo.typeToken->originalName() == "LONG_PTR" || argInfo.typeToken->originalName() == "LPARAM" || argInfo.typeToken->originalName() == "LRESULT")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; default: if (!Token::Match(argInfo.typeToken, "bool|char|short|wchar_t|int")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; } } } done = true; break; case 'd': case 'i': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) { invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "char|short")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (!Token::Match(argInfo.typeToken, "bool|char|short|int|long")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (!(argInfo.typeToken->str() == "char" && !argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (!(argInfo.typeToken->str() == "short" && !argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "intmax_t") invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "intmax_t") invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'j': if (argInfo.typeToken->originalName() != "intmax_t") invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'z': if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") || (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T")))) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; case 'L': if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; default: if (!Token::Match(argInfo.typeToken, "bool|char|short|int")) invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || argInfo.typeToken->originalName() == "intmax_t") invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); break; } } } done = true; break; case 'u': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) { invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (!argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "bool|_Bool")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!Token::Match(argInfo.typeToken, "bool|char|short|long|int")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else { switch (specifier[0]) { case 'h': if (specifier[1] == 'h') { if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned())) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'l': if (specifier[1] == 'l') { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'j': if (argInfo.typeToken->originalName() != "uintmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'z': if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 't': if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'I': if (specifier.find("I64") != std::string::npos) { if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (specifier.find("I32") != std::string::npos) { if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; case 'L': if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; default: if (!Token::Match(argInfo.typeToken, "bool|char|short|int")) invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || argInfo.typeToken->originalName() == "intmax_t") invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); break; } } } done = true; break; case 'p': if (argInfo.typeToken->tokType() == Token::eString) ;// string literals are passed as pointers to literal start, okay else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) invalidPrintfArgTypeError_p(tok, numFormat, &argInfo); done = true; break; case 'e': case 'E': case 'f': case 'g': case 'G': specifier += *i; if (argInfo.typeToken->tokType() == Token::eString) invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); else if (argInfo.isArrayOrPointer() && !argInfo.element) { // use %p on pointers and arrays invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else if (argInfo.isKnownType()) { if (!Token::Match(argInfo.typeToken, "float|double")) { if (!(!argInfo.isArrayOrPointer() && argInfo.element)) invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) || (specifier[0] != 'L' && argInfo.typeToken->isLong())) invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); } done = true; break; case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int) case 'l': { // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int) // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character if (i != formatString.end() && (i + 1) != formatString.end() && *(i + 1) == *i) { if (!isalpha(*(i + 2))) { std::string modifier; modifier += *i; modifier += *(i + 1); invalidLengthModifierError(tok, numFormat, modifier); done = true; } else { specifier = *i++; specifier += *i++; } } else { if (i != formatString.end()) { if ((i + 1) != formatString.end() && !isalpha(*(i + 1))) { std::string modifier; modifier += *i; invalidLengthModifierError(tok, numFormat, modifier); done = true; } else { specifier = *i++; } } else { done = true; } } } break; case 'I': // Microsoft extension: I for size_t and ptrdiff_t, I32 for __int32, and I64 for __int64 if ((*(i+1) == '3' && *(i+2) == '2') || (*(i+1) == '6' && *(i+2) == '4')) { specifier += *i++; specifier += *i++; } // fallthrough case 'j': // intmax_t or uintmax_t case 'z': // size_t case 't': // ptrdiff_t case 'L': // long double // Expect an alphabetical character after these specifiers if ((i + 1) != formatString.end() && !isalpha(*(i+1))) { specifier += *i; invalidLengthModifierError(tok, numFormat, specifier); done = true; } else { specifier += *i++; } break; default: done = true; break; } } } } if (argListTok) argListTok = argListTok->nextArgument(); // Find next argument } } } // Count printf/scanf parameters.. int numFunction = 0; while (argListTok2) { numFunction++; argListTok2 = argListTok2->nextArgument(); // Find next argument } if (printWarning) { // Check that all parameter positions reference an actual parameter for (int i : parameterPositionsUsed) { if ((i == 0) || (i > numFormat)) wrongPrintfScanfPosixParameterPositionError(tok, tok->str(), i, numFormat); } } // Mismatching number of parameters => warning if ((numFormat + numSecure) != numFunction) wrongPrintfScanfArgumentsError(tok, tok->originalName().empty() ? tok->str() : tok->originalName(), numFormat + numSecure, numFunction); } // We currently only support string literals, variables, and functions. /// @todo add non-string literals, and generic expressions CheckIO::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings *settings, bool _isCPP) : variableInfo(nullptr) , typeToken(nullptr) , functionInfo(nullptr) , tempToken(nullptr) , element(false) , _template(false) , address(false) , isCPP(_isCPP) { if (!arg) return; // Use AST type info // TODO: This is a bailout so that old code is used in simple cases. Remove the old code and always use the AST type. if (!Token::Match(arg, "%str% ,|)") && !(Token::Match(arg,"%var%") && arg->variable() && arg->variable()->isArray())) { const Token *top = arg; while (top->astParent() && top->astParent()->str() != "," && top->astParent() != arg->previous()) top = top->astParent(); const ValueType *valuetype = top->argumentType(); if (valuetype && valuetype->type >= ValueType::Type::BOOL) { typeToken = tempToken = new Token(); if (valuetype->pointer && valuetype->constness & 1) { tempToken->str("const"); tempToken->insertToken("a"); tempToken = tempToken->next(); } if (valuetype->type == ValueType::BOOL) tempToken->str("bool"); else if (valuetype->type == ValueType::CHAR) tempToken->str("char"); else if (valuetype->type == ValueType::SHORT) tempToken->str("short"); else if (valuetype->type == ValueType::WCHAR_T) tempToken->str("wchar_t"); else if (valuetype->type == ValueType::INT) tempToken->str("int"); else if (valuetype->type == ValueType::LONG) tempToken->str("long"); else if (valuetype->type == ValueType::LONGLONG) { tempToken->str("long"); tempToken->isLong(true); } else if (valuetype->type == ValueType::FLOAT) tempToken->str("float"); else if (valuetype->type == ValueType::DOUBLE) tempToken->str("double"); else if (valuetype->type == ValueType::LONGDOUBLE) { tempToken->str("double"); tempToken->isLong(true); } if (valuetype->isIntegral()) { if (valuetype->sign == ValueType::Sign::UNSIGNED) tempToken->isUnsigned(true); else if (valuetype->sign == ValueType::Sign::SIGNED) tempToken->isSigned(true); } if (!valuetype->originalTypeName.empty()) tempToken->originalName(valuetype->originalTypeName); for (int p = 0; p < valuetype->pointer; p++) tempToken->insertToken("*"); tempToken = const_cast(typeToken); return; } } if (arg->tokType() == Token::eString) { typeToken = arg; return; } else if (arg->str() == "&" || arg->tokType() == Token::eVariable || arg->tokType() == Token::eFunction || Token::Match(arg, "%type% ::") || (Token::Match(arg, "static_cast|reinterpret_cast|const_cast <") && Token::simpleMatch(arg->linkAt(1), "> (") && Token::Match(arg->linkAt(1)->linkAt(1), ") ,|)"))) { if (Token::Match(arg, "static_cast|reinterpret_cast|const_cast")) { typeToken = arg->tokAt(2); while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); return; } if (arg->str() == "&") { address = true; arg = arg->next(); } while (Token::Match(arg, "%type% ::")) arg = arg->tokAt(2); if (!arg || !(arg->tokType() == Token::eVariable || arg->tokType() == Token::eFunction)) return; const Token *varTok = nullptr; const Token *tok1 = arg->next(); for (; tok1; tok1 = tok1->next()) { if (tok1->str() == "," || tok1->str() == ")") { if (tok1->previous()->str() == "]") { varTok = tok1->linkAt(-1)->previous(); if (varTok->str() == ")" && varTok->link()->previous()->tokType() == Token::eFunction) { const Function * function = varTok->link()->previous()->function(); if (function && function->retType && function->retType->isEnumType()) { if (function->retType->classScope->enumType) typeToken = function->retType->classScope->enumType; else { tempToken = new Token(); tempToken->fileIndex(tok1->fileIndex()); tempToken->linenr(tok1->linenr()); tempToken->str("int"); typeToken = tempToken; } } else if (function && function->retDef) { typeToken = function->retDef; while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); functionInfo = function; element = true; } return; } } else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->tokType() == Token::eFunction) { const Function * function = tok1->linkAt(-1)->previous()->function(); if (function && function->retType && function->retType->isEnumType()) { if (function->retType->classScope->enumType) typeToken = function->retType->classScope->enumType; else { tempToken = new Token(); tempToken->fileIndex(tok1->fileIndex()); tempToken->linenr(tok1->linenr()); tempToken->str("int"); typeToken = tempToken; } } else if (function && function->retDef) { typeToken = function->retDef; while (typeToken->str() == "const" || typeToken->str() == "extern") typeToken = typeToken->next(); functionInfo = function; element = false; } return; } else varTok = tok1->previous(); break; } else if (tok1->str() == "(" || tok1->str() == "{" || tok1->str() == "[") tok1 = tok1->link(); else if (tok1->link() && tok1->str() == "<") tok1 = tok1->link(); // check for some common well known functions else if (isCPP && ((Token::Match(tok1->previous(), "%var% . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous())) || (Token::Match(tok1->previous(), "] . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous()->link()->previous())))) { tempToken = new Token(); tempToken->fileIndex(tok1->fileIndex()); tempToken->linenr(tok1->linenr()); if (tok1->next()->str() == "size") { // size_t is platform dependent if (settings->sizeof_size_t == 8) { tempToken->str("long"); if (settings->sizeof_long != 8) tempToken->isLong(true); } else if (settings->sizeof_size_t == 4) { if (settings->sizeof_long == 4) { tempToken->str("long"); } else { tempToken->str("int"); } } tempToken->originalName("size_t"); tempToken->isUnsigned(true); } else if (tok1->next()->str() == "empty") { tempToken->str("bool"); } else if (tok1->next()->str() == "c_str") { tempToken->str("const"); tempToken->insertToken("*"); if (typeToken->strAt(2) == "string") tempToken->insertToken("char"); else tempToken->insertToken("wchar_t"); } typeToken = tempToken; return; } // check for std::vector::at() and std::string::at() else if (Token::Match(tok1->previous(), "%var% . at (") && Token::Match(tok1->linkAt(2), ") [,)]")) { varTok = tok1->previous(); variableInfo = varTok->variable(); if (!variableInfo || !isStdVectorOrString()) { variableInfo = nullptr; typeToken = nullptr; } return; } else if (!(tok1->str() == "." || tok1->tokType() == Token::eVariable || tok1->tokType() == Token::eFunction)) return; } if (varTok) { variableInfo = varTok->variable(); element = tok1->previous()->str() == "]"; // look for std::vector operator [] and use template type as return type if (variableInfo) { if (element && isStdVectorOrString()) { // isStdVectorOrString sets type token if true element = false; // not really an array element } else if (variableInfo->isEnumType()) { if (variableInfo->type() && variableInfo->type()->classScope && variableInfo->type()->classScope->enumType) typeToken = variableInfo->type()->classScope->enumType; else { tempToken = new Token(); tempToken->fileIndex(tok1->fileIndex()); tempToken->linenr(tok1->linenr()); tempToken->str("int"); typeToken = tempToken; } } else typeToken = variableInfo->typeStartToken(); } return; } } } CheckIO::ArgumentInfo::~ArgumentInfo() { if (tempToken) { while (tempToken->next()) tempToken->deleteNext(); delete tempToken; } } namespace { const std::set stl_vector = { "array", "vector" }; const std::set stl_string = { "string", "u16string", "u32string", "wstring" }; } bool CheckIO::ArgumentInfo::isStdVectorOrString() { if (!isCPP) return false; if (variableInfo->isStlType(stl_vector)) { typeToken = variableInfo->typeStartToken()->tokAt(4); _template = true; return true; } else if (variableInfo->isStlType(stl_string)) { tempToken = new Token(); tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex()); tempToken->linenr(variableInfo->typeStartToken()->linenr()); if (variableInfo->typeStartToken()->strAt(2) == "string") tempToken->str("char"); else tempToken->str("wchar_t"); typeToken = tempToken; return true; } else if (variableInfo->type() && !variableInfo->type()->derivedFrom.empty()) { const std::vector& derivedFrom = variableInfo->type()->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Token* nameTok = i.nameTok; if (Token::Match(nameTok, "std :: vector|array <")) { typeToken = nameTok->tokAt(4); _template = true; return true; } else if (Token::Match(nameTok, "std :: string|wstring")) { tempToken = new Token(); tempToken->fileIndex(variableInfo->typeStartToken()->fileIndex()); tempToken->linenr(variableInfo->typeStartToken()->linenr()); if (nameTok->strAt(2) == "string") tempToken->str("char"); else tempToken->str("wchar_t"); typeToken = tempToken; return true; } } } else if (variableInfo->type()) { const Scope * classScope = variableInfo->type()->classScope; if (classScope) { for (const Function &func : classScope->functionList) { if (func.name() == "operator[]") { if (Token::Match(func.retDef, "%type% &")) { typeToken = func.retDef; return true; } } } } } return false; } static const std::set stl_container = { "array", "bitset", "deque", "forward_list", "hash_map", "hash_multimap", "hash_set", "list", "map", "multimap", "multiset", "priority_queue", "queue", "set", "stack", "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector" }; bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok) { if (!isCPP) return false; if (tok && tok->variable()) { const Variable* variable = tok->variable(); if (variable->isStlType(stl_container)) { typeToken = variable->typeStartToken()->tokAt(4); return true; } else if (variable->isStlType(stl_string)) { typeToken = variable->typeStartToken(); return true; } else if (variable->type() && !variable->type()->derivedFrom.empty()) { for (const Type::BaseInfo &baseInfo : variable->type()->derivedFrom) { const Token* nameTok = baseInfo.nameTok; if (Token::Match(nameTok, "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <")) { typeToken = nameTok->tokAt(4); return true; } else if (Token::Match(nameTok, "std :: string|wstring")) { typeToken = nameTok; return true; } } } } return false; } bool CheckIO::ArgumentInfo::isArrayOrPointer() const { if (address) return true; else if (variableInfo && !_template) { return variableInfo->isArrayOrPointer(); } else { const Token *tok = typeToken; while (Token::Match(tok, "const|struct")) tok = tok->next(); if (tok && tok->strAt(1) == "*") return true; } return false; } bool CheckIO::ArgumentInfo::isComplexType() const { if (variableInfo->type()) return (true); const Token* varTypeTok = typeToken; if (varTypeTok->str() == "std") varTypeTok = varTypeTok->tokAt(2); return ((variableInfo->isStlStringType() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer()); } bool CheckIO::ArgumentInfo::isKnownType() const { if (variableInfo) return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType()); else if (functionInfo) return (typeToken->isStandardType() || functionInfo->retType || Token::Match(typeToken, "std :: string|wstring")); return typeToken->isStandardType() || Token::Match(typeToken, "std :: string|wstring"); } bool CheckIO::ArgumentInfo::isLibraryType(const Settings *settings) const { return typeToken && typeToken->isStandardType() && settings->library.podtype(typeToken->str()); } void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, const std::string &functionName, nonneg int numFormat, nonneg int numFunction) { const Severity::SeverityType severity = numFormat > numFunction ? Severity::error : Severity::warning; if (severity != Severity::error && !mSettings->isEnabled(Settings::WARNING)) return; std::ostringstream errmsg; errmsg << functionName << " format string requires " << numFormat << " parameter" << (numFormat != 1 ? "s" : "") << " but " << (numFormat > numFunction ? "only " : "") << numFunction << (numFunction != 1 ? " are" : " is") << " given."; reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str(), CWE685, false); } void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, nonneg int index, nonneg int numFunction) { if (!mSettings->isEnabled(Settings::WARNING)) return; std::ostringstream errmsg; errmsg << functionName << ": "; if (index == 0) { errmsg << "parameter positions start at 1, not 0"; } else { errmsg << "referencing parameter " << index << " while " << numFunction << " arguments given"; } reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str(), CWE685, false); } void CheckIO::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a \'"; if (specifier[0] == 's') errmsg << "char"; else if (specifier[0] == 'S') errmsg << "wchar_t"; errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidScanfArgType_s", errmsg.str(), CWE686, false); } void CheckIO::invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; if (specifier[0] == 'h') { if (specifier[1] == 'h') errmsg << (isUnsigned ? "unsigned " : "") << "char"; else errmsg << (isUnsigned ? "unsigned " : "") << "short"; } else if (specifier[0] == 'l') { if (specifier[1] == 'l') errmsg << (isUnsigned ? "unsigned " : "") << "long long"; else errmsg << (isUnsigned ? "unsigned " : "") << "long"; } else if (specifier.find("I32") != std::string::npos) { errmsg << (isUnsigned ? "unsigned " : "") << "__int32"; } else if (specifier.find("I64") != std::string::npos) { errmsg << (isUnsigned ? "unsigned " : "") << "__int64"; } else if (specifier[0] == 'I') { errmsg << (isUnsigned ? "size_t" : "ptrdiff_t"); } else if (specifier[0] == 'j') { if (isUnsigned) errmsg << "uintmax_t"; else errmsg << "intmax_t"; } else if (specifier[0] == 'z') { if (specifier[1] == 'd') errmsg << "ssize_t"; else errmsg << "size_t"; } else if (specifier[0] == 't') { errmsg << (isUnsigned ? "unsigned " : "") << "ptrdiff_t"; } else if (specifier[0] == 'L') { errmsg << (isUnsigned ? "unsigned " : "") << "long long"; } else { errmsg << (isUnsigned ? "unsigned " : "") << "int"; } errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidScanfArgType_int", errmsg.str(), CWE686, false); } void CheckIO::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; if (specifier[0] == 'l' && specifier[1] != 'l') errmsg << "double"; else if (specifier[0] == 'L') errmsg << "long double"; else errmsg << "float"; errmsg << " *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidScanfArgType_float", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_s", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%n in format string (no. " << numFormat << ") requires \'int *\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_n", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_p", errmsg.str(), CWE686, false); } static void printfFormatType(std::ostream& os, const std::string& specifier, bool isUnsigned) { os << "\'"; if (specifier[0] == 'l') { if (specifier[1] == 'l') os << (isUnsigned ? "unsigned " : "") << "long long"; else os << (isUnsigned ? "unsigned " : "") << "long"; } else if (specifier[0] == 'h') { if (specifier[1] == 'h') os << (isUnsigned ? "unsigned " : "") << "char"; else os << (isUnsigned ? "unsigned " : "") << "short"; } else if (specifier.find("I32") != std::string::npos) { os << (isUnsigned ? "unsigned " : "") << "__int32"; } else if (specifier.find("I64") != std::string::npos) { os << (isUnsigned ? "unsigned " : "") << "__int64"; } else if (specifier[0] == 'I') { os << (isUnsigned ? "size_t" : "ptrdiff_t"); } else if (specifier[0] == 'j') { if (isUnsigned) os << "uintmax_t"; else os << "intmax_t"; } else if (specifier[0] == 'z') { if (specifier[1] == 'd') os << "ssize_t"; else os << "size_t"; } else if (specifier[0] == 't') { os << (isUnsigned ? "unsigned " : "") << "ptrdiff_t"; } else if (specifier[0] == 'L') { os << (isUnsigned ? "unsigned " : "") << "long long"; } else { os << (isUnsigned ? "unsigned " : "") << "int"; } os << "\'"; } void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires "; printfFormatType(errmsg, specifier, true); errmsg << " but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_uint", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires "; printfFormatType(errmsg, specifier, false); errmsg << " but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_sint", errmsg.str(), CWE686, false); } void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity::SeverityType severity = getSeverity(argInfo); if (!mSettings->isEnabled(severity)) return; std::ostringstream errmsg; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; if (specifier[0] == 'L') errmsg << "long "; errmsg << "double\' but the argument type is "; argumentType(errmsg, argInfo); errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_float", errmsg.str(), CWE686, false); } Severity::SeverityType CheckIO::getSeverity(const CheckIO::ArgumentInfo *argInfo) { return (argInfo && argInfo->typeToken && !argInfo->typeToken->originalName().empty()) ? Severity::portability : Severity::warning; } void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) { if (argInfo) { os << "\'"; const Token *type = argInfo->typeToken; if (type->tokType() == Token::eString) { if (type->isLong()) os << "const wchar_t *"; else os << "const char *"; } else { if (type->originalName().empty()) { if (type->strAt(-1) == "const") os << "const "; while (Token::Match(type, "const|struct")) { os << type->str() << " "; type = type->next(); } while (Token::Match(type, "%any% ::")) { os << type->str() << "::"; type = type->tokAt(2); } type->stringify(os, false, true, false); if (type->strAt(1) == "*" && !argInfo->element) os << " *"; else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray()) os << " *"; else if (type->strAt(1) == "*" && argInfo->variableInfo && argInfo->element && argInfo->variableInfo->isArray()) os << " *"; if (argInfo->address) os << " *"; } else { if (type->isUnsigned()) { if (type->originalName() == "__int64" || type->originalName() == "__int32" || type->originalName() == "ptrdiff_t") os << "unsigned "; } os << type->originalName(); if (type->strAt(1) == "*" || argInfo->address) os << " *"; os << " {aka "; type->stringify(os, false, true, false); if (type->strAt(1) == "*" || argInfo->address) os << " *"; os << "}"; } } os << "\'"; } else os << "Unknown"; } void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier) { if (!mSettings->isEnabled(Settings::WARNING)) return; std::ostringstream errmsg; errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier."; reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str(), CWE704, false); } void CheckIO::invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, char c) { MathLib::bigint arrlen = 0; std::string varname; if (var) { arrlen = var->dimension(0); varname = var->name(); } std::ostringstream errmsg; if (arrlen > width) { if (tok != nullptr && (!mSettings->inconclusive || !mSettings->isEnabled(Settings::WARNING))) return; errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer" << " '" << varname << "[" << arrlen << "]'."; reportError(tok, Severity::warning, "invalidScanfFormatWidth_smaller", errmsg.str(), CWE(0U), true); } else { errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '" << varname << "[" << arrlen << "]', use %" << (c == 'c' ? arrlen : (arrlen - 1)) << c << " to prevent overflowing it."; reportError(tok, Severity::error, "invalidScanfFormatWidth", errmsg.str(), CWE687, false); } } cppcheck-1.90/lib/checkio.h000066400000000000000000000202051357737443600155710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkioH #define checkioH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "errorlogger.h" #include #include class Function; class Settings; class Token; class Tokenizer; class Variable; /// @addtogroup Checks /// @{ /** @brief %Check input output operations. */ class CPPCHECKLIB CheckIO : public Check { public: /** @brief This constructor is used when registering CheckIO */ CheckIO() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckIO(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks on the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckIO checkIO(tokenizer, settings, errorLogger); checkIO.checkWrongPrintfScanfArguments(); checkIO.checkCoutCerrMisusage(); checkIO.checkFileUsage(); checkIO.invalidScanf(); } /** @brief %Check for missusage of std::cout */ void checkCoutCerrMisusage(); /** @brief %Check usage of files*/ void checkFileUsage(); /** @brief scanf can crash if width specifiers are not used */ void invalidScanf(); /** @brief %Checks type and number of arguments given to functions like printf or scanf*/ void checkWrongPrintfScanfArguments(); private: class ArgumentInfo { public: ArgumentInfo(const Token *arg, const Settings *settings, bool _isCPP); ~ArgumentInfo(); bool isArrayOrPointer() const; bool isComplexType() const; bool isKnownType() const; bool isStdVectorOrString(); bool isStdContainer(const Token *tok); bool isLibraryType(const Settings *settings) const; const Variable *variableInfo; const Token *typeToken; const Function *functionInfo; Token *tempToken; bool element; bool _template; bool address; bool isCPP; private: ArgumentInfo(const ArgumentInfo &); // not implemented ArgumentInfo operator = (const ArgumentInfo &); // not implemented }; void checkFormatString(const Token * const tok, const Token * const formatStringTok, const Token * argListTok, const bool scan, const bool scanf_s); // Reporting errors.. void coutCerrMisusageError(const Token* tok, const std::string& streamName); void fflushOnInputStreamError(const Token *tok, const std::string &varname); void ioWithoutPositioningError(const Token *tok); void readWriteOnlyFileError(const Token *tok); void writeReadOnlyFileError(const Token *tok); void useClosedFileError(const Token *tok); void seekOnAppendedFileError(const Token *tok); void invalidScanfError(const Token *tok); void wrongPrintfScanfArgumentsError(const Token* tok, const std::string &functionName, nonneg int numFormat, nonneg int numFunction); void wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, nonneg int index, nonneg int numFunction); void invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned); void invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); void invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier); void invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, char c); static void argumentType(std::ostream & os, const ArgumentInfo * argInfo); static Severity::SeverityType getSeverity(const ArgumentInfo *argInfo); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckIO c(nullptr, settings, errorLogger); c.coutCerrMisusageError(nullptr, "cout"); c.fflushOnInputStreamError(nullptr, "stdin"); c.ioWithoutPositioningError(nullptr); c.readWriteOnlyFileError(nullptr); c.writeReadOnlyFileError(nullptr); c.useClosedFileError(nullptr); c.seekOnAppendedFileError(nullptr); c.invalidScanfError(nullptr); c.wrongPrintfScanfArgumentsError(nullptr, "printf",3,2); c.invalidScanfArgTypeError_s(nullptr, 1, "s", nullptr); c.invalidScanfArgTypeError_int(nullptr, 1, "d", nullptr, false); c.invalidScanfArgTypeError_float(nullptr, 1, "f", nullptr); c.invalidPrintfArgTypeError_s(nullptr, 1, nullptr); c.invalidPrintfArgTypeError_n(nullptr, 1, nullptr); c.invalidPrintfArgTypeError_p(nullptr, 1, nullptr); c.invalidPrintfArgTypeError_uint(nullptr, 1, "u", nullptr); c.invalidPrintfArgTypeError_sint(nullptr, 1, "i", nullptr); c.invalidPrintfArgTypeError_float(nullptr, 1, "f", nullptr); c.invalidLengthModifierError(nullptr, 1, "I"); c.invalidScanfFormatWidthError(nullptr, 10, 5, nullptr, 's'); c.invalidScanfFormatWidthError(nullptr, 99, -1, nullptr, 's'); c.wrongPrintfScanfPosixParameterPositionError(nullptr, "printf", 2, 1); } static std::string myName() { return "IO using format string"; } std::string classInfo() const OVERRIDE { return "Check format string input/output operations.\n" "- Bad usage of the function 'sprintf' (overlapping data)\n" "- Missing or wrong width specifiers in 'scanf' format string\n" "- Use a file that has been closed\n" "- File input/output without positioning results in undefined behaviour\n" "- Read to a file that has only been opened for writing (or vice versa)\n" "- Repositioning operation on a file opened in append mode\n" "- Using fflush() on an input stream\n" "- Invalid usage of output stream. For example: 'std::cout << std::cout;'\n" "- Wrong number of arguments given to 'printf' or 'scanf;'\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkioH cppcheck-1.90/lib/checkleakautovar.cpp000066400000000000000000001224531357737443600200430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // Leaks when using auto variables //--------------------------------------------------------------------------- #include "checkleakautovar.h" #include "astutils.h" #include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak #include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef #include "errorlogger.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckLeakAutoVar instance; } static const CWE CWE672(672U); static const CWE CWE415(415U); // Hardcoded allocation types (not from library) static const int NEW_ARRAY = -2; static const int NEW = -1; /** * @brief Is variable type some class with automatic deallocation? * @param vartok variable token * @return true unless it can be seen there is no automatic deallocation */ static bool isAutoDealloc(const Variable *var) { if (var->valueType() && var->valueType()->type != ValueType::Type::RECORD && var->valueType()->type != ValueType::Type::UNKNOWN_TYPE) return false; // return false if the type is a simple record type without side effects // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check base class for side effects */ /** @todo false negative: check constructors for side effects */ if (var->typeScope() && var->typeScope()->numConstructors == 0 && (var->typeScope()->varlist.empty() || var->type()->needInitialization == Type::NeedInitialization::True) && var->type()->derivedFrom.empty()) return false; return true; } //--------------------------------------------------------------------------- void VarInfo::print() { std::cout << "size=" << alloctype.size() << std::endl; for (std::map::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) { std::string strusage; const std::map::const_iterator use = possibleUsage.find(it->first); if (use != possibleUsage.end()) strusage = use->second; std::string status; switch (it->second.status) { case OWNED: status = "owned"; break; case DEALLOC: status = "dealloc"; break; case ALLOC: status = "alloc"; break; case NOALLOC: status = "noalloc"; break; default: status = "?"; break; }; std::cout << "status=" << status << " " << "alloctype='" << it->second.type << "' " << "possibleUsage='" << strusage << "' " << "conditionalAlloc=" << (conditionalAlloc.find(it->first) != conditionalAlloc.end() ? "yes" : "no") << " " << "referenced=" << (referenced.find(it->first) != referenced.end() ? "yes" : "no") << " " << std::endl; } } void VarInfo::possibleUsageAll(const std::string &functionName) { possibleUsage.clear(); for (std::map::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) possibleUsage[it->first] = functionName; } void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type) { const CheckMemoryLeak checkmemleak(mTokenizer, mErrorLogger, mSettings); if (Library::isresource(type)) checkmemleak.resourceLeakError(tok, varname); else checkmemleak.memleakError(tok, varname); } void CheckLeakAutoVar::mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname) { const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings); const std::list callstack = { allocTok, deallocTok }; c.mismatchAllocDealloc(callstack, varname); } void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname) { const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings); c.deallocuseError(tok, varname); } void CheckLeakAutoVar::deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname) { const std::list locations = { deallocTok, tok }; reportError(locations, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, false); } void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::string &functionName) { if (mSettings->checkLibrary && mSettings->isEnabled(Settings::INFORMATION)) { reportError(tok, Severity::information, "checkLibraryUseIgnore", "--check-library: Function " + functionName + "() should have / configuration"); } } void CheckLeakAutoVar::doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type) { const std::list locations = { prevFreeTok, tok }; if (Library::isresource(type)) reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nResource handle '$symbol' freed twice.", CWE415, false); else reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nMemory pointed to by '$symbol' is freed twice.", CWE415, false); } void CheckLeakAutoVar::check() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // Local variables that are known to be non-zero. const std::set notzero; // Check function scopes for (const Scope * scope : symbolDatabase->functionScopes) { if (scope->hasInlineOrLambdaFunction()) continue; // Empty variable info VarInfo varInfo; checkScope(scope->bodyStart, &varInfo, notzero, 0); varInfo.conditionalAlloc.clear(); // Clear reference arguments from varInfo.. std::map::iterator it = varInfo.alloctype.begin(); while (it != varInfo.alloctype.end()) { const Variable *var = symbolDatabase->getVariableFromVarId(it->first); if (!var || (var->isArgument() && var->isReference()) || (!var->isArgument() && !var->isLocal())) varInfo.alloctype.erase(it++); else ++it; } ret(scope->bodyEnd, varInfo); } } static bool isVarUsedInTree(const Token *tok, nonneg int varid) { if (!tok) return false; if (tok->varId() == varid) return true; if (tok->str() == "(" && Token::simpleMatch(tok->astOperand1(), "sizeof")) return false; return isVarUsedInTree(tok->astOperand1(), varid) || isVarUsedInTree(tok->astOperand2(), varid); } static bool isPointerReleased(const Token *startToken, const Token *endToken, nonneg int varid) { for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { if (tok->varId() != varid) continue; if (Token::Match(tok, "%var% . release ( )")) return true; if (Token::Match(tok, "%var% =")) return false; } return false; } static bool isLocalVarNoAutoDealloc(const Token *varTok, const bool isCpp) { // not a local variable nor argument? const Variable *var = varTok->variable(); if (!var) return true; if (!var->isArgument() && (!var->isLocal() || var->isStatic())) return false; // Don't check reference variables if (var->isReference()) return false; // non-pod variable if (isCpp) { // Possibly automatically deallocated memory if (isAutoDealloc(var) && Token::Match(varTok, "%var% = new")) return false; if (!var->isPointer() && !var->typeStartToken()->isStandardType()) return false; } return true; } /** checks if nameToken is a name of a function in a function call: * func(arg) * or * func(arg) * @param nameToken Function name token * @return opening parenthesis token or NULL if not a function call */ static const Token * isFunctionCall(const Token * nameToken) { if (nameToken->isName()) { nameToken = nameToken->next(); // check if function is a template if (nameToken && nameToken->link() && nameToken->str() == "<") { // skip template arguments nameToken = nameToken->link()->next(); } // check for '(' if (nameToken && nameToken->link() && nameToken->str() == "(") { // returning opening parenthesis pointer return nameToken; } } return nullptr; } void CheckLeakAutoVar::checkScope(const Token * const startToken, VarInfo *varInfo, std::set notzero, nonneg int recursiveCount) { #if ASAN static const nonneg int recursiveLimit = 300; #else static const nonneg int recursiveLimit = 1000; #endif if (++recursiveCount > recursiveLimit) // maximum number of "else if ()" throw InternalError(startToken, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 1000 reached.", InternalError::LIMIT); std::map &alloctype = varInfo->alloctype; std::map &possibleUsage = varInfo->possibleUsage; const std::set conditionalAlloc(varInfo->conditionalAlloc); // Parse all tokens const Token * const endToken = startToken->link(); for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { if (!tok->scope()->isExecutable()) { tok = tok->scope()->bodyEnd; if (!tok) // Ticket #6666 (crash upon invalid code) break; } // check each token { const Token * nextTok = checkTokenInsideExpression(tok, varInfo); if (nextTok) { tok = nextTok; continue; } } // look for end of statement if (!Token::Match(tok, "[;{},]") || Token::Match(tok->next(), "[;{},]")) continue; tok = tok->next(); if (!tok || tok == endToken) break; // parse statement, skip to last member const Token *varTok = tok; while (Token::Match(varTok, "%name% ::|. %name% !!(")) varTok = varTok->tokAt(2); const Token *ftok = tok; if (ftok->str() == "::") ftok = ftok->next(); while (Token::Match(ftok, "%name% :: %name%")) ftok = ftok->tokAt(2); // assignment.. if (Token::Match(varTok, "%var% =")) { const Token* const tokAssignOp = varTok->next(); // taking address of another variable.. if (Token::Match(tokAssignOp, "= %var% [+;]")) { if (varTok->tokAt(2)->varId() != varTok->varId()) { // If variable points at allocated memory => error leakIfAllocated(varTok, *varInfo); // no multivariable checking currently => bail out for rhs variables for (const Token *tok2 = varTok; tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { break; } if (tok2->varId()) { varInfo->erase(tok2->varId()); } } } } // right ast part (after `=` operator) const Token* tokRightAstOperand = tokAssignOp->astOperand2(); while (tokRightAstOperand && tokRightAstOperand->isCast()) tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1(); // is variable used in rhs? if (isVarUsedInTree(tokRightAstOperand, varTok->varId())) continue; // Variable has already been allocated => error if (conditionalAlloc.find(varTok->varId()) == conditionalAlloc.end()) leakIfAllocated(varTok, *varInfo); varInfo->erase(varTok->varId()); if (!isLocalVarNoAutoDealloc(varTok, mTokenizer->isCPP())) continue; // allocation? const Token *const fTok = tokRightAstOperand ? tokRightAstOperand->previous() : nullptr; if (Token::Match(fTok, "%type% (")) { const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(fTok); if (f && f->arg == -1) { VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()]; varAlloc.type = f->groupId; varAlloc.status = VarInfo::ALLOC; varAlloc.allocTok = fTok; } changeAllocStatusIfRealloc(alloctype, fTok, varTok); } else if (mTokenizer->isCPP() && Token::Match(varTok->tokAt(2), "new !!(")) { const Token* tok2 = varTok->tokAt(2)->astOperand1(); const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "["))); VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()]; varAlloc.type = arrayNew ? NEW_ARRAY : NEW; varAlloc.status = VarInfo::ALLOC; varAlloc.allocTok = varTok->tokAt(2); } // Assigning non-zero value variable. It might be used to // track the execution for a later if condition. if (Token::Match(varTok->tokAt(2), "%num% ;") && MathLib::toLongNumber(varTok->strAt(2)) != 0) notzero.insert(varTok->varId()); else if (Token::Match(varTok->tokAt(2), "- %type% ;") && varTok->tokAt(3)->isUpperCaseName()) notzero.insert(varTok->varId()); else notzero.erase(varTok->varId()); } // if/else else if (Token::simpleMatch(tok, "if (")) { // Parse function calls inside the condition const Token * closingParenthesis = tok->linkAt(1); for (const Token *innerTok = tok->tokAt(2); innerTok && innerTok != closingParenthesis; innerTok = innerTok->next()) { // TODO: replace with checkTokenInsideExpression() if (!isLocalVarNoAutoDealloc(innerTok, mTokenizer->isCPP())) continue; if (Token::Match(innerTok, "%var% =") && innerTok->astParent() == innerTok->next()) { // allocation? // right ast part (after `=` operator) const Token* tokRightAstOperand = innerTok->next()->astOperand2(); while (tokRightAstOperand && tokRightAstOperand->isCast()) tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1(); if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) { const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(tokRightAstOperand->previous()); if (f && f->arg == -1) { VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()]; varAlloc.type = f->groupId; varAlloc.status = VarInfo::ALLOC; varAlloc.allocTok = tokRightAstOperand->previous(); } else { // Fixme: warn about leak alloctype.erase(innerTok->varId()); } changeAllocStatusIfRealloc(alloctype, innerTok->tokAt(2), varTok); } else if (mTokenizer->isCPP() && Token::Match(innerTok->tokAt(2), "new !!(")) { const Token* tok2 = innerTok->tokAt(2)->astOperand1(); const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "["))); VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()]; varAlloc.type = arrayNew ? NEW_ARRAY : NEW; varAlloc.status = VarInfo::ALLOC; varAlloc.allocTok = innerTok->tokAt(2); } } // check for function call const Token * const openingPar = isFunctionCall(innerTok); if (openingPar) { // innerTok is a function name const VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC); functionCall(innerTok, openingPar, varInfo, allocation, nullptr); innerTok = openingPar->link(); } } if (Token::simpleMatch(closingParenthesis, ") {")) { VarInfo varInfo1(*varInfo); // VarInfo for if code VarInfo varInfo2(*varInfo); // VarInfo for else code // Recursively scan variable comparisons in condition std::stack tokens; tokens.push(tok->next()->astOperand2()); while (!tokens.empty()) { const Token *tok3 = tokens.top(); tokens.pop(); if (!tok3) continue; if (tok3->str() == "&&" || tok3->str() == "||") { // FIXME: handle && ! || better tokens.push(tok3->astOperand1()); tokens.push(tok3->astOperand2()); continue; } if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) { tokens.push(tok3->astOperand2()); continue; } else if (tok3->str() == "(" && Token::Match(tok3->previous(), "%name%")) { const std::vector params = getArguments(tok3->previous()); for (const Token *par : params) { if (!par->isComparisonOp()) continue; const Token *vartok = nullptr; if (astIsVariableComparison(par, "!=", "0", &vartok) || astIsVariableComparison(par, "==", "0", &vartok) || astIsVariableComparison(par, "<", "0", &vartok) || astIsVariableComparison(par, ">", "0", &vartok) || astIsVariableComparison(par, "==", "-1", &vartok) || astIsVariableComparison(par, "!=", "-1", &vartok)) { varInfo1.erase(vartok->varId()); varInfo2.erase(vartok->varId()); } } continue; } const Token *vartok = nullptr; if (astIsVariableComparison(tok3, "!=", "0", &vartok)) { varInfo2.erase(vartok->varId()); if (notzero.find(vartok->varId()) != notzero.end()) varInfo2.clear(); } else if (astIsVariableComparison(tok3, "==", "0", &vartok)) { varInfo1.erase(vartok->varId()); } else if (astIsVariableComparison(tok3, "<", "0", &vartok)) { varInfo1.erase(vartok->varId()); } else if (astIsVariableComparison(tok3, ">", "0", &vartok)) { varInfo2.erase(vartok->varId()); } else if (astIsVariableComparison(tok3, "==", "-1", &vartok)) { varInfo1.erase(vartok->varId()); } } checkScope(closingParenthesis->next(), &varInfo1, notzero, recursiveCount); closingParenthesis = closingParenthesis->linkAt(1); if (Token::simpleMatch(closingParenthesis, "} else {")) { checkScope(closingParenthesis->tokAt(2), &varInfo2, notzero, recursiveCount); tok = closingParenthesis->linkAt(2)->previous(); } else { tok = closingParenthesis->previous(); } VarInfo old; old.swap(*varInfo); std::map::const_iterator it; for (it = old.alloctype.begin(); it != old.alloctype.end(); ++it) { const int varId = it->first; if (old.conditionalAlloc.find(varId) == old.conditionalAlloc.end()) continue; if (varInfo1.alloctype.find(varId) == varInfo1.alloctype.end() || varInfo2.alloctype.find(varId) == varInfo2.alloctype.end()) { varInfo1.erase(varId); varInfo2.erase(varId); } } // Conditional allocation in varInfo1 for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) { if (varInfo2.alloctype.find(it->first) == varInfo2.alloctype.end() && old.alloctype.find(it->first) == old.alloctype.end()) { varInfo->conditionalAlloc.insert(it->first); } } // Conditional allocation in varInfo2 for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) { if (varInfo1.alloctype.find(it->first) == varInfo1.alloctype.end() && old.alloctype.find(it->first) == old.alloctype.end()) { varInfo->conditionalAlloc.insert(it->first); } } // Conditional allocation/deallocation for (it = varInfo1.alloctype.begin(); it != varInfo1.alloctype.end(); ++it) { if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) { varInfo->conditionalAlloc.erase(it->first); varInfo2.erase(it->first); } } for (it = varInfo2.alloctype.begin(); it != varInfo2.alloctype.end(); ++it) { if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) { varInfo->conditionalAlloc.erase(it->first); varInfo1.erase(it->first); } } alloctype.insert(varInfo1.alloctype.begin(), varInfo1.alloctype.end()); alloctype.insert(varInfo2.alloctype.begin(), varInfo2.alloctype.end()); possibleUsage.insert(varInfo1.possibleUsage.begin(), varInfo1.possibleUsage.end()); possibleUsage.insert(varInfo2.possibleUsage.begin(), varInfo2.possibleUsage.end()); } } // unknown control.. (TODO: handle loops) else if ((Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) || Token::simpleMatch(tok, "do {")) { varInfo->clear(); break; } // return else if (tok->str() == "return") { ret(tok, *varInfo); varInfo->clear(); } // throw else if (mTokenizer->isCPP() && tok->str() == "throw") { bool tryFound = false; const Scope* scope = tok->scope(); while (scope && scope->isExecutable()) { if (scope->type == Scope::eTry) tryFound = true; scope = scope->nestedIn; } // If the execution leaves the function then treat it as return if (!tryFound) ret(tok, *varInfo); varInfo->clear(); } // delete else if (mTokenizer->isCPP() && tok->str() == "delete") { const Token * delTok = tok; const bool arrayDelete = Token::simpleMatch(tok->next(), "[ ]"); if (arrayDelete) tok = tok->tokAt(3); else tok = tok->next(); if (tok->str() == "(") tok = tok->next(); while (Token::Match(tok, "%name% ::|.")) tok = tok->tokAt(2); const bool isnull = tok->hasKnownIntValue() && tok->values().front().intvalue == 0; if (!isnull && tok->varId() && tok->strAt(1) != "[") { const VarInfo::AllocInfo allocation(arrayDelete ? NEW_ARRAY : NEW, VarInfo::DEALLOC, delTok); changeAllocStatus(varInfo, allocation, tok, tok); } } // Function call.. else if (isFunctionCall(ftok)) { const Token * openingPar = isFunctionCall(ftok); const Library::AllocFunc* af = mSettings->library.getDeallocFuncInfo(ftok); VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC, ftok); if (allocation.type == 0) allocation.status = VarInfo::NOALLOC; functionCall(ftok, openingPar, varInfo, allocation, af); tok = ftok->next()->link(); // Handle scopes that might be noreturn if (allocation.status == VarInfo::NOALLOC && Token::simpleMatch(tok, ") ; }")) { const std::string &functionName(tok->link()->previous()->str()); bool unknown = false; if (mTokenizer->isScopeNoReturn(tok->tokAt(2), &unknown)) { if (!unknown) varInfo->clear(); else if (!mSettings->library.isLeakIgnore(functionName) && !mSettings->library.isUse(functionName)) varInfo->possibleUsageAll(functionName); } } continue; } // goto => weird execution path else if (tok->str() == "goto") { varInfo->clear(); } // continue/break else if (Token::Match(tok, "continue|break ;")) { varInfo->clear(); } // Check smart pointer else if (Token::Match(ftok, "%name% <") && mSettings->library.isSmartPointer(tok)) { const Token * typeEndTok = ftok->linkAt(1); if (!Token::Match(typeEndTok, "> %var% {|( %var% ,|)|}")) continue; tok = typeEndTok->linkAt(2); const int varid = typeEndTok->next()->varId(); if (isPointerReleased(typeEndTok->tokAt(2), endToken, varid)) continue; bool arrayDelete = false; if (Token::findsimplematch(ftok->next(), "[ ]", typeEndTok)) arrayDelete = true; // Check deleter const Token * deleterToken = nullptr; const Token * endDeleterToken = nullptr; const Library::AllocFunc* af = nullptr; if (Token::Match(ftok, "unique_ptr < %type% ,")) { deleterToken = ftok->tokAt(4); endDeleterToken = typeEndTok; } else if (Token::Match(typeEndTok, "> %var% {|( %var% ,")) { deleterToken = typeEndTok->tokAt(5); endDeleterToken = typeEndTok->linkAt(2); } if (deleterToken) { // Skip the decaying plus in expressions like +[](T*){} if (deleterToken->str() == "+") { deleterToken = deleterToken->next(); } // Check if its a pointer to a function const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken); if (dtok) { af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1)); } else { const Token * tscopeStart = nullptr; const Token * tscopeEnd = nullptr; // If the deleter is a lambda, check if it calls the dealloc function if (deleterToken->str() == "[" && Token::simpleMatch(deleterToken->link(), "] (") && // TODO: Check for mutable keyword Token::simpleMatch(deleterToken->link()->linkAt(1), ") {")) { tscopeStart = deleterToken->link()->linkAt(1)->tokAt(1); tscopeEnd = tscopeStart->link(); // If the deleter is a class, check if class calls the dealloc function } else if ((dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken)) && dtok->type()) { const Scope * tscope = dtok->type()->classScope; if (tscope) { tscopeStart = tscope->bodyStart; tscopeEnd = tscope->bodyEnd; } } if (tscopeStart && tscopeEnd) { for (const Token *tok2 = tscopeStart; tok2 != tscopeEnd; tok2 = tok2->next()) { af = mSettings->library.getDeallocFuncInfo(tok2); if (af) break; } } } } const Token * vtok = typeEndTok->tokAt(3); const VarInfo::AllocInfo allocation(af ? af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, ftok); changeAllocStatus(varInfo, allocation, vtok, vtok); } } } const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const tok, VarInfo *varInfo) { // Deallocation and then dereferencing pointer.. if (tok->varId() > 0) { // TODO : Write a separate checker for this that uses valueFlowForward. const std::map::const_iterator var = varInfo->alloctype.find(tok->varId()); if (var != varInfo->alloctype.end()) { bool unknown = false; if (var->second.status == VarInfo::DEALLOC && CheckNullPointer::isPointerDeRef(tok, unknown, mSettings) && !unknown) { deallocUseError(tok, tok->str()); } else if (Token::simpleMatch(tok->tokAt(-2), "= &")) { varInfo->erase(tok->varId()); } else if (Token::Match(tok->previous(), "= %var% [;,)]")) { varInfo->erase(tok->varId()); } } else if (Token::Match(tok->previous(), "& %name% = %var% ;")) { varInfo->referenced.insert(tok->tokAt(2)->varId()); } } // check for function call const Token * const openingPar = isFunctionCall(tok); if (openingPar) { const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(tok); VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC, tok); if (alloc.type == 0) alloc.status = VarInfo::NOALLOC; functionCall(tok, openingPar, varInfo, alloc, nullptr); return openingPar->link(); } return nullptr; } void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok) { const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(fTok); if (f && f->arg == -1 && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(fTok)) { const Token* argTok = getArguments(fTok).at(f->reallocArg - 1); VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()]; VarInfo::AllocInfo& retAlloc = alloctype[retTok->varId()]; if (argAlloc.type != 0 && argAlloc.type != f->groupId) mismatchError(fTok, argAlloc.allocTok, argTok->str()); argAlloc.status = VarInfo::DEALLOC; argAlloc.allocTok = fTok; retAlloc.type = f->groupId; retAlloc.status = VarInfo::ALLOC; retAlloc.allocTok = fTok; } } void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg) { std::map &alloctype = varInfo->alloctype; const std::map::iterator var = alloctype.find(arg->varId()); if (var != alloctype.end()) { if (allocation.status == VarInfo::NOALLOC) { // possible usage varInfo->possibleUsage[arg->varId()] = tok->str(); if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&") varInfo->erase(arg->varId()); } else if (var->second.managed()) { doubleFreeError(tok, var->second.allocTok, arg->str(), allocation.type); } else if (var->second.type != allocation.type) { // mismatching allocation and deallocation mismatchError(tok, var->second.allocTok, arg->str()); varInfo->erase(arg->varId()); } else { // deallocation var->second.status = allocation.status; var->second.type = allocation.type; var->second.allocTok = allocation.allocTok; } } else if (allocation.status != VarInfo::NOALLOC) { alloctype[arg->varId()].status = VarInfo::DEALLOC; alloctype[arg->varId()].allocTok = tok; } } void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af) { // Ignore function call? if (mSettings->library.isLeakIgnore(tokName->str())) return; if (mSettings->library.getReallocFuncInfo(tokName)) return; const Token * const tokFirstArg = tokOpeningPar->next(); if (!tokFirstArg || tokFirstArg->str() == ")") { // no arguments return; } int argNr = 1; for (const Token *arg = tokFirstArg; arg; arg = arg->nextArgument()) { if (mTokenizer->isCPP() && arg->str() == "new") { arg = arg->next(); if (Token::simpleMatch(arg, "( std :: nothrow )")) arg = arg->tokAt(5); } // Skip casts while (arg && arg->isCast()) arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1(); const Token * const argTypeStartTok = arg; while (Token::Match(arg, "%name% .|:: %name%")) arg = arg->tokAt(2); if (Token::Match(arg, "%var% [-,)] !!.") || Token::Match(arg, "& %var%")) { // goto variable if (arg->str() == "&") arg = arg->next(); const bool isnull = arg->hasKnownIntValue() && arg->values().front().intvalue == 0; // Is variable allocated? if (!isnull && (!af || af->arg == argNr)) changeAllocStatus(varInfo, allocation, tokName, arg); } // Check smart pointer else if (Token::Match(arg, "%name% < %type%") && mSettings->library.isSmartPointer(argTypeStartTok)) { const Token * typeEndTok = arg->linkAt(1); const Token * allocTok = nullptr; if (!Token::Match(typeEndTok, "> {|( %var% ,|)|}")) continue; bool arrayDelete = false; if (Token::findsimplematch(arg->next(), "[ ]", typeEndTok)) arrayDelete = true; // Check deleter const Token * deleterToken = nullptr; const Token * endDeleterToken = nullptr; const Library::AllocFunc* sp_af = nullptr; if (Token::Match(arg, "unique_ptr < %type% ,")) { deleterToken = arg->tokAt(4); endDeleterToken = typeEndTok; } else if (Token::Match(typeEndTok, "> {|( %var% ,")) { deleterToken = typeEndTok->tokAt(4); endDeleterToken = typeEndTok->linkAt(1); } if (deleterToken) { // Check if its a pointer to a function const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken); if (dtok) { sp_af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1)); } else { // If the deleter is a class, check if class calls the dealloc function dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken); if (dtok && dtok->type()) { const Scope * tscope = dtok->type()->classScope; for (const Token *tok2 = tscope->bodyStart; tok2 != tscope->bodyEnd; tok2 = tok2->next()) { sp_af = mSettings->library.getDeallocFuncInfo(tok2); if (sp_af) { allocTok = tok2; break; } } } } } const Token * vtok = typeEndTok->tokAt(2); const VarInfo::AllocInfo sp_allocation(sp_af ? sp_af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, allocTok); changeAllocStatus(varInfo, sp_allocation, vtok, vtok); } else { checkTokenInsideExpression(arg, varInfo); } // TODO: check each token in argument expression (could contain multiple variables) argNr++; } } void CheckLeakAutoVar::leakIfAllocated(const Token *vartok, const VarInfo &varInfo) { const std::map &alloctype = varInfo.alloctype; const std::map &possibleUsage = varInfo.possibleUsage; const std::map::const_iterator var = alloctype.find(vartok->varId()); if (var != alloctype.end() && var->second.status == VarInfo::ALLOC) { const std::map::const_iterator use = possibleUsage.find(vartok->varId()); if (use == possibleUsage.end()) { leakError(vartok, vartok->str(), var->second.type); } else { configurationInfo(vartok, use->second); } } } void CheckLeakAutoVar::ret(const Token *tok, const VarInfo &varInfo) { const std::map &alloctype = varInfo.alloctype; const std::map &possibleUsage = varInfo.possibleUsage; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (std::map::const_iterator it = alloctype.begin(); it != alloctype.end(); ++it) { // don't warn if variable is conditionally allocated if (!it->second.managed() && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end()) continue; // don't warn if there is a reference of the variable if (varInfo.referenced.find(it->first) != varInfo.referenced.end()) continue; const int varid = it->first; const Variable *var = symbolDatabase->getVariableFromVarId(varid); if (var) { bool used = false; for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == ";") break; if (!Token::Match(tok2, "return|(|{|,")) continue; const Token* tok3 = tok2->next(); while (tok3 && tok3->isCast() && tok3->valueType() && (tok3->valueType()->pointer || (tok3->valueType()->typeSize(*mSettings) == 0) || (tok3->valueType()->typeSize(*mSettings) >= mSettings->sizeof_pointer))) tok3 = tok3->astOperand2() ? tok3->astOperand2() : tok3->astOperand1(); if (Token::Match(tok3, "%varid%", varid)) tok2 = tok3->next(); else if (Token::Match(tok3, "& %varid% . %name%", varid)) tok2 = tok3->tokAt(4); else continue; if (Token::Match(tok2, "[});,]")) { used = true; break; } } // return deallocated pointer if (used && it->second.status == VarInfo::DEALLOC) deallocReturnError(tok, it->second.allocTok, var->name()); else if (!used && !it->second.managed()) { const std::map::const_iterator use = possibleUsage.find(varid); if (use == possibleUsage.end()) { leakError(tok, var->name(), it->second.type); } else { configurationInfo(tok, use->second); } } } } } cppcheck-1.90/lib/checkleakautovar.h000066400000000000000000000137111357737443600175040ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkleakautovarH #define checkleakautovarH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "library.h" #include #include #include class ErrorLogger; class Settings; class Token; class Tokenizer; class CPPCHECKLIB VarInfo { public: enum AllocStatus { OWNED = -2, DEALLOC = -1, NOALLOC = 0, ALLOC = 1 }; struct AllocInfo { AllocStatus status; /** Allocation type. If it is a positive value then it corresponds to * a Library allocation id. A negative value is a builtin * checkleakautovar allocation type. */ int type; const Token * allocTok; AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC, const Token* allocTok_ = nullptr) : status(status_), type(type_), allocTok(allocTok_) {} bool managed() const { return status < 0; } }; std::map alloctype; std::map possibleUsage; std::set conditionalAlloc; std::set referenced; void clear() { alloctype.clear(); possibleUsage.clear(); conditionalAlloc.clear(); referenced.clear(); } void erase(nonneg int varid) { alloctype.erase(varid); possibleUsage.erase(varid); conditionalAlloc.erase(varid); referenced.erase(varid); } void swap(VarInfo &other) { alloctype.swap(other.alloctype); possibleUsage.swap(other.possibleUsage); conditionalAlloc.swap(other.conditionalAlloc); referenced.swap(other.referenced); } /** set possible usage for all variables */ void possibleUsageAll(const std::string &functionName); void print(); }; /// @addtogroup Checks /// @{ /** * @brief Check for leaks */ class CPPCHECKLIB CheckLeakAutoVar : public Check { public: /** This constructor is used when registering the CheckLeakAutoVar */ CheckLeakAutoVar() : Check(myName()) { } /** This constructor is used when running checks. */ CheckLeakAutoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckLeakAutoVar checkLeakAutoVar(tokenizer, settings, errorLogger); checkLeakAutoVar.check(); } private: /** check for leaks in all scopes */ void check(); /** check for leaks in a function scope */ void checkScope(const Token * const startToken, VarInfo *varInfo, std::set notzero, nonneg int recursiveCount); /** Check token inside expression. * @param tok token inside expression. * @param varInfo Variable info * @return next token to process (if no other checks needed for this token). NULL if other checks could be performed. */ const Token * checkTokenInsideExpression(const Token * const tok, VarInfo *varInfo); /** parse function call */ void functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af); /** parse changes in allocation status */ void changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg); /** update allocation status if reallocation function */ void changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok); /** return. either "return" or end of variable scope is seen */ void ret(const Token *tok, const VarInfo &varInfo); /** if variable is allocated then there is a leak */ void leakIfAllocated(const Token *vartok, const VarInfo &varInfo); void leakError(const Token* tok, const std::string &varname, int type); void mismatchError(const Token* deallocTok, const Token* allocTok, const std::string &varname); void deallocUseError(const Token *tok, const std::string &varname); void deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname); void doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type); /** message: user configuration is needed to complete analysis */ void configurationInfo(const Token* tok, const std::string &functionName); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckLeakAutoVar c(nullptr, settings, errorLogger); c.deallocReturnError(nullptr, nullptr, "p"); c.configurationInfo(nullptr, "f"); // user configuration is needed to complete analysis c.doubleFreeError(nullptr, nullptr, "varname", 0); } static std::string myName() { return "Leaks (auto variables)"; } std::string classInfo() const OVERRIDE { return "Detect when a auto variable is allocated but not deallocated or deallocated twice.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkleakautovarH cppcheck-1.90/lib/checkmemoryleak.cpp000066400000000000000000001333261357737443600176730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkmemoryleak.h" #include "astutils.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include "valueflow.h" #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckMemoryLeakInFunction instance1; CheckMemoryLeakInClass instance2; CheckMemoryLeakStructMember instance3; CheckMemoryLeakNoVar instance4; } // CWE ID used: static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE401(401U); // Improper Release of Memory Before Removing Last Reference ('Memory Leak') static const CWE CWE771(771U); // Missing Reference to Active Allocated Resource static const CWE CWE772(772U); // Missing Release of Resource after Effective Lifetime /** List of functions that can be ignored when searching for memory leaks. * These functions don't take the address of the given pointer * This list contains function names with const parameters e.g.: atof(const char *) * TODO: This list should be replaced by in .cfg files. */ static const std::set call_func_white_list = { "_open", "_wopen", "access", "adjtime", "asctime_r", "asprintf", "chdir", "chmod", "chown" , "creat", "ctime_r", "execl", "execle", "execlp", "execv", "execve", "fchmod", "fcntl" , "fdatasync", "fclose", "flock", "fmemopen", "fnmatch", "fopen", "fopencookie", "for", "free" , "freopen", "fseeko", "fstat", "fsync", "ftello", "ftruncate", "getgrnam", "gethostbyaddr", "gethostbyname" , "getnetbyname", "getopt", "getopt_long", "getprotobyname", "getpwnam", "getservbyname", "getservbyport" , "glob", "gmtime", "gmtime_r", "if", "index", "inet_addr", "inet_aton", "inet_network", "initgroups" , "ioctl", "link", "localtime_r", "lockf", "lseek", "lstat", "mkdir", "mkfifo", "mknod", "mkstemp" , "obstack_printf", "obstack_vprintf", "open", "opendir", "parse_printf_format", "pathconf" , "perror", "popen", "posix_fadvise", "posix_fallocate", "pread", "psignal", "pwrite", "read", "readahead" , "readdir", "readdir_r", "readlink", "readv", "realloc", "regcomp", "return", "rewinddir", "rindex" , "rmdir", "scandir", "seekdir", "setbuffer", "sethostname", "setlinebuf", "sizeof", "strdup" , "stat", "stpcpy", "strcasecmp", "stricmp", "strncasecmp", "switch" , "symlink", "sync_file_range", "telldir", "tempnam", "time", "typeid", "unlink" , "utime", "utimes", "vasprintf", "while", "wordexp", "write", "writev" }; //--------------------------------------------------------------------------- CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack) const { // What we may have... // * var = (char *)malloc(10); // * var = new char[10]; // * var = strdup("hello"); // * var = strndup("hello", 3); if (tok2 && tok2->str() == "(") { tok2 = tok2->link(); tok2 = tok2 ? tok2->next() : nullptr; } if (! tok2) return No; if (tok2->str() == "::") tok2 = tok2->next(); if (! tok2->isName()) return No; if (!Token::Match(tok2, "%name% ::|. %type%")) { // Using realloc.. AllocType reallocType = getReallocationType(tok2, varid); if (reallocType != No) return reallocType; if (mTokenizer_->isCPP() && tok2->str() == "new") { if (tok2->strAt(1) == "(" && !Token::Match(tok2->next(),"( std| ::| nothrow )")) return No; if (tok2->astOperand1() && (tok2->astOperand1()->str() == "[" || (tok2->astOperand1()->astOperand1() && tok2->astOperand1()->astOperand1()->str() == "["))) return NewArray; const Token *typeTok = tok2->next(); while (Token::Match(typeTok, "%name% :: %name%")) typeTok = typeTok->tokAt(2); if (typeTok->type() && typeTok->type()->isClassType()) { const Scope *classScope = typeTok->type()->classScope; if (classScope && classScope->numConstructors > 0) return No; } return New; } if (mSettings_->posix()) { if (Token::Match(tok2, "open|openat|creat|mkstemp|mkostemp|socket (")) { // simple sanity check of function parameters.. // TODO: Make such check for all these functions const int num = numberOfArguments(tok2); if (tok2->str() == "open" && num != 2 && num != 3) return No; // is there a user function with this name? if (tok2->function()) return No; return Fd; } if (Token::simpleMatch(tok2, "popen (")) return Pipe; } // Does tok2 point on a Library allocation function? const int alloctype = mSettings_->library.getAllocId(tok2, -1); if (alloctype > 0) { if (alloctype == mSettings_->library.deallocId("free")) return Malloc; if (alloctype == mSettings_->library.deallocId("fclose")) return File; return Library::ismemory(alloctype) ? OtherMem : OtherRes; } } while (Token::Match(tok2,"%name% ::|. %type%")) tok2 = tok2->tokAt(2); // User function const Function* func = tok2->function(); if (func == nullptr) return No; // Prevent recursion if (callstack && std::find(callstack->begin(), callstack->end(), func) != callstack->end()) return No; std::list cs; if (!callstack) callstack = &cs; callstack->push_back(func); return functionReturnType(func, callstack); } CheckMemoryLeak::AllocType CheckMemoryLeak::getReallocationType(const Token *tok2, nonneg int varid) const { // What we may have... // * var = (char *)realloc(..; if (tok2 && tok2->str() == "(") { tok2 = tok2->link(); tok2 = tok2 ? tok2->next() : nullptr; } if (! tok2) return No; if (!Token::Match(tok2, "%name% (")) return No; const Library::AllocFunc *f = mSettings_->library.getReallocFuncInfo(tok2); if (!(f && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(tok2))) return No; const Token* arg = getArguments(tok2).at(f->reallocArg - 1); while (arg && arg->isCast()) arg = arg->astOperand1(); while (arg && arg->isUnaryOp("*")) arg = arg->astOperand1(); if (varid > 0 && !Token::Match(arg, "%varid% [,)]", varid)) return No; const int realloctype = mSettings_->library.getReallocId(tok2, -1); if (realloctype > 0) { if (realloctype == mSettings_->library.deallocId("free")) return Malloc; if (realloctype == mSettings_->library.deallocId("fclose")) return File; return Library::ismemory(realloctype) ? OtherMem : OtherRes; } return No; } CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, nonneg int varid) const { if (mTokenizer_->isCPP() && tok->str() == "delete" && tok->astOperand1()) { const Token* vartok = tok->astOperand1(); if (Token::Match(vartok, ".|::")) vartok = vartok->astOperand2(); if (vartok && vartok->varId() == varid) { if (tok->strAt(1) == "[") return NewArray; return New; } } if (tok->str() == "::") tok = tok->next(); if (Token::Match(tok, "%name% (")) { if (Token::simpleMatch(tok, "fcloseall ( )")) return File; int argNr = 1; for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->nextArgument()) { const Token* vartok = tok2; while (Token::Match(vartok, "%name% .|::")) vartok = vartok->tokAt(2); if (Token::Match(vartok, "%varid% )|,|-", varid)) { if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )")) return Malloc; if (mSettings_->posix()) { if (tok->str() == "close") return Fd; if (tok->str() == "pclose") return Pipe; } // Does tok point on a Library deallocation function? const int dealloctype = mSettings_->library.getDeallocId(tok, argNr); if (dealloctype > 0) { if (dealloctype == mSettings_->library.deallocId("free")) return Malloc; if (dealloctype == mSettings_->library.deallocId("fclose")) return File; return Library::ismemory(dealloctype) ? OtherMem : OtherRes; } } argNr++; } } return No; } bool CheckMemoryLeak::isReopenStandardStream(const Token *tok) const { if (getReallocationType(tok, 0) == File) { const Library::AllocFunc *f = mSettings_->library.getReallocFuncInfo(tok); if (f && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(tok)) { const Token* arg = getArguments(tok).at(f->reallocArg - 1); if (Token::Match(arg, "stdin|stdout|stderr")) return true; } } return false; } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- void CheckMemoryLeak::memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const { if (alloctype == CheckMemoryLeak::File || alloctype == CheckMemoryLeak::Pipe || alloctype == CheckMemoryLeak::Fd || alloctype == CheckMemoryLeak::OtherRes) resourceLeakError(tok, varname); else memleakError(tok, varname); } //--------------------------------------------------------------------------- void CheckMemoryLeak::reportErr(const Token *tok, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const { std::list callstack; if (tok) callstack.push_back(tok); reportErr(callstack, severity, id, msg, cwe); } void CheckMemoryLeak::reportErr(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const { const ErrorLogger::ErrorMessage errmsg(callstack, mTokenizer_ ? &mTokenizer_->list : nullptr, severity, id, msg, cwe, false); if (mErrorLogger_) mErrorLogger_->reportErr(errmsg); else Check::reportError(errmsg); } void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "memleak", "$symbol:" + varname + "\nMemory leak: $symbol", CWE(401U)); } void CheckMemoryLeak::memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const { reportErr(tok, Severity::error, "memleakOnRealloc", "$symbol:" + varname + "\nCommon " + reallocfunction + " mistake: \'$symbol\' nulled but not freed upon failure", CWE(401U)); } void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &varname) const { std::string errmsg("Resource leak"); if (!varname.empty()) errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol"; reportErr(tok, Severity::error, "resourceLeak", errmsg, CWE(775U)); } void CheckMemoryLeak::deallocDeallocError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocDealloc", "$symbol:" + varname + "\nDeallocating a deallocated pointer: $symbol", CWE(415U)); } void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocuse", "$symbol:" + varname + "\nDereferencing '$symbol' after it is deallocated / released", CWE(416U)); } void CheckMemoryLeak::mismatchSizeError(const Token *tok, const std::string &sz) const { reportErr(tok, Severity::error, "mismatchSize", "The allocated size " + sz + " is not a multiple of the underlying type's size.", CWE(131U)); } void CheckMemoryLeak::mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const { reportErr(callstack, Severity::error, "mismatchAllocDealloc", "$symbol:" + varname + "\nMismatching allocation and deallocation: $symbol", CWE(762U)); } CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Function* func, std::list *callstack) const { if (!func || !func->hasBody() || !func->functionScope) return No; // Get return pointer.. int varid = 0; for (const Token *tok2 = func->functionScope->bodyStart; tok2 != func->functionScope->bodyEnd; tok2 = tok2->next()) { if (const Token *endOfLambda = findLambdaEndToken(tok2)) tok2 = endOfLambda; if (tok2->str() == "{" && !tok2->scope()->isExecutable()) tok2 = tok2->link(); if (tok2->str() == "return") { const AllocType allocType = getAllocationType(tok2->next(), 0, callstack); if (allocType != No) return allocType; if (tok2->scope() != func->functionScope || !tok2->astOperand1()) return No; const Token* tok = tok2->astOperand1(); if (Token::Match(tok, ".|::")) tok = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); if (tok) varid = tok->varId(); break; } } // Not returning pointer value.. if (varid == 0) return No; // If variable is not local then alloctype shall be "No" // Todo: there can be false negatives about mismatching allocation/deallocation. // => Generate "alloc ; use ;" if variable is not local? const Variable *var = mTokenizer_->getSymbolDatabase()->getVariableFromVarId(varid); if (!var || !var->isLocal() || var->isStatic()) return No; // Check if return pointer is allocated.. AllocType allocType = No; for (const Token* tok = func->functionScope->bodyStart; tok != func->functionScope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%varid% =", varid)) { allocType = getAllocationType(tok->tokAt(2), varid, callstack); } if (Token::Match(tok, "= %varid% ;", varid)) { return No; } if (!mTokenizer_->isC() && Token::Match(tok, "[(,] %varid% [,)]", varid)) { return No; } if (Token::Match(tok, "[(,] & %varid% [.,)]", varid)) { return No; } if (Token::Match(tok, "[;{}] %varid% .", varid)) { return No; } if (allocType == No && tok->str() == "return") return No; } return allocType; } const char *CheckMemoryLeak::functionArgAlloc(const Function *func, nonneg int targetpar, AllocType &allocType) const { allocType = No; if (!func || !func->functionScope) return ""; if (!Token::simpleMatch(func->retDef, "void")) return ""; std::list::const_iterator arg = func->argumentList.begin(); for (; arg != func->argumentList.end(); ++arg) { if (arg->index() == targetpar-1) break; } if (arg == func->argumentList.end()) return ""; // Is ** if (!arg->isPointer()) return ""; const Token* tok = arg->typeEndToken(); tok = tok->previous(); if (tok->str() != "*") return ""; // Check if pointer is allocated. bool realloc = false; for (tok = func->functionScope->bodyStart; tok && tok != func->functionScope->bodyEnd; tok = tok->next()) { if (tok->varId() == arg->declarationId()) { if (Token::Match(tok->tokAt(-3), "free ( * %name% )")) { realloc = true; allocType = No; } else if (Token::Match(tok->previous(), "* %name% =")) { allocType = getAllocationType(tok->tokAt(2), arg->declarationId()); if (allocType != No) { if (realloc) return "realloc"; return "alloc"; } } else { // unhandled variable usage: bailout return ""; } } } return ""; } static bool notvar(const Token *tok, nonneg int varid) { if (!tok) return false; if (Token::Match(tok, "&&|;")) return notvar(tok->astOperand1(),varid) || notvar(tok->astOperand2(),varid); if (tok->str() == "(" && Token::Match(tok->astOperand1(), "UNLIKELY|LIKELY")) return notvar(tok->astOperand2(), varid); const Token *vartok = astIsVariableComparison(tok, "==", "0"); return vartok && (vartok->varId() == varid); } static bool ifvar(const Token *tok, nonneg int varid, const std::string &comp, const std::string &rhs) { if (!Token::simpleMatch(tok, "if (")) return false; const Token *condition = tok->next()->astOperand2(); if (condition && condition->str() == "(" && Token::Match(condition->astOperand1(), "UNLIKELY|LIKELY")) condition = condition->astOperand2(); if (!condition || condition->str() == "&&") return false; const Token *vartok = astIsVariableComparison(condition, comp, rhs); return (vartok && vartok->varId() == varid); } bool CheckMemoryLeakInFunction::test_white_list(const std::string &funcname, const Settings *settings, bool cpp) { return ((call_func_white_list.find(funcname)!=call_func_white_list.end()) || settings->library.isLeakIgnore(funcname) || (cpp && funcname == "delete")); } //--------------------------------------------------------------------------- // Check for memory leaks due to improper realloc() usage. // Below, "a" may be set to null without being freed if realloc() cannot // allocate the requested memory: // a = malloc(10); a = realloc(a, 100); //--------------------------------------------------------------------------- static bool isNoArgument(const SymbolDatabase* symbolDatabase, nonneg int varid) { const Variable* var = symbolDatabase->getVariableFromVarId(varid); return var && !var->isArgument(); } void CheckMemoryLeakInFunction::checkReallocUsage() { // only check functions const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { // Search for the "var = realloc(var, 100" pattern within this function for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->varId() > 0 && Token::Match(tok, "%name% =")) { // Get the parenthesis in "realloc(" const Token* parTok = tok->next()->astOperand2(); // Skip casts while (parTok && parTok->isCast()) parTok = parTok->astOperand1(); if (!parTok) continue; const Token *const reallocTok = parTok->astOperand1(); if (!reallocTok) continue; const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(reallocTok); if (!(f && f->arg == -1 && mSettings->library.isnotnoreturn(reallocTok))) continue; const AllocType allocType = getReallocationType(reallocTok, tok->varId()); if (!((allocType == Malloc || allocType == OtherMem))) continue; const Token* arg = getArguments(reallocTok).at(f->reallocArg - 1); while (arg && arg->isCast()) arg = arg->astOperand1(); const Token* tok2 = tok; while (arg && arg->isUnaryOp("*") && tok2 && tok2->astParent() && tok2->astParent()->isUnaryOp("*")) { arg = arg->astOperand1(); tok2 = tok2->astParent(); } if (!arg || !tok2) continue; if (!((tok->varId() == arg->varId()) && isNoArgument(symbolDatabase, tok->varId()))) continue; // Check that another copy of the pointer wasn't saved earlier in the function if (Token::findmatch(scope->bodyStart, "%name% = %varid% ;", tok, tok->varId()) || Token::findmatch(scope->bodyStart, "[{};] %varid% = *| %name% .| %name%| [;=]", tok, tok->varId())) continue; const Token* tokEndRealloc = reallocTok->linkAt(1); // Check that the allocation isn't followed immediately by an 'if (!var) { error(); }' that might handle failure if (Token::simpleMatch(tokEndRealloc->next(), "; if (") && notvar(tokEndRealloc->tokAt(3)->astOperand2(), tok->varId())) { const Token* tokEndBrace = tokEndRealloc->linkAt(3)->linkAt(1); if (tokEndBrace && mTokenizer->isScopeNoReturn(tokEndBrace)) continue; } memleakUponReallocFailureError(tok, reallocTok->str(), tok->str()); } } } } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Checks for memory leaks in classes.. //--------------------------------------------------------------------------- void CheckMemoryLeakInClass::check() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // only check classes and structures for (const Scope * scope : symbolDatabase->classAndStructScopes) { for (const Variable &var : scope->varlist) { if (!var.isStatic() && var.isPointer()) { // allocation but no deallocation of private variables in public function.. const Token *tok = var.typeStartToken(); // Either it is of standard type or a non-derived type if (tok->isStandardType() || (var.type() && var.type()->derivedFrom.empty())) { if (var.isPrivate()) checkPublicFunctions(scope, var.nameToken()); variable(scope, var.nameToken()); } } } } } void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarname) { const std::string& varname = tokVarname->str(); const int varid = tokVarname->varId(); const std::string& classname = scope->className; // Check if member variable has been allocated and deallocated.. CheckMemoryLeak::AllocType memberAlloc = CheckMemoryLeak::No; CheckMemoryLeak::AllocType memberDealloc = CheckMemoryLeak::No; bool allocInConstructor = false; bool deallocInDestructor = false; // Inspect member functions for (const Function &func : scope->functionList) { const bool constructor = func.isConstructor(); const bool destructor = func.isDestructor(); if (!func.hasBody()) { if (destructor) { // implementation for destructor is not seen => assume it deallocates all variables properly deallocInDestructor = true; memberDealloc = CheckMemoryLeak::Many; } continue; } bool body = false; const Token *end = func.functionScope->bodyEnd; for (const Token *tok = func.arg->link(); tok != end; tok = tok->next()) { if (tok == func.functionScope->bodyStart) body = true; else { if (!body) { if (!Token::Match(tok, ":|, %varid% (", varid)) continue; } // Allocate.. if (!body || Token::Match(tok, "%varid% =", varid)) { // var1 = var2 = ... // bail out if (tok->strAt(-1) == "=") return; // Foo::var1 = .. // bail out when not same class if (tok->strAt(-1) == "::" && tok->strAt(-2) != scope->className) return; AllocType alloc = getAllocationType(tok->tokAt(body ? 2 : 3), 0); if (alloc != CheckMemoryLeak::No) { if (constructor) allocInConstructor = true; if (memberAlloc != No && memberAlloc != alloc) alloc = CheckMemoryLeak::Many; if (alloc != CheckMemoryLeak::Many && memberDealloc != CheckMemoryLeak::No && memberDealloc != CheckMemoryLeak::Many && memberDealloc != alloc) { std::list callstack; callstack.push_back(tok); mismatchAllocDealloc(callstack, classname + "::" + varname); } memberAlloc = alloc; } } if (!body) continue; // Deallocate.. AllocType dealloc = getDeallocationType(tok, varid); // some usage in the destructor => assume it's related // to deallocation if (destructor && tok->str() == varname) dealloc = CheckMemoryLeak::Many; if (dealloc != CheckMemoryLeak::No) { if (destructor) deallocInDestructor = true; // several types of allocation/deallocation? if (memberDealloc != CheckMemoryLeak::No && memberDealloc != dealloc) dealloc = CheckMemoryLeak::Many; if (dealloc != CheckMemoryLeak::Many && memberAlloc != CheckMemoryLeak::No && memberAlloc != Many && memberAlloc != dealloc) { std::list callstack; callstack.push_back(tok); mismatchAllocDealloc(callstack, classname + "::" + varname); } memberDealloc = dealloc; } // Function call .. possible deallocation else if (Token::Match(tok->previous(), "[{};] %name% (")) { if (!CheckMemoryLeakInFunction::test_white_list(tok->str(), mSettings, mTokenizer->isCPP())) { return; } } } } } if (allocInConstructor && !deallocInDestructor) { unsafeClassError(tokVarname, classname, classname + "::" + varname /*, memberAlloc*/); } else if (memberAlloc != CheckMemoryLeak::No && memberDealloc == CheckMemoryLeak::No) { unsafeClassError(tokVarname, classname, classname + "::" + varname /*, memberAlloc*/); } } void CheckMemoryLeakInClass::unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname) { if (!mSettings->isEnabled(Settings::STYLE)) return; reportError(tok, Severity::style, "unsafeClassCanLeak", "$symbol:" + classname + "\n" "$symbol:" + varname + "\n" "Class '" + classname + "' is unsafe, '" + varname + "' can leak by wrong usage.\n" "The class '" + classname + "' is unsafe, wrong usage can cause memory/resource leaks for '" + varname + "'. This can for instance be fixed by adding proper cleanup in the destructor.", CWE398, false); } void CheckMemoryLeakInClass::checkPublicFunctions(const Scope *scope, const Token *classtok) { // Check that public functions deallocate the pointers that they allocate. // There is no checking how these functions are used and therefore it // isn't established if there is real leaks or not. if (!mSettings->isEnabled(Settings::WARNING)) return; const int varid = classtok->varId(); // Parse public functions.. // If they allocate member variables, they should also deallocate for (const Function &func : scope->functionList) { if ((func.type == Function::eFunction || func.type == Function::eOperatorEqual) && func.access == AccessControl::Public && func.hasBody()) { const Token *tok2 = func.functionScope->bodyStart->next(); if (Token::Match(tok2, "%varid% =", varid)) { const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(2), varid); if (alloc != CheckMemoryLeak::No) publicAllocationError(tok2, tok2->str()); } else if (Token::Match(tok2, "%type% :: %varid% =", varid) && tok2->str() == scope->className) { const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(4), varid); if (alloc != CheckMemoryLeak::No) publicAllocationError(tok2, tok2->strAt(2)); } } } } void CheckMemoryLeakInClass::publicAllocationError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "publicAllocationError", "$symbol:" + varname + "\nPossible leak in public function. The pointer '$symbol' is not deallocated before it is allocated.", CWE398, false); } void CheckMemoryLeakStructMember::check() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->isLocal() || var->isStatic() || var->isReference()) continue; if (var->typeEndToken()->isStandardType()) continue; checkStructVariable(var); } } bool CheckMemoryLeakStructMember::isMalloc(const Variable *variable) { const int declarationId(variable->declarationId()); bool alloc = false; for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { if (Token::Match(tok2, "= %varid% [;=]", declarationId)) { return false; } else if (Token::Match(tok2, "%varid% = malloc|kmalloc (", declarationId)) { alloc = true; } } return alloc; } void CheckMemoryLeakStructMember::checkStructVariable(const Variable * const variable) { // Is struct variable a pointer? if (variable->isPointer()) { // Check that variable is allocated with malloc if (!isMalloc(variable)) return; } else if (!mTokenizer->isC() && (!variable->typeScope() || variable->typeScope()->getDestructor())) { // For non-C code a destructor might cleanup members return; } // Check struct.. int indentlevel2 = 0; for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel2; else if (tok2->str() == "}") { if (indentlevel2 == 0) break; --indentlevel2; } // Unknown usage of struct /** @todo Check how the struct is used. Only bail out if necessary */ else if (Token::Match(tok2, "[(,] %varid% [,)]", variable->declarationId())) break; // Struct member is allocated => check if it is also properly deallocated.. else if (Token::Match(tok2->previous(), "[;{}] %varid% . %var% =", variable->declarationId())) { if (getAllocationType(tok2->tokAt(4), tok2->tokAt(2)->varId()) == AllocType::No) continue; const int structid(variable->declarationId()); const int structmemberid(tok2->tokAt(2)->varId()); // This struct member is allocated.. check that it is deallocated int indentlevel3 = indentlevel2; for (const Token *tok3 = tok2; tok3; tok3 = tok3->next()) { if (tok3->str() == "{") ++indentlevel3; else if (tok3->str() == "}") { if (indentlevel3 == 0) { memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); break; } --indentlevel3; } // Deallocating the struct member.. else if (getDeallocationType(tok3, structmemberid) != AllocType::No) { // If the deallocation happens at the base level, don't check this member anymore if (indentlevel3 == 0) break; // deallocating and then returning from function in a conditional block => // skip ahead out of the block bool ret = false; while (tok3) { if (tok3->str() == "return") ret = true; else if (tok3->str() == "{" || tok3->str() == "}") break; tok3 = tok3->next(); } if (!ret || !tok3 || tok3->str() != "}") break; --indentlevel3; continue; } // Deallocating the struct.. else if (Token::Match(tok3, "free|kfree ( %varid% )", structid)) { if (indentlevel2 == 0) memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); break; } // failed allocation => skip code.. else if (Token::simpleMatch(tok3, "if (") && notvar(tok3->next()->astOperand2(), structmemberid)) { // Goto the ")" tok3 = tok3->next()->link(); // make sure we have ") {".. it should be if (!Token::simpleMatch(tok3, ") {")) break; // Goto the "}" tok3 = tok3->next()->link(); } // succeeded allocation else if (ifvar(tok3, structmemberid, "!=", "0")) { // goto the ")" tok3 = tok3->next()->link(); // check if the variable is deallocated or returned.. int indentlevel4 = 0; for (const Token *tok4 = tok3; tok4; tok4 = tok4->next()) { if (tok4->str() == "{") ++indentlevel4; else if (tok4->str() == "}") { --indentlevel4; if (indentlevel4 == 0) break; } else if (Token::Match(tok4, "free|kfree ( %var% . %varid% )", structmemberid)) { break; } } // was there a proper deallocation? if (indentlevel4 > 0) break; } // Returning from function.. else if (tok3->str() == "return") { // Returning from function without deallocating struct member? if (!Token::Match(tok3, "return %varid% ;", structid) && !Token::Match(tok3, "return & %varid%", structid) && !(Token::Match(tok3, "return %varid% . %var%", structid) && tok3->tokAt(3)->varId() == structmemberid)) { memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); } break; } // struct assignment.. else if (Token::Match(tok3, "= %varid% ;", structid)) { break; } else if (Token::Match(tok3, "= %var% . %varid% ;", structmemberid)) { break; } // goto isn't handled well.. bail out even though there might be leaks else if (tok3->str() == "goto") break; // using struct in a function call.. else if (Token::Match(tok3, "%name% (")) { // Calling non-function / function that doesn't deallocate? if (CheckMemoryLeakInFunction::test_white_list(tok3->str(), mSettings, mTokenizer->isCPP())) continue; // Check if the struct is used.. bool deallocated = false; const Token* const end4 = tok3->linkAt(1); for (const Token *tok4 = tok3; tok4 != end4; tok4 = tok4->next()) { if (Token::Match(tok4, "[(,] &| %varid% [,)]", structid)) { /** @todo check if the function deallocates the memory */ deallocated = true; break; } if (Token::Match(tok4, "[(,] &| %varid% . %name% [,)]", structid)) { /** @todo check if the function deallocates the memory */ deallocated = true; break; } } if (deallocated) break; } } } } } void CheckMemoryLeakNoVar::check() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // only check functions for (const Scope * scope : symbolDatabase->functionScopes) { // Checks if a call to an allocation function like malloc() is made and its return value is not assigned. checkForUnusedReturnValue(scope); // Checks to see if a function is called with memory allocated for an argument that // could be leaked if a function called for another argument throws. checkForUnsafeArgAlloc(scope); // Check for leaks where a the return value of an allocation function like malloc() is an input argument, // for example f(malloc(1)), where f is known to not release the input argument. checkForUnreleasedInputArgument(scope); } } //--------------------------------------------------------------------------- // Checks if an input argument to a function is the return value of an allocation function // like malloc(), and the function does not release it. //--------------------------------------------------------------------------- void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) { // parse the executable scope until tok is reached... for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { // allocating memory in parameter for function call.. if (!Token::Match(tok, "%name% (")) continue; // check if the output of the function is assigned const Token* tok2 = tok->next()->astParent(); while (tok2 && tok2->isCast()) tok2 = tok2->astParent(); if (tok2 && tok2->isAssignmentOp()) continue; const std::string& functionName = tok->str(); if ((mTokenizer->isCPP() && functionName == "delete") || functionName == "free" || functionName == "fclose" || functionName == "realloc" || functionName == "return") continue; if (!CheckMemoryLeakInFunction::test_white_list(functionName, mSettings, mTokenizer->isCPP())) continue; const std::vector args = getArguments(tok); for (const Token* arg : args) { if (arg->isOp()) continue; while (arg->astOperand1()) arg = arg->astOperand1(); if (getAllocationType(arg, 0) == No) continue; if (isReopenStandardStream(arg)) continue; functionCallLeak(arg, arg->str(), functionName); } } } //--------------------------------------------------------------------------- // Checks if a call to an allocation function like malloc() is made and its return value is not assigned. //--------------------------------------------------------------------------- void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; if (tok->varId()) continue; const AllocType allocType = getAllocationType(tok, 0); if (allocType == No) continue; if (tok != tok->next()->astOperand1()) continue; if (isReopenStandardStream(tok)) continue; // get ast parent, skip casts const Token *parent = tok->next()->astParent(); while (parent && parent->str() == "(" && !parent->astOperand2()) parent = parent->astParent(); if (!parent) { // Check if we are in a C++11 constructor const Token * closingBrace = Token::findmatch(tok, "}|;"); if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%")) continue; returnValueNotUsedError(tok, tok->str()); } else if (Token::Match(parent, "%comp%|!")) { returnValueNotUsedError(tok, tok->str()); } } } //--------------------------------------------------------------------------- // Check if an exception could cause a leak in an argument constructed with // shared_ptr/unique_ptr. For example, in the following code, it is possible // that if g() throws an exception, the memory allocated by "new int(42)" // could be leaked. See stackoverflow.com/questions/19034538/ // why-is-there-memory-leak-while-using-shared-ptr-as-a-function-parameter // // void x() { // f(shared_ptr(new int(42)), g()); // } //--------------------------------------------------------------------------- void CheckMemoryLeakNoVar::checkForUnsafeArgAlloc(const Scope *scope) { // This test only applies to C++ source if (!mTokenizer->isCPP() || !mSettings->inconclusive || !mSettings->isEnabled(Settings::WARNING)) return; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%name% (")) { const Token *endParamToken = tok->next()->link(); const Token* pointerType = nullptr; const Token* functionCalled = nullptr; // Scan through the arguments to the function call for (const Token *tok2 = tok->tokAt(2); tok2 && tok2 != endParamToken; tok2 = tok2->nextArgument()) { const Function *func = tok2->function(); const bool isNothrow = func && (func->isAttributeNothrow() || func->isThrow()); if (Token::Match(tok2, "shared_ptr|unique_ptr <") && Token::Match(tok2->next()->link(), "> ( new %name%")) { pointerType = tok2; } else if (!isNothrow) { if (Token::Match(tok2, "%name% (")) functionCalled = tok2; else if (tok2->isName() && Token::simpleMatch(tok2->next()->link(), "> (")) functionCalled = tok2; } } if (pointerType && functionCalled) { std::string functionName = functionCalled->str(); if (functionCalled->strAt(1) == "<") { functionName += '<'; for (const Token* tok2 = functionCalled->tokAt(2); tok2 != functionCalled->next()->link(); tok2 = tok2->next()) functionName += tok2->str(); functionName += '>'; } std::string objectTypeName; for (const Token* tok2 = pointerType->tokAt(2); tok2 != pointerType->next()->link(); tok2 = tok2->next()) objectTypeName += tok2->str(); unsafeArgAllocError(tok, functionName, pointerType->str(), objectTypeName); } } } } void CheckMemoryLeakNoVar::functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall) { reportError(loc, Severity::error, "leakNoVarFunctionCall", "Allocation with " + alloc + ", " + functionCall + " doesn't release it.", CWE772, false); } void CheckMemoryLeakNoVar::returnValueNotUsedError(const Token *tok, const std::string &alloc) { reportError(tok, Severity::error, "leakReturnValNotUsed", "$symbol:" + alloc + "\nReturn value of allocation function '$symbol' is not stored.", CWE771, false); } void CheckMemoryLeakNoVar::unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string& objType) { const std::string factoryFunc = ptrType == "shared_ptr" ? "make_shared" : "make_unique"; reportError(tok, Severity::warning, "leakUnsafeArgAlloc", "$symbol:" + funcName + "\n" "Unsafe allocation. If $symbol() throws, memory could be leaked. Use " + factoryFunc + "<" + objType + ">() instead.", CWE401, true); // Inconclusive because funcName may never throw } cppcheck-1.90/lib/checkmemoryleak.h000066400000000000000000000326541357737443600173420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkmemoryleakH #define checkmemoryleakH //--------------------------------------------------------------------------- /** * @file * * %Check for memory leaks * * The checking is split up into three specialized classes. * - CheckMemoryLeakInFunction can detect when a function variable is allocated but not deallocated properly. * - CheckMemoryLeakInClass can detect when a class variable is allocated but not deallocated properly. * - CheckMemoryLeakStructMember checks allocation/deallocation of structs and struct members */ #include "check.h" #include "config.h" #include "errorlogger.h" #include "tokenize.h" #include #include class Function; class Scope; class Settings; class SymbolDatabase; class Token; class Variable; /// @addtogroup Core /// @{ /** @brief Base class for memory leaks checking */ class CPPCHECKLIB CheckMemoryLeak { private: /** For access to the tokens */ const Tokenizer * const mTokenizer_; /** ErrorLogger used to report errors */ ErrorLogger * const mErrorLogger_; /** Enabled standards */ const Settings * const mSettings_; /** * Report error. Similar with the function Check::reportError * @param tok the token where the error occurs * @param severity the severity of the bug * @param id type of message * @param msg text * @param cwe cwe number */ void reportErr(const Token *tok, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const; /** * Report error. Similar with the function Check::reportError * @param callstack callstack of error * @param severity the severity of the bug * @param id type of message * @param msg text * @param cwe cwe number */ void reportErr(const std::list &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const; public: CheckMemoryLeak() = delete; CheckMemoryLeak(const CheckMemoryLeak &) = delete; void operator=(const CheckMemoryLeak &) = delete; CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e, const Settings *s) : mTokenizer_(t), mErrorLogger_(e), mSettings_(s) { } /** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */ enum AllocType { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many }; void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const; /** * @brief Get type of deallocation at given position * @param tok position * @param varid variable id * @return type of deallocation */ AllocType getDeallocationType(const Token *tok, nonneg int varid) const; /** * @brief Get type of allocation at given position */ AllocType getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack = nullptr) const; /** * @brief Get type of reallocation at given position */ AllocType getReallocationType(const Token *tok2, nonneg int varid) const; /** * Check if token reopens a standard stream * @param tok token to check */ bool isReopenStandardStream(const Token *tok) const; /** * Report that there is a memory leak (new/malloc/etc) * @param tok token where memory is leaked * @param varname name of variable */ void memleakError(const Token *tok, const std::string &varname) const; /** * Report that there is a resource leak (fopen/popen/etc) * @param tok token where resource is leaked * @param varname name of variable */ void resourceLeakError(const Token *tok, const std::string &varname) const; /** * @brief Report error: deallocating a deallocated pointer * @param tok token where error occurs * @param varname name of variable */ void deallocDeallocError(const Token *tok, const std::string &varname) const; void deallocuseError(const Token *tok, const std::string &varname) const; void mismatchSizeError(const Token *tok, const std::string &sz) const; void mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const; void memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const; /** What type of allocated memory does the given function return? */ AllocType functionReturnType(const Function* func, std::list *callstack = nullptr) const; /** Function allocates pointed-to argument (a la asprintf)? */ const char *functionArgAlloc(const Function *func, nonneg int targetpar, AllocType &allocType) const; }; /// @} /// @addtogroup Checks /// @{ /** * @brief %CheckMemoryLeakInFunction detects when a function variable is allocated but not deallocated properly. * * The checking is done by looking at each function variable separately. By repeating these 4 steps over and over: * -# locate a function variable * -# create a simple token list that describes the usage of the function variable. * -# simplify the token list. * -# finally, check if the simplified token list contain any leaks. */ class CPPCHECKLIB CheckMemoryLeakInFunction : private Check, public CheckMemoryLeak { public: /** @brief This constructor is used when registering this class */ CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) { } /** @brief This constructor is used when running checks */ CheckMemoryLeakInFunction(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckMemoryLeakInFunction checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.checkReallocUsage(); } /** @brief Unit testing : testing the white list */ static bool test_white_list(const std::string &funcname, const Settings *settings, bool cpp); /** * Checking for a memory leak caused by improper realloc usage. */ void checkReallocUsage(); private: /** Report all possible errors (for the --errorlist) */ void getErrorMessages(ErrorLogger *e, const Settings *settings) const OVERRIDE { CheckMemoryLeakInFunction c(nullptr, settings, e); c.memleakError(nullptr, "varname"); c.resourceLeakError(nullptr, "varname"); c.deallocDeallocError(nullptr, "varname"); c.deallocuseError(nullptr, "varname"); c.mismatchSizeError(nullptr, "sz"); const std::list callstack; c.mismatchAllocDealloc(callstack, "varname"); c.memleakUponReallocFailureError(nullptr, "realloc", "varname"); } /** * Get name of class (--doc) * @return name of class */ static std::string myName() { return "Memory leaks (function variables)"; } /** * Get class information (--doc) * @return Wiki formatted information about this class */ std::string classInfo() const OVERRIDE { return "Is there any allocated memory when a function goes out of scope\n"; } }; /** * @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor */ class CPPCHECKLIB CheckMemoryLeakInClass : private Check, private CheckMemoryLeak { public: CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) { } CheckMemoryLeakInClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) { } void runChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) OVERRIDE { if (!tokenizr->isCPP()) return; CheckMemoryLeakInClass checkMemoryLeak(tokenizr, settings, errLog); checkMemoryLeak.check(); } void check(); private: void variable(const Scope *scope, const Token *tokVarname); /** Public functions: possible double-allocation */ void checkPublicFunctions(const Scope *scope, const Token *classtok); void publicAllocationError(const Token *tok, const std::string &varname); void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname); void getErrorMessages(ErrorLogger *e, const Settings *settings) const OVERRIDE { CheckMemoryLeakInClass c(nullptr, settings, e); c.publicAllocationError(nullptr, "varname"); c.unsafeClassError(nullptr, "class", "class::varname"); } static std::string myName() { return "Memory leaks (class variables)"; } std::string classInfo() const OVERRIDE { return "If the constructor allocate memory then the destructor must deallocate it.\n"; } }; /** @brief detect simple memory leaks for struct members */ class CPPCHECKLIB CheckMemoryLeakStructMember : private Check, private CheckMemoryLeak { public: CheckMemoryLeakStructMember() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) { } CheckMemoryLeakStructMember(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckMemoryLeakStructMember checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.check(); } void check(); private: /** Is local variable allocated with malloc? */ static bool isMalloc(const Variable *variable); void checkStructVariable(const Variable * const variable); void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const OVERRIDE { } static std::string myName() { return "Memory leaks (struct members)"; } std::string classInfo() const OVERRIDE { return "Don't forget to deallocate struct members\n"; } }; /** @brief detect simple memory leaks (address not taken) */ class CPPCHECKLIB CheckMemoryLeakNoVar : private Check, private CheckMemoryLeak { public: CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) { } CheckMemoryLeakNoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckMemoryLeakNoVar checkMemoryLeak(tokenizer, settings, errorLogger); checkMemoryLeak.check(); } void check(); private: /** * @brief %Check if an input argument to a function is the return value of an allocation function * like malloc(), and the function does not release it. * @param scope The scope of the function to check. */ void checkForUnreleasedInputArgument(const Scope *scope); /** * @brief %Check if a call to an allocation function like malloc() is made and its return value is not assigned. * @param scope The scope of the function to check. */ void checkForUnusedReturnValue(const Scope *scope); /** * @brief %Check if an exception could cause a leak in an argument constructed with shared_ptr/unique_ptr. * @param scope The scope of the function to check. */ void checkForUnsafeArgAlloc(const Scope *scope); void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall); void returnValueNotUsedError(const Token* tok, const std::string &alloc); void unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string &objType); void getErrorMessages(ErrorLogger *e, const Settings *settings) const OVERRIDE { CheckMemoryLeakNoVar c(nullptr, settings, e); c.functionCallLeak(nullptr, "funcName", "funcName"); c.returnValueNotUsedError(nullptr, "funcName"); c.unsafeArgAllocError(nullptr, "funcName", "shared_ptr", "int"); } static std::string myName() { return "Memory leaks (address not taken)"; } std::string classInfo() const OVERRIDE { return "Not taking the address to allocated memory\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkmemoryleakH cppcheck-1.90/lib/checknullpointer.cpp000066400000000000000000000667131357737443600201060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checknullpointer.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckNullPointer instance; } static const CWE CWE476(476U); // NULL Pointer Dereference static const CWE CWE682(682U); // Incorrect Calculation //--------------------------------------------------------------------------- static bool checkNullpointerFunctionCallPlausibility(const Function* func, unsigned int arg) { return !func || (func->argCount() >= arg && func->getArgumentVar(arg - 1) && func->getArgumentVar(arg - 1)->isPointer()); } /** * @brief parse a function call and extract information about variable usage * @param tok first token * @param var variables that the function read / write. * @param library --library files data */ void CheckNullPointer::parseFunctionCall(const Token &tok, std::list &var, const Library *library) { if (Token::Match(&tok, "%name% ( )") || !tok.tokAt(2)) return; const std::vector args = getArguments(&tok); if (library || tok.function() != nullptr) { for (int argnr = 1; argnr <= args.size(); ++argnr) { const Token *param = args[argnr - 1]; if (library && library->isnullargbad(&tok, argnr) && checkNullpointerFunctionCallPlausibility(tok.function(), argnr)) var.push_back(param); else if (tok.function()) { const Variable* argVar = tok.function()->getArgumentVar(argnr-1); if (argVar && argVar->isStlStringType() && !argVar->isArrayOrPointer()) var.push_back(param); } } } if (library && library->formatstr_function(&tok)) { const int formatStringArgNr = library->formatstr_argno(&tok); if (formatStringArgNr < 0 || formatStringArgNr >= args.size()) return; // 1st parameter.. if (Token::Match(&tok, "snprintf|vsnprintf|fnprintf|vfnprintf") && args.size() > 1 && !(args[1] && args[1]->hasKnownIntValue() && args[1]->getKnownIntValue() == 0)) // Only if length (second parameter) is not zero var.push_back(args[0]); if (args[formatStringArgNr]->tokType() != Token::eString) return; const std::string &formatString = args[formatStringArgNr]->strValue(); int argnr = formatStringArgNr + 1; const bool scan = library->formatstr_scan(&tok); bool percent = false; for (std::string::const_iterator i = formatString.begin(); i != formatString.end(); ++i) { if (*i == '%') { percent = !percent; } else if (percent) { percent = false; bool _continue = false; while (!std::isalpha((unsigned char)*i)) { if (*i == '*') { if (scan) _continue = true; else argnr++; } ++i; if (i == formatString.end()) return; } if (_continue) continue; if (argnr < args.size() && (*i == 'n' || *i == 's' || scan)) var.push_back(args[argnr]); if (*i != 'm') // %m is a non-standard glibc extension that requires no parameter argnr++; } } } } namespace { const std::set stl_stream = { "fstream", "ifstream", "iostream", "istream", "istringstream", "ofstream", "ostream", "ostringstream", "stringstream", "wistringstream", "wostringstream", "wstringstream" }; } /** * Is there a pointer dereference? Everything that should result in * a nullpointer dereference error message will result in a true * return value. If it's unknown if the pointer is dereferenced false * is returned. * @param tok token for the pointer * @param unknown it is not known if there is a pointer dereference (could be reported as a debug message) * @return true => there is a dereference */ bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown) const { return isPointerDeRef(tok, unknown, mSettings); } bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Settings *settings) { unknown = false; // Is pointer used as function parameter? if (Token::Match(tok->previous(), "[(,] %name% [,)]") && settings) { const Token *ftok = tok->previous(); while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); ftok = ftok->previous(); } if (ftok && ftok->previous()) { std::list varlist; parseFunctionCall(*ftok->previous(), varlist, &settings->library); if (std::find(varlist.begin(), varlist.end(), tok) != varlist.end()) { return true; } } } if (tok->str() == "(" && !tok->scope()->isExecutable()) return false; const Token* parent = tok->astParent(); if (!parent) return false; if (parent->str() == "." && parent->astOperand2() == tok) return isPointerDeRef(parent, unknown, settings); const bool firstOperand = parent->astOperand1() == tok; while (parent->str() == "(" && (parent->astOperand2() == nullptr && parent->strAt(1) != ")")) { // Skip over casts parent = parent->astParent(); if (!parent) return false; } // Dereferencing pointer.. if (parent->isUnaryOp("*") && !Token::Match(parent->tokAt(-2), "sizeof|decltype|typeof")) return true; // array access if (firstOperand && parent->str() == "[" && (!parent->astParent() || parent->astParent()->str() != "&")) return true; // address of member variable / array element const Token *parent2 = parent; while (Token::Match(parent2, "[|.")) parent2 = parent2->astParent(); if (parent2 != parent && parent2 && parent2->isUnaryOp("&")) return false; // read/write member variable if (firstOperand && parent->originalName() == "->" && (!parent->astParent() || parent->astParent()->str() != "&")) { if (!parent->astParent() || parent->astParent()->str() != "(" || parent->astParent() == tok->previous()) return true; unknown = true; return false; } if (Token::Match(tok, "%name% (")) return true; if (Token::Match(tok, "%var% = %var% .") && tok->varId() == tok->tokAt(2)->varId()) return true; // std::string dereferences nullpointers if (Token::Match(parent->tokAt(-3), "std :: string|wstring (") && tok->strAt(1) == ")") return true; if (Token::Match(parent->previous(), "%name% (") && tok->strAt(1) == ")") { const Variable* var = tok->tokAt(-2)->variable(); if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) return true; } // streams dereference nullpointers if (Token::Match(parent, "<<|>>") && !firstOperand) { const Variable* var = tok->variable(); if (var && var->isPointer() && Token::Match(var->typeStartToken(), "char|wchar_t")) { // Only outputting or reading to char* can cause problems const Token* tok2 = parent; // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:")) break; } if (Token::Match(tok2, "std :: cout|cin|cerr")) return true; if (tok2 && tok2->varId() != 0) { const Variable* var2 = tok2->variable(); if (var2 && var2->isStlType(stl_stream)) return true; } } } const Variable *ovar = nullptr; if (Token::Match(parent, "+|==|!=") || (parent->str() == "=" && !firstOperand)) { if (parent->astOperand1() == tok && parent->astOperand2()) ovar = parent->astOperand2()->variable(); else if (parent->astOperand1() && parent->astOperand2() == tok) ovar = parent->astOperand1()->variable(); } if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType()) return true; // assume that it's not a dereference (no false positives) return false; } void CheckNullPointer::nullPointerLinkedList() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // looping through items in a linked list in a inner loop. // Here is an example: // for (const Token *tok = tokens; tok; tok = tok->next) { // if (tok->str() == "hello") // tok = tok->next; // <- tok might become a null pointer! // } for (const Scope &forScope : symbolDatabase->scopeList) { const Token* const tok1 = forScope.classDef; // search for a "for" scope.. if (forScope.type != Scope::eFor || !tok1) continue; // is there any dereferencing occurring in the for statement const Token* end2 = tok1->linkAt(1); for (const Token *tok2 = tok1->tokAt(2); tok2 != end2; tok2 = tok2->next()) { // Dereferencing a variable inside the "for" parentheses.. if (Token::Match(tok2, "%var% . %name%")) { // Is this variable a pointer? const Variable *var = tok2->variable(); if (!var || !var->isPointer()) continue; // Variable id for dereferenced variable const unsigned int varid(tok2->varId()); if (Token::Match(tok2->tokAt(-2), "%varid% ?", varid)) continue; // Check usage of dereferenced variable in the loop.. // TODO: Move this to ValueFlow for (const Scope *innerScope : forScope.nestedList) { if (innerScope->type != Scope::eWhile) continue; // TODO: are there false negatives for "while ( %varid% ||" if (Token::Match(innerScope->classDef->next(), "( %varid% &&|)", varid)) { // Make sure there is a "break" or "return" inside the loop. // Without the "break" a null pointer could be dereferenced in the // for statement. for (const Token *tok4 = innerScope->bodyStart; tok4; tok4 = tok4->next()) { if (tok4 == forScope.bodyEnd) { const ValueFlow::Value v(innerScope->classDef, 0LL); nullPointerError(tok1, var->name(), &v, false); break; } // There is a "break" or "return" inside the loop. // TODO: there can be false negatives. There could still be // execution paths that are not properly terminated else if (tok4->str() == "break" || tok4->str() == "return") break; } } } } } } } static bool isNullablePointer(const Token* tok, const Settings* settings) { if (!tok) return false; if (Token::simpleMatch(tok, "new") && tok->varId() == 0) return false; if (astIsPointer(tok)) return true; if (astIsSmartPointer(tok)) return true; if (Token::simpleMatch(tok, ".")) return isNullablePointer(tok->astOperand2(), settings); if (const Variable* var = tok->variable()) { return (var->isPointer() || var->isSmartPointer()); } return false; } void CheckNullPointer::nullPointerByDeRefAndChec() { const bool printInconclusive = (mSettings->inconclusive); for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) { tok = tok->next()->link(); continue; } if (Token::Match(tok, "%num%|%char%|%str%")) continue; if (!isNullablePointer(tok, mSettings)) continue; // Can pointer be NULL? const ValueFlow::Value *value = tok->getValue(0); if (!value) continue; if (!printInconclusive && value->isInconclusive()) continue; // Pointer dereference. bool unknown = false; if (!isPointerDeRef(tok,unknown)) { if (unknown) nullPointerError(tok, tok->expressionString(), value, true); continue; } nullPointerError(tok, tok->expressionString(), value, value->isInconclusive()); } } void CheckNullPointer::nullPointer() { nullPointerLinkedList(); nullPointerByDeRefAndChec(); } namespace { const std::set stl_istream = { "fstream", "ifstream", "iostream", "istream", "istringstream", "stringstream", "wistringstream", "wstringstream" }; } /** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body continue; const Token *tok = scope->bodyStart; if (scope->function->isConstructor()) tok = scope->function->token; // Check initialization list for (; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) tok = tok->next()->link(); else if (Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp()) { nullPointerError(tok); } } else if (Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) nullPointerError(tok); else if (Token::Match(tok->previous(), "!!. %name% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::Match(tok->tokAt(2), "0|NULL|nullptr )") && tok->varId()) { // constructor call const Variable *var = tok->variable(); if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) nullPointerError(tok); } else { // function call std::list var; parseFunctionCall(*tok, var, &mSettings->library); // is one of the var items a NULL pointer? for (const Token *vartok : var) { if (vartok->hasKnownIntValue() && vartok->getKnownIntValue() == 0) nullPointerError(vartok); } } } else if (Token::Match(tok, "std :: string|wstring ( 0|NULL|nullptr )")) nullPointerError(tok); else if (Token::Match(tok->previous(), "::|. %name% (")) { const std::vector &args = getArguments(tok); for (int argnr = 0; argnr < args.size(); ++argnr) { const Token *argtok = args[argnr]; if (!argtok->hasKnownIntValue()) continue; if (argtok->values().front().intvalue != 0) continue; if (mSettings->library.isnullargbad(tok, argnr+1)) nullPointerError(argtok); } } else if (Token::Match(tok->previous(), ">> 0|NULL|nullptr")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well const Token* tok2 = tok->previous(); // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:|(")) break; } if (tok2 && tok2->previous() && tok2->previous()->str()=="(") continue; if (Token::simpleMatch(tok2, "std :: cin")) nullPointerError(tok); if (tok2 && tok2->varId() != 0) { const Variable *var = tok2->variable(); if (var && var->isStlType(stl_istream)) nullPointerError(tok); } } const Variable *ovar = nullptr; const Token *tokNull = nullptr; if (Token::Match(tok, "0|NULL|nullptr ==|!=|>|>=|<|<= %var%")) { if (!Token::Match(tok->tokAt(3),".|[")) { ovar = tok->tokAt(2)->variable(); tokNull = tok; } } else if (Token::Match(tok, "%var% ==|!=|>|>=|<|<= 0|NULL|nullptr") || Token::Match(tok, "%var% =|+ 0|NULL|nullptr )|]|,|;|+")) { ovar = tok->variable(); tokNull = tok->tokAt(2); } if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType() && tokNull && tokNull->originalName() != "'\\0'") nullPointerError(tokNull); } } } void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) { const std::string errmsgcond("$symbol:" + varname + '\n' + ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible null pointer dereference: $symbol."); const std::string errmsgdefarg("$symbol:" + varname + "\nPossible null pointer dereference if the default parameter value is used: $symbol"); if (!tok) { reportError(tok, Severity::error, "nullPointer", "Null pointer dereference", CWE476, false); reportError(tok, Severity::warning, "nullPointerDefaultArg", errmsgdefarg, CWE476, false); reportError(tok, Severity::warning, "nullPointerRedundantCheck", errmsgcond, CWE476, false); return; } if (!value) { reportError(tok, Severity::error, "nullPointer", "Null pointer dereference", CWE476, inconclusive); return; } if (!mSettings->isEnabled(value, inconclusive)) return; const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer dereference"); if (value->condition) { reportError(errorPath, Severity::warning, "nullPointerRedundantCheck", errmsgcond, CWE476, inconclusive || value->isInconclusive()); } else if (value->defaultArg) { reportError(errorPath, Severity::warning, "nullPointerDefaultArg", errmsgdefarg, CWE476, inconclusive || value->isInconclusive()); } else { std::string errmsg; errmsg = std::string(value->isKnown() ? "Null" : "Possible null") + " pointer dereference"; if (!varname.empty()) errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol"; reportError(errorPath, value->isKnown() ? Severity::error : Severity::warning, "nullPointer", errmsg, CWE476, inconclusive || value->isInconclusive()); } } void CheckNullPointer::arithmetic() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "-|+|+=|-=|++|--")) continue; const Token *pointerOperand; const Token *numericOperand; if (tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->pointer != 0) { pointerOperand = tok->astOperand1(); numericOperand = tok->astOperand2(); } else if (tok->astOperand2() && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->pointer != 0) { pointerOperand = tok->astOperand2(); numericOperand = tok->astOperand1(); } else continue; if (numericOperand && numericOperand->valueType() && !numericOperand->valueType()->isIntegral()) continue; MathLib::bigint checkValue = 0; // When using an assign op, the value read from // valueflow has already been updated, so instead of // checking for zero we check that the value is equal // to RHS if (tok->astOperand2() && tok->astOperand2()->hasKnownIntValue()) { if (tok->str() == "-=") checkValue -= tok->astOperand2()->values().front().intvalue; else if (tok->str() == "+=") checkValue = tok->astOperand2()->values().front().intvalue; } const ValueFlow::Value *value = pointerOperand->getValue(checkValue); if (!value) continue; if (!mSettings->inconclusive && value->isInconclusive()) continue; if (value->condition && !mSettings->isEnabled(Settings::WARNING)) continue; if (value->condition) redundantConditionWarning(tok, value, value->condition, value->isInconclusive()); else pointerArithmeticError(tok, value, value->isInconclusive()); } } } static std::string arithmeticTypeString(const Token *tok) { if (tok && tok->str()[0] == '-') return "subtraction"; else if (tok && tok->str()[0] == '+') return "addition"; else return "arithmetic"; } void CheckNullPointer::pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) { std::string arithmetic = arithmeticTypeString(tok); std::string errmsg; if (tok && tok->str()[0] == '-') { errmsg = "Overflow in pointer arithmetic, NULL pointer is subtracted."; } else { errmsg = "Pointer " + arithmetic + " with NULL pointer."; } const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer " + arithmetic); reportError(errorPath, Severity::error, "nullPointerArithmetic", errmsg, CWE682, inconclusive); } void CheckNullPointer::redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive) { std::string arithmetic = arithmeticTypeString(tok); std::string errmsg; if (tok && tok->str()[0] == '-') { errmsg = ValueFlow::eitherTheConditionIsRedundant(condition) + " or there is overflow in pointer " + arithmetic + "."; } else { errmsg = ValueFlow::eitherTheConditionIsRedundant(condition) + " or there is pointer arithmetic with NULL pointer."; } const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer " + arithmetic); reportError(errorPath, Severity::warning, "nullPointerArithmeticRedundantCheck", errmsg, CWE682, inconclusive); } std::string CheckNullPointer::MyFileInfo::toString() const { return CTU::toString(unsafeUsage); } static bool isUnsafeUsage(const Check *check, const Token *vartok, MathLib::bigint *value) { (void)value; const CheckNullPointer *checkNullPointer = dynamic_cast(check); bool unknown = false; return checkNullPointer && checkNullPointer->isPointerDeRef(vartok, unknown); } Check::FileInfo *CheckNullPointer::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { CheckNullPointer check(tokenizer, settings, nullptr); const std::list &unsafeUsage = CTU::getUnsafeUsage(tokenizer, settings, &check, ::isUnsafeUsage); if (unsafeUsage.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } Check::FileInfo * CheckNullPointer::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { const std::list &unsafeUsage = CTU::loadUnsafeUsageListFromXml(xmlElement); if (unsafeUsage.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } bool CheckNullPointer::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { if (!ctu) return false; bool foundErrors = false; (void)settings; // This argument is unused const std::map> callsMap = ctu->getCallsMap(); for (Check::FileInfo *fi1 : fileInfo) { const MyFileInfo *fi = dynamic_cast(fi1); if (!fi) continue; for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeUsage) { for (int warning = 0; warning <= 1; warning++) { if (warning == 1 && !settings.isEnabled(Settings::WARNING)) break; const std::list &locationList = ctu->getErrorPath(CTU::FileInfo::InvalidValueType::null, unsafeUsage, callsMap, "Dereferencing argument ARG that is null", nullptr, warning); if (locationList.empty()) continue; const ErrorLogger::ErrorMessage errmsg(locationList, emptyString, warning ? Severity::warning : Severity::error, "Null pointer dereference: " + unsafeUsage.myArgumentName, "ctunullpointer", CWE476, false); errorLogger.reportErr(errmsg); foundErrors = true; break; } } } return foundErrors; } cppcheck-1.90/lib/checknullpointer.h000066400000000000000000000134061357737443600175420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checknullpointerH #define checknullpointerH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "ctu.h" #include "valueflow.h" #include #include class ErrorLogger; class Library; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** @brief check for null pointer dereferencing */ class CPPCHECKLIB CheckNullPointer : public Check { public: /** @brief This constructor is used when registering the CheckNullPointer */ CheckNullPointer() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckNullPointer(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckNullPointer checkNullPointer(tokenizer, settings, errorLogger); checkNullPointer.nullPointer(); checkNullPointer.arithmetic(); checkNullPointer.nullConstantDereference(); } /** * @brief parse a function call and extract information about variable usage * @param tok first token * @param var variables that the function read / write. * @param library --library files data */ static void parseFunctionCall(const Token &tok, std::list &var, const Library *library); /** * Is there a pointer dereference? Everything that should result in * a nullpointer dereference error message will result in a true * return value. If it's unknown if the pointer is dereferenced false * is returned. * @param tok token for the pointer * @param unknown it is not known if there is a pointer dereference (could be reported as a debug message) * @return true => there is a dereference */ bool isPointerDeRef(const Token *tok, bool &unknown) const; static bool isPointerDeRef(const Token *tok, bool &unknown, const Settings *settings); /** @brief possible null pointer dereference */ void nullPointer(); /** @brief dereferencing null constant (after Tokenizer::simplifyKnownVariables) */ void nullConstantDereference(); void nullPointerError(const Token *tok) { ValueFlow::Value v(0); v.setKnown(); nullPointerError(tok, "", &v, false); } void nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value* value, bool inconclusive); /* data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: /** function arguments that are dereferenced without checking if they are null */ std::list unsafeUsage; /** Convert MyFileInfo data into xml string */ std::string toString() const OVERRIDE; }; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; private: /** Get error messages. Used by --errorlist */ void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckNullPointer c(nullptr, settings, errorLogger); c.nullPointerError(nullptr, "pointer", nullptr, false); c.pointerArithmeticError(nullptr, nullptr, false); c.redundantConditionWarning(nullptr, nullptr, nullptr, false); } /** Name of check */ static std::string myName() { return "Null pointer"; } /** class info in WIKI format. Used by --doc */ std::string classInfo() const OVERRIDE { return "Null pointers\n" "- null pointer dereferencing\n" "- undefined null pointer arithmetic\n"; } /** * @brief Does one part of the check for nullPointer(). * looping through items in a linked list in a inner loop.. */ void nullPointerLinkedList(); /** * @brief Does one part of the check for nullPointer(). * Dereferencing a pointer and then checking if it's NULL.. */ void nullPointerByDeRefAndChec(); /** undefined null pointer arithmetic */ void arithmetic(); void pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); void redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive); }; /// @} //--------------------------------------------------------------------------- #endif // checknullpointerH cppcheck-1.90/lib/checkother.cpp000066400000000000000000004324121357737443600166450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkother.h" #include "checkuninitvar.h" // CheckUninitVar::isVariableUsage #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include // find_if() #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckOther instance; } static const struct CWE CWE128(128U); // Wrap-around Error static const struct CWE CWE131(131U); // Incorrect Calculation of Buffer Size static const struct CWE CWE197(197U); // Numeric Truncation Error static const struct CWE CWE362(362U); // Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') static const struct CWE CWE369(369U); // Divide By Zero static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE475(475U); // Undefined Behavior for Input to API static const struct CWE CWE482(482U); // Comparing instead of Assigning static const struct CWE CWE561(561U); // Dead Code static const struct CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') static const struct CWE CWE570(570U); // Expression is Always False static const struct CWE CWE571(571U); // Expression is Always True static const struct CWE CWE672(672U); // Operation on a Resource after Expiration or Release static const struct CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const struct CWE CWE683(683U); // Function Call With Incorrect Order of Arguments static const struct CWE CWE686(686U); // Function Call With Incorrect Argument Type static const struct CWE CWE704(704U); // Incorrect Type Conversion or Cast static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const struct CWE CWE768(768U); // Incorrect Short Circuit Evaluation static const struct CWE CWE783(783U); // Operator Precedence Logic Error //---------------------------------------------------------------------------------- // The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value. // If this return value is stored in a character variable and then compared // to EOF, which is an integer, the comparison maybe be false. // // Reference: // - Ticket #160 // - http://www.cplusplus.com/reference/cstdio/fgetc/ // - http://www.cplusplus.com/reference/cstdio/getc/ // - http://www.cplusplus.com/reference/cstdio/getchar/ // - http://www.cplusplus.com/reference/cstdio/ungetc/ ... //---------------------------------------------------------------------------------- void CheckOther::checkCastIntToCharAndBack() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { std::map vars; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // Quick check to see if any of the matches below have any chances if (!Token::Match(tok, "%var%|EOF %comp%|=")) continue; if (Token::Match(tok, "%var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { vars[tok->varId()] = tok->strAt(2); } } else if (Token::Match(tok, "EOF %comp% ( %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { tok = tok->tokAt(3); const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { checkCastIntToCharAndBackError(tok, tok->strAt(2)); } } else if (mTokenizer->isCPP() && (Token::Match(tok, "EOF %comp% ( %var% = std :: cin . get (") || Token::Match(tok, "EOF %comp% ( %var% = cin . get ("))) { tok = tok->tokAt(3); const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { checkCastIntToCharAndBackError(tok, "cin.get"); } } else if (mTokenizer->isCPP() && (Token::Match(tok, "%var% = std :: cin . get (") || Token::Match(tok, "%var% = cin . get ("))) { const Variable *var = tok->variable(); if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { vars[tok->varId()] = "cin.get"; } } else if (Token::Match(tok, "%var% %comp% EOF")) { if (vars.find(tok->varId()) != vars.end()) { checkCastIntToCharAndBackError(tok, vars[tok->varId()]); } } else if (Token::Match(tok, "EOF %comp% %var%")) { tok = tok->tokAt(2); if (vars.find(tok->varId()) != vars.end()) { checkCastIntToCharAndBackError(tok, vars[tok->varId()]); } } } } } void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName) { reportError( tok, Severity::warning, "checkCastIntToCharAndBack", "$symbol:" + strFunctionName + "\n" "Storing $symbol() return value in char variable and then comparing with EOF.\n" "When saving $symbol() return value in char variable there is loss of precision. " " When $symbol() returns EOF this value is truncated. Comparing the char " "variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = $symbol());\" " "loops forever on some compilers/platforms and on other compilers/platforms it will stop " "when the file contains a matching character.", CWE197, false ); } //--------------------------------------------------------------------------- // Clarify calculation precedence for ternary operators. //--------------------------------------------------------------------------- void CheckOther::clarifyCalculation() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // ? operator where lhs is arithmetical expression if (tok->str() != "?" || !tok->astOperand1() || !tok->astOperand1()->isCalculation()) continue; if (!tok->astOperand1()->isArithmeticalOp() && tok->astOperand1()->tokType() != Token::eBitOp) continue; // Is code clarified by parentheses already? const Token *tok2 = tok->astOperand1(); for (; tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ")") break; else if (tok2->str() == "?") { clarifyCalculationError(tok, tok->astOperand1()->str()); break; } } } } } void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) { // suspicious calculation const std::string calc("'a" + op + "b?c:d'"); // recommended calculation #1 const std::string s1("'(a" + op + "b)?c:d'"); // recommended calculation #2 const std::string s2("'a" + op + "(b?c:d)'"); reportError(tok, Severity::style, "clarifyCalculation", "Clarify calculation precedence for '" + op + "' and '?'.\n" "Suspicious calculation. Please use parentheses to clarify the code. " "The code '" + calc + "' should be written as either '" + s1 + "' or '" + s2 + "'.", CWE783, false); } //--------------------------------------------------------------------------- // Clarify (meaningless) statements like *foo++; with parentheses. //--------------------------------------------------------------------------- void CheckOther::clarifyStatement() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "* %name%") && tok->astOperand1()) { const Token *tok2 = tok->previous(); while (tok2 && tok2->str() == "*") tok2 = tok2->previous(); if (tok2 && !tok2->astParent() && Token::Match(tok2, "[{};]")) { tok2 = tok->astOperand1(); if (Token::Match(tok2, "++|-- [;,]")) clarifyStatementError(tok2); } } } } } void CheckOther::clarifyStatementError(const Token *tok) { reportError(tok, Severity::warning, "clarifyStatement", "In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n" "A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. " "Thus, the dereference is meaningless. Did you intend to write '(*A)++;'?", CWE783, false); } //--------------------------------------------------------------------------- // Check for suspicious occurrences of 'if(); {}'. //--------------------------------------------------------------------------- void CheckOther::checkSuspiciousSemicolon() { if (!mSettings->inconclusive || !mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // Look for "if(); {}", "for(); {}" or "while(); {}" for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type == Scope::eIf || scope.type == Scope::eElse || scope.type == Scope::eWhile || scope.type == Scope::eFor) { // Ensure the semicolon is at the same line number as the if/for/while statement // and the {..} block follows it without an extra empty line. if (Token::simpleMatch(scope.bodyStart, "{ ; } {") && scope.bodyStart->previous()->linenr() == scope.bodyStart->tokAt(2)->linenr() && scope.bodyStart->linenr()+1 >= scope.bodyStart->tokAt(3)->linenr()) { suspiciousSemicolonError(scope.classDef); } } } } void CheckOther::suspiciousSemicolonError(const Token* tok) { reportError(tok, Severity::warning, "suspiciousSemicolon", "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", CWE398, true); } //--------------------------------------------------------------------------- // For C++ code, warn if C-style casts are used on pointer types //--------------------------------------------------------------------------- void CheckOther::warningOldStylePointerCast() { // Only valid on C++ code if (!mSettings->isEnabled(Settings::STYLE) || !mTokenizer->isCPP()) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { const Token* tok; if (scope->function && scope->function->isConstructor()) tok = scope->classDef; else tok = scope->bodyStart; for (; tok && tok != scope->bodyEnd; tok = tok->next()) { // Old style pointer casting.. if (!Token::Match(tok, "( const|volatile| const|volatile| %type% * const| ) (| %name%|%num%|%bool%|%char%|%str%")) continue; // skip first "const" in "const Type* const" while (Token::Match(tok->next(), "const|volatile")) tok = tok->next(); const Token* typeTok = tok->next(); // skip second "const" in "const Type* const" if (tok->strAt(3) == "const") tok = tok->next(); const Token *p = tok->tokAt(4); if (p->hasKnownIntValue() && p->values().front().intvalue==0) // Casting nullpointers is safe continue; // Is "type" a class? if (typeTok->type()) cstyleCastError(tok); } } } void CheckOther::cstyleCastError(const Token *tok) { reportError(tok, Severity::style, "cstyleCast", "C-style pointer casting\n" "C-style pointer casting detected. C++ offers four different kinds of casts as replacements: " "static_cast, const_cast, dynamic_cast and reinterpret_cast. A C-style cast could evaluate to " "any of those automatically, thus it is considered safer if the programmer explicitly states " "which kind of cast is expected. See also: https://www.securecoding.cert.org/confluence/display/cplusplus/EXP05-CPP.+Do+not+use+C-style+casts.", CWE398, false); } //--------------------------------------------------------------------------- // float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation //--------------------------------------------------------------------------- void CheckOther::invalidPointerCast() { if (!mSettings->isEnabled(Settings::PORTABILITY)) return; const bool printInconclusive = mSettings->inconclusive; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const Token* toTok = nullptr; const Token* fromTok = nullptr; // Find cast if (Token::Match(tok, "( const|volatile| const|volatile| %type% %type%| const| * )")) { toTok = tok; fromTok = tok->astOperand1(); } else if (Token::simpleMatch(tok, "reinterpret_cast <") && tok->linkAt(1)) { toTok = tok->linkAt(1)->next(); fromTok = toTok->astOperand2(); } if (!fromTok) continue; const ValueType* fromType = fromTok->valueType(); const ValueType* toType = toTok->valueType(); if (!fromType || !toType || !fromType->pointer || !toType->pointer) continue; if (fromType->type != toType->type && fromType->type >= ValueType::Type::BOOL && toType->type >= ValueType::Type::BOOL && (toType->type != ValueType::Type::CHAR || printInconclusive)) { if (toType->isIntegral() && fromType->isIntegral()) continue; invalidPointerCastError(tok, fromType->str(), toType->str(), toType->type == ValueType::Type::CHAR, toType->isIntegral()); } } } } void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt) { if (toIsInt) { // If we cast something to int*, this can be useful to play with its binary data representation reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + " to " + to + " is not portable due to different binary data representations on different platforms.", CWE704, inconclusive); } else reportError(tok, Severity::portability, "invalidPointerCast", "Casting between " + from + " and " + to + " which have an incompatible binary data representation.", CWE704, false); } //--------------------------------------------------------------------------- // This check detects errors on POSIX systems, when a pipe command called // with a wrong dimensioned file descriptor array. The pipe command requires // exactly an integer array of dimension two as parameter. // // References: // - http://linux.die.net/man/2/pipe // - ticket #3521 //--------------------------------------------------------------------------- void CheckOther::checkPipeParameterSize() { if (!mSettings->posix()) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "pipe ( %var% )") || Token::Match(tok, "pipe2 ( %var% ,")) { const Token * const varTok = tok->tokAt(2); const Variable *var = varTok->variable(); MathLib::bigint dim; if (var && var->isArray() && !var->isArgument() && ((dim=var->dimension(0U)) < 2)) { const std::string strDim = MathLib::toString(dim); checkPipeParameterSizeError(varTok,varTok->str(), strDim); } } } } } void CheckOther::checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim) { reportError(tok, Severity::error, "wrongPipeParameterSize", "$symbol:" + strVarName + "\n" "Buffer '$symbol' must have size of 2 integers if used as parameter of pipe().\n" "The pipe()/pipe2() system command takes an argument, which is an array of exactly two integers.\n" "The variable '$symbol' is an array of size " + strDim + ", which does not match.", CWE686, false); } //--------------------------------------------------------------------------- // Detect redundant assignments: x = 0; x = 4; //--------------------------------------------------------------------------- void CheckOther::checkRedundantAssignment() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { if (!scope->bodyStart) continue; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "] (")) // todo: handle lambdas break; if (Token::simpleMatch(tok, "try {")) // todo: check try blocks tok = tok->linkAt(1); if ((tok->isAssignmentOp() || Token::Match(tok, "++|--")) && tok->astOperand1()) { if (tok->astParent()) continue; // Do not warn about redundant initialization when rhs is trivial // TODO : do not simplify the variable declarations bool isInitialization = false; if (Token::Match(tok->tokAt(-3), "%var% ; %var% =") && tok->previous()->variable() && tok->previous()->variable()->nameToken() == tok->tokAt(-3) && tok->tokAt(-3)->linenr() == tok->previous()->linenr()) { isInitialization = true; bool trivial = true; visitAstNodes(tok->astOperand2(), [&](const Token *rhs) { if (Token::simpleMatch(rhs, "{ 0 }")) return ChildrenToVisit::none; if (Token::Match(rhs, "%str%|%num%|%name%") && !rhs->varId()) return ChildrenToVisit::none; if (Token::Match(rhs, ":: %name%") && rhs->hasKnownIntValue()) return ChildrenToVisit::none; if (rhs->isCast()) return ChildrenToVisit::op2; trivial = false; return ChildrenToVisit::done; }); if (trivial) continue; } // Do not warn about assignment with 0 / NULL if (Token::simpleMatch(tok->astOperand2(), "0") || FwdAnalysis::isNullOperand(tok->astOperand2())) continue; if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) // todo: check references continue; if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) // todo: check static variables continue; // If there is a custom assignment operator => this is inconclusive bool inconclusive = false; if (mTokenizer->isCPP() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->typeScope) { const std::string op = "operator" + tok->str(); for (const Function &f : tok->astOperand1()->valueType()->typeScope->functionList) { if (f.name() == op) { inconclusive = true; break; } } } if (inconclusive && !mSettings->inconclusive) continue; FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library); if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1())) continue; // Is there a redundant assignment? const Token *start; if (tok->isAssignmentOp()) start = tok->next(); else start = tok->findExpressionStartEndTokens().second->next(); // Get next assignment.. const Token *nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd); if (!nextAssign) continue; // there is redundant assignment. Is there a case between the assignments? bool hasCase = false; for (const Token *tok2 = tok; tok2 != nextAssign; tok2 = tok2->next()) { if (tok2->str() == "break" || tok2->str() == "return") break; if (tok2->str() == "case") { hasCase = true; break; } } // warn if (hasCase) redundantAssignmentInSwitchError(tok, nextAssign, tok->astOperand1()->expressionString()); else if (isInitialization) redundantInitializationError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); else redundantAssignmentError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); } } } } void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) { const std::list callstack = { tok1, tok2 }; reportError(callstack, Severity::performance, "redundantCopy", "$symbol:" + var + "\n" "Buffer '$symbol' is being written before its old content has been used.", CWE563, false); } void CheckOther::redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { const std::list callstack = { tok1, tok2 }; reportError(callstack, Severity::warning, "redundantCopyInSwitch", "$symbol:" + var + "\n" "Buffer '$symbol' is being written before its old content has been used. 'break;' missing?", CWE563, false); } void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) { const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is assigned"), ErrorPathItem(tok2, var + " is overwritten") }; if (inconclusive) reportError(errorPath, Severity::style, "redundantAssignment", "$symbol:" + var + "\n" "Variable '$symbol' is reassigned a value before the old one has been used if variable is no semaphore variable.\n" "Variable '$symbol' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", CWE563, true); else reportError(errorPath, Severity::style, "redundantAssignment", "$symbol:" + var + "\n" "Variable '$symbol' is reassigned a value before the old one has been used.", CWE563, false); } void CheckOther::redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) { const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is initialized"), ErrorPathItem(tok2, var + " is overwritten") }; reportError(errorPath, Severity::style, "redundantInitialization", "$symbol:" + var + "\nRedundant initialization for '$symbol'. The initialized value is overwritten before it is read.", CWE563, inconclusive); } void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { const ErrorPath errorPath = { ErrorPathItem(tok1, "$symbol is assigned"), ErrorPathItem(tok2, "$symbol is overwritten") }; reportError(errorPath, Severity::warning, "redundantAssignInSwitch", "$symbol:" + var + "\n" "Variable '$symbol' is reassigned a value before the old one has been used. 'break;' missing?", CWE563, false); } //--------------------------------------------------------------------------- // switch (x) // { // case 2: // y = a; // <- this assignment is redundant // case 3: // y = b; // <- case 2 falls through and sets y twice // } //--------------------------------------------------------------------------- static inline bool isFunctionOrBreakPattern(const Token *tok) { if (Token::Match(tok, "%name% (") || Token::Match(tok, "break|continue|return|exit|goto|throw")) return true; return false; } void CheckOther::checkRedundantAssignmentInSwitch() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // Find the beginning of a switch. E.g.: // switch (var) { ... for (const Scope &switchScope : symbolDatabase->scopeList) { if (switchScope.type != Scope::eSwitch || !switchScope.bodyStart) continue; // Check the contents of the switch statement std::map varsWithBitsSet; std::map bitOperations; for (const Token *tok2 = switchScope.bodyStart->next(); tok2 != switchScope.bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") { // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.: // case 3: b = 1; // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) { const Token* endOfConditional = tok2->link(); for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) { if (tok3->varId() != 0) { varsWithBitsSet.erase(tok3->varId()); bitOperations.erase(tok3->varId()); } else if (isFunctionOrBreakPattern(tok3)) { varsWithBitsSet.clear(); bitOperations.clear(); } } tok2 = endOfConditional; } } // Variable assignment. Report an error if it's assigned to twice before a break. E.g.: // case 3: b = 1; // <== redundant // case 4: b = 2; if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;")) { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } // Bitwise operation. Report an error if it's performed twice before a break. E.g.: // case 3: b |= 1; // <== redundant // case 4: b |= 1; else if (Token::Match(tok2->previous(), ";|{|}|: %var% %assign% %num% ;") && (tok2->strAt(1) == "|=" || tok2->strAt(1) == "&=") && Token::Match(tok2->next()->astOperand2(), "%num%")) { const std::string bitOp = tok2->strAt(1)[0] + tok2->strAt(2); const std::map::const_iterator i2 = varsWithBitsSet.find(tok2->varId()); // This variable has not had a bit operation performed on it yet, so just make a note of it if (i2 == varsWithBitsSet.end()) { varsWithBitsSet[tok2->varId()] = tok2; bitOperations[tok2->varId()] = bitOp; } // The same bit operation has been performed on the same variable twice, so report an error else if (bitOperations[tok2->varId()] == bitOp) redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); // A different bit operation was performed on the variable, so clear it else { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } } // Bitwise operation. Report an error if it's performed twice before a break. E.g.: // case 3: b = b | 1; // <== redundant // case 4: b = b | 1; else if (Token::Match(tok2->previous(), ";|{|}|: %var% = %name% %or%|& %num% ;") && tok2->varId() == tok2->tokAt(2)->varId()) { const std::string bitOp = tok2->strAt(3) + tok2->strAt(4); const std::map::const_iterator i2 = varsWithBitsSet.find(tok2->varId()); // This variable has not had a bit operation performed on it yet, so just make a note of it if (i2 == varsWithBitsSet.end()) { varsWithBitsSet[tok2->varId()] = tok2; bitOperations[tok2->varId()] = bitOp; } // The same bit operation has been performed on the same variable twice, so report an error else if (bitOperations[tok2->varId()] == bitOp) redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); // A different bit operation was performed on the variable, so clear it else { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } } // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.: // case 3: b = 1; // case 4: b++; else if (tok2->varId() != 0 && tok2->strAt(1) != "|" && tok2->strAt(1) != "&") { varsWithBitsSet.erase(tok2->varId()); bitOperations.erase(tok2->varId()); } // Reset our record of assignments if there is a break or function call. E.g.: // case 3: b = 1; break; if (isFunctionOrBreakPattern(tok2)) { varsWithBitsSet.clear(); bitOperations.clear(); } } } } void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "redundantBitwiseOperationInSwitch", "$symbol:" + varname + "\n" "Redundant bitwise operation on '$symbol' in 'switch' statement. 'break;' missing?"); } //--------------------------------------------------------------------------- // Check for statements like case A||B: in switch() //--------------------------------------------------------------------------- void CheckOther::checkSuspiciousCaseInSwitch() { if (!mSettings->inconclusive || !mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope & scope : symbolDatabase->scopeList) { if (scope.type != Scope::eSwitch) continue; for (const Token* tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) { if (tok->str() == "case") { const Token* finding = nullptr; for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ":") break; if (Token::Match(tok2, "[;}{]")) break; if (tok2->str() == "?") finding = nullptr; else if (Token::Match(tok2, "&&|%oror%")) finding = tok2; } if (finding) suspiciousCaseInSwitchError(finding, finding->str()); } } } } void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString) { reportError(tok, Severity::warning, "suspiciousCase", "Found suspicious case label in switch(). Operator '" + operatorString + "' probably doesn't work as intended.\n" "Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", CWE398, true); } //--------------------------------------------------------------------------- // Find consecutive return, break, continue, goto or throw statements. e.g.: // break; break; // Detect dead code, that follows such a statement. e.g.: // return(0); foo(); //--------------------------------------------------------------------------- void CheckOther::checkUnreachableCode() { if (!mSettings->isEnabled(Settings::STYLE)) return; const bool printInconclusive = mSettings->inconclusive; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { const Token* secondBreak = nullptr; const Token* labelName = nullptr; if (tok->link() && Token::Match(tok, "(|[|<")) tok = tok->link(); else if (Token::Match(tok, "break|continue ;")) secondBreak = tok->tokAt(2); else if (Token::Match(tok, "[;{}:] return|throw")) { if (Token::simpleMatch(tok->astParent(), "?")) continue; tok = tok->next(); // tok should point to return or throw for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "{") tok2 = tok2->link(); if (tok2->str() == ";") { secondBreak = tok2->next(); break; } } } else if (Token::Match(tok, "goto %any% ;")) { secondBreak = tok->tokAt(3); labelName = tok->next(); } else if (Token::Match(tok, "%name% (") && mSettings->library.isnoreturn(tok) && !Token::Match(tok->next()->astParent(), "?|:")) { if ((!tok->function() || (tok->function()->token != tok && tok->function()->tokenDef != tok)) && tok->linkAt(1)->strAt(1) != "{") secondBreak = tok->linkAt(1)->tokAt(2); } // Statements follow directly, no line between them. (#3383) // TODO: Try to find a better way to avoid false positives due to preprocessor configurations. const bool inconclusive = secondBreak && (secondBreak->linenr() - 1 > secondBreak->previous()->linenr()); if (secondBreak && (printInconclusive || !inconclusive)) { if (Token::Match(secondBreak, "continue|goto|throw") || (secondBreak->str() == "return" && (tok->str() == "return" || secondBreak->strAt(1) == ";"))) { // return with value after statements like throw can be necessary to make a function compile duplicateBreakError(secondBreak, inconclusive); tok = Token::findmatch(secondBreak, "[}:]"); } else if (secondBreak->str() == "break") { // break inside switch as second break statement should not issue a warning if (tok->str() == "break") // If the previous was a break, too: Issue warning duplicateBreakError(secondBreak, inconclusive); else { if (tok->scope()->type != Scope::eSwitch) // Check, if the enclosing scope is a switch duplicateBreakError(secondBreak, inconclusive); } tok = Token::findmatch(secondBreak, "[}:]"); } else if (!Token::Match(secondBreak, "return|}|case|default") && secondBreak->strAt(1) != ":") { // TODO: No bailout for unconditional scopes // If the goto label is followed by a loop construct in which the label is defined it's quite likely // that the goto jump was intended to skip some code on the first loop iteration. bool labelInFollowingLoop = false; if (labelName && Token::Match(secondBreak, "while|do|for")) { const Token *scope2 = Token::findsimplematch(secondBreak, "{"); if (scope2) { for (const Token *tokIter = scope2; tokIter != scope2->link() && tokIter; tokIter = tokIter->next()) { if (Token::Match(tokIter, "[;{}] %any% :") && labelName->str() == tokIter->strAt(1)) { labelInFollowingLoop = true; break; } } } } // hide FP for statements that just hide compiler warnings about unused function arguments bool silencedCompilerWarningOnly = false; const Token *silencedWarning = secondBreak; for (;;) { if (Token::Match(silencedWarning, "( void ) %name% ;")) { silencedWarning = silencedWarning->tokAt(5); continue; } else if (silencedWarning && silencedWarning == scope->bodyEnd) silencedCompilerWarningOnly = true; break; } if (silencedWarning) secondBreak = silencedWarning; if (!labelInFollowingLoop && !silencedCompilerWarningOnly) unreachableCodeError(secondBreak, inconclusive); tok = Token::findmatch(secondBreak, "[}:]"); } else tok = secondBreak; if (!tok) break; tok = tok->previous(); // Will be advanced again by for loop } } } } void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "duplicateBreak", "Consecutive return, break, continue, goto or throw statements are unnecessary.\n" "Consecutive return, break, continue, goto or throw statements are unnecessary. " "The second statement can never be executed, and so should be removed.", CWE561, inconclusive); } void CheckOther::unreachableCodeError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "unreachableCode", "Statements following return, break, continue, goto or throw will never be executed.", CWE561, inconclusive); } //--------------------------------------------------------------------------- // Check scope of variables.. //--------------------------------------------------------------------------- void CheckOther::checkVariableScope() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->isLocal() || (!var->isPointer() && !var->isReference() && !var->typeStartToken()->isStandardType())) continue; if (var->isConst()) continue; // reference of range for loop variable.. if (Token::Match(var->nameToken()->previous(), "& %var% = %var% .")) { const Token *otherVarToken = var->nameToken()->tokAt(2); const Variable *otherVar = otherVarToken->variable(); if (otherVar && Token::Match(otherVar->nameToken(), "%var% :") && otherVar->nameToken()->next()->astParent() && Token::simpleMatch(otherVar->nameToken()->next()->astParent()->previous(), "for (")) continue; } bool forHead = false; // Don't check variables declared in header of a for loop for (const Token* tok = var->typeStartToken(); tok; tok = tok->previous()) { if (tok->str() == "(") { forHead = true; break; } else if (Token::Match(tok, "[;{}]")) break; } if (forHead) continue; const Token* tok = var->nameToken()->next(); if (Token::Match(tok, "; %varid% = %any% ;", var->declarationId())) { tok = tok->tokAt(3); if (!tok->isNumber() && tok->tokType() != Token::eString && tok->tokType() != Token::eChar && !tok->isBoolean()) continue; } // bailout if initialized with function call that has possible side effects if (Token::Match(tok, "[(=]") && Token::simpleMatch(tok->astOperand2(), "(")) continue; bool reduce = true; bool used = false; // Don't warn about unused variables for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) { if (tok->str() == "{" && tok->scope() != tok->previous()->scope() && !tok->isExpandedMacro() && tok->scope()->type != Scope::eLambda) { if (used) { bool used2 = false; if (!checkInnerScope(tok, var, used2) || used2) { reduce = false; break; } } else if (!checkInnerScope(tok, var, used)) { reduce = false; break; } tok = tok->link(); // parse else if blocks.. } else if (Token::simpleMatch(tok, "else { if (") && Token::simpleMatch(tok->linkAt(3), ") {")) { const Token *endif = tok->linkAt(3)->linkAt(1); bool elseif = false; if (Token::simpleMatch(endif, "} }")) elseif = true; else if (Token::simpleMatch(endif, "} else {") && Token::simpleMatch(endif->linkAt(2),"} }")) elseif = true; if (elseif && Token::findmatch(tok->next(), "%varid%", tok->linkAt(1), var->declarationId())) { reduce = false; break; } } else if (tok->varId() == var->declarationId() || tok->str() == "goto") { reduce = false; break; } } if (reduce && used) variableScopeError(var->nameToken(), var->name()); } } bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) { const Scope* scope = tok->next()->scope(); bool loopVariable = scope->type == Scope::eFor || scope->type == Scope::eWhile || scope->type == Scope::eDo; bool noContinue = true; const Token* forHeadEnd = nullptr; const Token* end = tok->link(); if (scope->type == Scope::eUnconditional && (tok->strAt(-1) == ")" || tok->previous()->isName())) // Might be an unknown macro like BOOST_FOREACH loopVariable = true; if (scope->type == Scope::eDo) { end = end->linkAt(2); } else if (loopVariable && tok->strAt(-1) == ")") { tok = tok->linkAt(-1); // Jump to opening ( of for/while statement } else if (scope->type == Scope::eSwitch) { for (const Scope* innerScope : scope->nestedList) { if (used) { bool used2 = false; if (!checkInnerScope(innerScope->bodyStart, var, used2) || used2) { return false; } } else if (!checkInnerScope(innerScope->bodyStart, var, used)) { return false; } } } bool bFirstAssignment=false; for (; tok && tok != end; tok = tok->next()) { if (tok->str() == "goto") return false; if (tok->str() == "continue") noContinue = false; if (Token::simpleMatch(tok, "for (")) forHeadEnd = tok->linkAt(1); if (tok == forHeadEnd) forHeadEnd = nullptr; if (loopVariable && noContinue && tok->scope() == scope && !forHeadEnd && scope->type != Scope::eSwitch && Token::Match(tok, "%varid% =", var->declarationId())) { // Assigned in outer scope. loopVariable = false; int indent = 0; for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { // Ensure that variable isn't used on right side of =, too if (tok2->str() == "(") indent++; else if (tok2->str() == ")") { if (indent == 0) break; indent--; } else if (tok2->str() == ";") break; else if (tok2->varId() == var->declarationId()) { loopVariable = true; break; } } } if (loopVariable && Token::Match(tok, "%varid% !!=", var->declarationId())) // Variable used in loop return false; if (Token::Match(tok, "& %varid%", var->declarationId())) // Taking address of variable return false; if (Token::Match(tok, "%varid% =", var->declarationId())) bFirstAssignment = true; if (!bFirstAssignment && Token::Match(tok, "* %varid%", var->declarationId())) // dereferencing means access to previous content return false; if (Token::Match(tok, "= %varid%", var->declarationId()) && (var->isArray() || var->isPointer())) // Create a copy of array/pointer. Bailout, because the memory it points to might be necessary in outer scope return false; if (tok->varId() == var->declarationId()) { used = true; if (scope->type == Scope::eSwitch && scope == tok->scope()) return false; // Used in outer switch scope - unsafe or impossible to reduce scope } } return true; } void CheckOther::variableScopeError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "variableScope", "$symbol:" + varname + "\n" "The scope of the variable '$symbol' can be reduced.\n" "The scope of the variable '$symbol' can be reduced. Warning: Be careful " "when fixing this message, especially when there are inner loops. Here is an " "example where cppcheck will write that the scope for 'i' can be reduced:\n" "void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {\n" " // it's safe to move 'int i = 0;' here\n" " for (int n = 0; n < 10; ++n) {\n" " // it is possible but not safe to move 'int i = 0;' here\n" " do_something(&i);\n" " }\n" " }\n" "}\n" "When you see this message it is always safe to reduce the variable scope 1 level.", CWE398, false); } //--------------------------------------------------------------------------- // Comma in return statement: return a+1, b++;. (experimental) //--------------------------------------------------------------------------- void CheckOther::checkCommaSeparatedReturn() { // This is experimental for now. See #5076 if (!mSettings->experimental) return; if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() == "return") { tok = tok->next(); while (tok && tok->str() != ";") { if (tok->link() && Token::Match(tok, "[([{<]")) tok = tok->link(); if (!tok->isExpandedMacro() && tok->str() == "," && tok->linenr() != tok->next()->linenr()) commaSeparatedReturnError(tok); tok = tok->next(); } // bailout: missing semicolon (invalid code / bad tokenizer) if (!tok) break; } } } void CheckOther::commaSeparatedReturnError(const Token *tok) { reportError(tok, Severity::style, "commaSeparatedReturn", "Comma is used in return statement. The comma can easily be misread as a ';'.\n" "Comma is used in return statement. When comma is used in a return statement it can " "easily be misread as a semicolon. For example in the code below the value " "of 'b' is returned if the condition is true, but it is easy to think that 'a+1' is " "returned:\n" " if (x)\n" " return a + 1,\n" " b++;\n" "However it can be useful to use comma in macros. Cppcheck does not warn when such a " "macro is then used in a return statement, it is less likely such code is misunderstood.", CWE398, false); } //--------------------------------------------------------------------------- // Check for function parameters that should be passed by const reference //--------------------------------------------------------------------------- static int estimateSize(const Type* type, const Settings* settings, const SymbolDatabase* symbolDatabase, int recursionDepth = 0) { if (recursionDepth > 20) return 0; int cumulatedSize = 0; for (const Variable&var : type->classScope->varlist) { int size = 0; if (var.isStatic()) continue; if (var.isPointer() || var.isReference()) size = settings->sizeof_pointer; else if (var.type() && var.type()->classScope) size = estimateSize(var.type(), settings, symbolDatabase, recursionDepth+1); else if (var.valueType()->type == ValueType::Type::CONTAINER) size = 3 * settings->sizeof_pointer; // Just guess else size = symbolDatabase->sizeOfType(var.typeStartToken()); if (var.isArray()) cumulatedSize += size * var.dimension(0); else cumulatedSize += size; } for (const Type::BaseInfo &baseInfo : type->derivedFrom) { if (baseInfo.type && baseInfo.type->classScope) cumulatedSize += estimateSize(baseInfo.type, settings, symbolDatabase, recursionDepth+1); } return cumulatedSize; } static bool canBeConst(const Variable *var) { { // check initializer list. If variable is moved from it can't be const. const Function* func_scope = var->scope()->function; if (func_scope->type == Function::Type::eConstructor) { //could be initialized in initializer list if (func_scope->arg->link()->next()->str() == ":") { for (const Token* tok2 = func_scope->arg->link()->next()->next(); tok2 != var->scope()->bodyStart; tok2 = tok2->next()) { if (tok2->varId() != var->declarationId()) continue; const Token* parent = tok2->astParent(); if (parent && Token::simpleMatch(parent->previous(), "move (")) return false; } } } } for (const Token* tok2 = var->scope()->bodyStart; tok2 != var->scope()->bodyEnd; tok2 = tok2->next()) { if (tok2->varId() != var->declarationId()) continue; const Token* parent = tok2->astParent(); if (!parent) continue; if (parent->str() == "<<" || isLikelyStreamRead(true, parent)) { if (parent->str() == "<<" && parent->astOperand1() == tok2) return false; if (parent->str() == ">>" && parent->astOperand2() == tok2) return false; } else if (parent->str() == "," || parent->str() == "(") { // function argument const Token* tok3 = tok2->previous(); int argNr = 0; while (tok3 && tok3->str() != "(") { if (tok3->link() && Token::Match(tok3, ")|]|}|>")) tok3 = tok3->link(); else if (tok3->link()) break; else if (tok3->str() == ";") break; else if (tok3->str() == ",") argNr++; tok3 = tok3->previous(); } if (!tok3 || tok3->str() != "(" || !tok3->astOperand1() || !tok3->astOperand1()->function()) return false; else { const Variable* argVar = tok3->astOperand1()->function()->getArgumentVar(argNr); if (!argVar|| (!argVar->isConst() && argVar->isReference())) return false; } } else if (parent->isUnaryOp("&")) { // TODO: check how pointer is used return false; } else if (parent->isConstOp()) continue; else if (parent->isAssignmentOp()) { if (parent->astOperand1() == tok2) return false; else if (parent->astOperand1()->str() == "&") { const Variable* assignedVar = parent->previous()->variable(); if (!assignedVar || !assignedVar->isConst()) return false; } } else if (Token::Match(tok2, "%var% . %name% (")) { const Function* func = tok2->tokAt(2)->function(); if (func && (func->isConst() || func->isStatic())) continue; else return false; } else return false; } return true; } void CheckOther::checkPassByReference() { if (!mSettings->isEnabled(Settings::PERFORMANCE) || mTokenizer->isC()) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->isArgument() || !var->isClass() || var->isPointer() || var->isArray() || var->isReference() || var->isEnumType()) continue; if (var->scope() && var->scope()->function->arg->link()->strAt(-1) == "...") continue; // references could not be used as va_start parameters (#5824) if ((var->declEndToken() && var->declEndToken()->isExternC()) || (var->scope() && var->scope()->function && var->scope()->function->tokenDef && var->scope()->function->tokenDef->isExternC())) continue; // references cannot be used in functions in extern "C" blocks bool inconclusive = false; if (var->valueType()->type == ValueType::Type::CONTAINER) { } else if (var->type() && !var->type()->isEnumType()) { // Check if type is a struct or class. // Ensure that it is a large object. if (!var->type()->classScope) inconclusive = true; else if (estimateSize(var->type(), mSettings, symbolDatabase) <= 2 * mSettings->sizeof_pointer) continue; } else continue; if (inconclusive && !mSettings->inconclusive) continue; const bool isConst = var->isConst(); if (isConst) { passedByValueError(var->nameToken(), var->name(), inconclusive); continue; } // Check if variable could be const if (!var->scope() || var->scope()->function->hasVirtualSpecifier()) continue; if (canBeConst(var)) { passedByValueError(var->nameToken(), var->name(), inconclusive); } } } void CheckOther::passedByValueError(const Token *tok, const std::string &parname, bool inconclusive) { reportError(tok, Severity::performance, "passedByValue", "$symbol:" + parname + "\n" "Function parameter '$symbol' should be passed by const reference.\n" "Parameter '$symbol' is passed by value. It could be passed " "as a const reference which is usually faster and recommended in C++.", CWE398, inconclusive); } static bool isUnusedVariable(const Variable *var) { if (!var) return false; if (!var->scope()) return false; const Token *start = var->declEndToken(); if (!start) return false; if (Token::Match(start, "; %varid% =", var->declarationId())) start = start->tokAt(2); return !Token::findmatch(start->next(), "%varid%", var->scope()->bodyEnd, var->declarationId()); } static bool isVariableMutableInInitializer(const Token* start, const Token * end, nonneg int varid) { if (!start) return false; if (!end) return false; for (const Token *tok = start; tok != end; tok = tok->next()) { if (tok->varId() != varid) continue; if (tok->astParent()) { const Token * memberTok = tok->astParent()->previous(); if (Token::Match(memberTok, "%var% (") && memberTok->variable()) { const Variable * memberVar = memberTok->variable(); if (!memberVar->isReference()) continue; if (memberVar->isConst()) continue; } return true; } else { return true; } } return false; } void CheckOther::checkConstVariable() { if (!mSettings->isEnabled(Settings::STYLE) || mTokenizer->isC()) return; const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable *var : symbolDatabase->variableList()) { if (!var) continue; if (!var->isReference()) continue; if (var->isRValueReference()) continue; if (var->isConst()) continue; if (!var->scope()) continue; const Scope *scope = var->scope(); if (!scope->function) continue; Function *function = scope->function; if (var->isArgument()) { if (function->isImplicitlyVirtual() || function->templateDef) continue; if (isUnusedVariable(var)) continue; if (function->isConstructor() && isVariableMutableInInitializer(function->constructorMemberInitialization(), scope->bodyStart, var->declarationId())) continue; } if (var->isGlobal()) continue; if (var->isStatic()) continue; if (var->isArray()) continue; if (var->isEnumType()) continue; if (var->isVolatile()) continue; if (isAliased(var)) continue; if (isVariableChanged(var, mSettings, mTokenizer->isCPP())) continue; if (Function::returnsReference(function) && Token::findmatch(var->nameToken(), "return %varid% ;|[|.", scope->bodyEnd, var->declarationId())) continue; // Skip if address is taken if (Token::findmatch(var->nameToken(), "& %varid%", scope->bodyEnd, var->declarationId())) continue; constVariableError(var); } } void CheckOther::constVariableError(const Variable *var) { const Token *tok = nullptr; std::string name = "x"; std::string id = "Variable"; if (var) { tok = var->nameToken(); name = var->name(); if (var->isArgument()) id = "Parameter"; } reportError(tok, Severity::style, "const" + id, id + " '" + name + "' can be declared with const", CWE398, false); } //--------------------------------------------------------------------------- // Check usage of char variables.. //--------------------------------------------------------------------------- void CheckOther::checkCharVariable() { const bool warning = mSettings->isEnabled(Settings::WARNING); const bool portability = mSettings->isEnabled(Settings::PORTABILITY); if (!warning && !portability) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%var% [")) { if (!tok->variable()) continue; if (!tok->variable()->isArray() && !tok->variable()->isPointer()) continue; const Token *index = tok->next()->astOperand2(); if (warning && tok->variable()->isArray() && astIsSignedChar(index) && index->getValueGE(0x80, mSettings)) signedCharArrayIndexError(tok); if (portability && astIsUnknownSignChar(index) && index->getValueGE(0x80, mSettings)) unknownSignCharArrayIndexError(tok); } else if (warning && Token::Match(tok, "[&|^]") && tok->isBinaryOp()) { bool warn = false; if (astIsSignedChar(tok->astOperand1())) { const ValueFlow::Value *v1 = tok->astOperand1()->getValueLE(-1, mSettings); const ValueFlow::Value *v2 = tok->astOperand2()->getMaxValue(false); if (!v1) v1 = tok->astOperand1()->getValueGE(0x80, mSettings); if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) warn = true; } else if (astIsSignedChar(tok->astOperand2())) { const ValueFlow::Value *v1 = tok->astOperand2()->getValueLE(-1, mSettings); const ValueFlow::Value *v2 = tok->astOperand1()->getMaxValue(false); if (!v1) v1 = tok->astOperand2()->getValueGE(0x80, mSettings); if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) warn = true; } // is the result stored in a short|int|long? if (warn && Token::simpleMatch(tok->astParent(), "=")) { const Token *lhs = tok->astParent()->astOperand1(); if (lhs && lhs->valueType() && lhs->valueType()->type >= ValueType::Type::SHORT) charBitOpError(tok); // This is an error.. } } } } } void CheckOther::signedCharArrayIndexError(const Token *tok) { reportError(tok, Severity::warning, "signedCharArrayIndex", "Signed 'char' type used as array index.\n" "Signed 'char' type used as array index. If the value " "can be greater than 127 there will be a buffer underflow " "because of sign extension.", CWE128, false); } void CheckOther::unknownSignCharArrayIndexError(const Token *tok) { reportError(tok, Severity::portability, "unknownSignCharArrayIndex", "'char' type used as array index.\n" "'char' type used as array index. Values greater than 127 will be " "treated depending on whether 'char' is signed or unsigned on target platform.", CWE758, false); } void CheckOther::charBitOpError(const Token *tok) { reportError(tok, Severity::warning, "charBitOp", "When using 'char' variables in bit operations, sign extension can generate unexpected results.\n" "When using 'char' variables in bit operations, sign extension can generate unexpected results. For example:\n" " char c = 0x80;\n" " int i = 0 | c;\n" " if (i & 0x8000)\n" " printf(\"not expected\");\n" "The \"not expected\" will be printed on the screen.", CWE398, false); } //--------------------------------------------------------------------------- // Incomplete statement.. //--------------------------------------------------------------------------- static bool isType(const Token * tok, bool unknown) { if (Token::Match(tok, "%type%")) return true; if (Token::simpleMatch(tok, "::")) return isType(tok->astOperand2(), unknown); if (Token::simpleMatch(tok, "<") && tok->link()) return true; if (unknown && Token::Match(tok, "%name% !!(")) return true; return false; } static bool isVarDeclOp(const Token* tok) { if (!tok) return false; const Token * vartok = tok->astOperand2(); if (vartok && vartok->variable() && vartok->variable()->nameToken() == vartok) return true; const Token * typetok = tok->astOperand1(); return isType(typetok, Token::Match(vartok, "%var%")); } static bool isConstStatement(const Token *tok) { if (!tok) return false; if (tok->isExpandedMacro()) return false; if (Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL")) return true; if (Token::Match(tok, "%var%")) return true; if (Token::Match(tok, "*|&|&&") && (Token::Match(tok->previous(), "::|.|const|volatile|restrict") || isVarDeclOp(tok))) return false; if (Token::Match(tok, "<<|>>") && !astIsIntegral(tok, false)) return false; if (Token::Match(tok, "!|~|%cop%") && (tok->astOperand1() || tok->astOperand2())) return true; if (Token::simpleMatch(tok->previous(), "sizeof (")) return true; if (isCPPCast(tok)) return isConstStatement(tok->astOperand2()); if (Token::Match(tok, "( %type%")) return isConstStatement(tok->astOperand1()); if (Token::simpleMatch(tok, ",")) return isConstStatement(tok->astOperand2()); return false; } static bool isVoidStmt(const Token *tok) { if (Token::simpleMatch(tok, "( void")) return true; const Token *tok2 = tok; while (tok2->astOperand1()) tok2 = tok2->astOperand1(); if (Token::simpleMatch(tok2->previous(), ")") && Token::simpleMatch(tok2->previous()->link(), "( void")) return true; if (Token::simpleMatch(tok2, "( void")) return true; return Token::Match(tok2->previous(), "delete|throw|return"); } static bool isConstTop(const Token *tok) { if (!tok) return false; if (tok == tok->astTop()) return true; if (Token::simpleMatch(tok->astParent(), ";") && tok->astTop() && Token::Match(tok->astTop()->previous(), "for|if (") && Token::simpleMatch(tok->astTop()->astOperand2(), ";")) { if (Token::simpleMatch(tok->astParent()->astParent(), ";")) return tok->astParent()->astOperand2() == tok; else return tok->astParent()->astOperand1() == tok; } return false; } void CheckOther::checkIncompleteStatement() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { const Scope *scope = tok->scope(); if (scope && !scope->isExecutable()) continue; if (!isConstTop(tok)) continue; const Token *rtok = nextAfterAstRightmostLeaf(tok); if (!Token::simpleMatch(tok->astParent(), ";") && !Token::simpleMatch(rtok, ";") && !Token::Match(tok->previous(), ";|}|{ %any% ;")) continue; // Skip statement expressions if (Token::simpleMatch(rtok, "; } )")) continue; if (!isConstStatement(tok)) continue; if (isVoidStmt(tok)) continue; if (mTokenizer->isCPP() && tok->str() == "&" && !(tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isIntegral())) // Possible archive continue; bool inconclusive = Token::Match(tok, "%cop%"); if (mSettings->inconclusive || !inconclusive) constStatementError(tok, tok->isNumber() ? "numeric" : "string", inconclusive); } } void CheckOther::constStatementError(const Token *tok, const std::string &type, bool inconclusive) { const Token *valueTok = tok; while (valueTok && valueTok->isCast()) valueTok = valueTok->astOperand2() ? valueTok->astOperand2() : valueTok->astOperand1(); std::string msg; if (Token::simpleMatch(tok, "==")) msg = "Found suspicious equality comparison. Did you intend to assign a value instead?"; else if (Token::Match(tok, ",|!|~|%cop%")) msg = "Found suspicious operator '" + tok->str() + "'"; else if (Token::Match(tok, "%var%")) msg = "Unused variable value '" + tok->str() + "'"; else if (Token::Match(valueTok, "%str%|%num%")) msg = "Redundant code: Found a statement that begins with " + std::string(valueTok->isNumber() ? "numeric" : "string") + " constant."; else if (!tok) msg = "Redundant code: Found a statement that begins with " + type + " constant."; else return; // Strange! reportError(tok, Severity::warning, "constStatement", msg, CWE398, inconclusive); } //--------------------------------------------------------------------------- // Detect division by zero. //--------------------------------------------------------------------------- void CheckOther::checkZeroDivision() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->astOperand2() || !tok->astOperand1()) continue; if (tok->str() != "%" && tok->str() != "/" && tok->str() != "%=" && tok->str() != "/=") continue; if (!tok->valueType() || !tok->valueType()->isIntegral()) continue; // Value flow.. const ValueFlow::Value *value = tok->astOperand2()->getValue(0LL); if (value && mSettings->isEnabled(value, false)) zerodivError(tok, value); } } void CheckOther::zerodivError(const Token *tok, const ValueFlow::Value *value) { if (!tok && !value) { reportError(tok, Severity::error, "zerodiv", "Division by zero.", CWE369, false); reportError(tok, Severity::error, "zerodivcond", ValueFlow::eitherTheConditionIsRedundant(nullptr) + " or there is division by zero.", CWE369, false); return; } const ErrorPath errorPath = getErrorPath(tok, value, "Division by zero"); std::ostringstream errmsg; if (value->condition) { const int line = tok ? tok->linenr() : 0; errmsg << ValueFlow::eitherTheConditionIsRedundant(value->condition) << " or there is division by zero at line " << line << "."; } else errmsg << "Division by zero."; reportError(errorPath, value->errorSeverity() ? Severity::error : Severity::warning, value->condition ? "zerodivcond" : "zerodiv", errmsg.str(), CWE369, value->isInconclusive()); } //--------------------------------------------------------------------------- // Check for NaN (not-a-number) in an arithmetic expression, e.g. // double d = 1.0 / 0.0 + 100.0; //--------------------------------------------------------------------------- void CheckOther::checkNanInArithmeticExpression() { if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "/") continue; if (!Token::Match(tok->astParent(), "[+-]")) continue; if (Token::simpleMatch(tok->astOperand2(), "0.0")) nanInArithmeticExpressionError(tok); } } void CheckOther::nanInArithmeticExpressionError(const Token *tok) { reportError(tok, Severity::style, "nanInArithmeticExpression", "Using NaN/Inf in a computation.\n" "Using NaN/Inf in a computation. " "Although nothing bad really happens, it is suspicious.", CWE369, false); } //--------------------------------------------------------------------------- // Creating instance of classes which are destroyed immediately //--------------------------------------------------------------------------- void CheckOther::checkMisusedScopedObject() { // Skip this check for .c files if (mTokenizer->isC()) return; if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { if ((tok->next()->type() || (tok->next()->function() && tok->next()->function()->isConstructor())) // TODO: The rhs of || should be removed; It is a workaround for a symboldatabase bug && Token::Match(tok, "[;{}] %name% (") && Token::Match(tok->linkAt(2), ") ; !!}") && (!tok->next()->function() || // is not a function on this scope tok->next()->function()->isConstructor())) { // or is function in this scope and it's a ctor tok = tok->next(); misusedScopeObjectError(tok, tok->str()); tok = tok->next(); } } } } void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname) { reportError(tok, Severity::style, "unusedScopedObject", "$symbol:" + varname + "\n" "Instance of '$symbol' object is destroyed immediately.", CWE563, false); } static const Token * getSingleExpressionInBlock(const Token * tok) { if (!tok) return nullptr; const Token * top = tok->astTop(); if (!top) return nullptr; const Token * nextExpression = nextAfterAstRightmostLeaf(top); if (!Token::simpleMatch(nextExpression, "; }")) return nullptr; return top; } //----------------------------------------------------------------------------- // check for duplicate code in if and else branches // if (a) { b = true; } else { b = true; } //----------------------------------------------------------------------------- void CheckOther::checkDuplicateBranch() { // This is inconclusive since in practice most warnings are noise: // * There can be unfixed low-priority todos. The code is fine as it // is but it could be possible to enhance it. Writing a warning // here is noise since the code is fine (see cppcheck, abiword, ..) // * There can be overspecified code so some conditions can't be true // and their conditional code is a duplicate of the condition that // is always true just in case it would be false. See for instance // abiword. if (!mSettings->isEnabled(Settings::STYLE) || !mSettings->inconclusive) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope & scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf) continue; // check all the code in the function for if (..) else if (Token::simpleMatch(scope.bodyEnd, "} else {")) { // Make sure there are no macros (different macros might be expanded // to the same code) bool macro = false; for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd->linkAt(2); tok = tok->next()) { if (tok->isExpandedMacro()) { macro = true; break; } } if (macro) continue; // save if branch code const std::string branch1 = scope.bodyStart->next()->stringifyList(scope.bodyEnd); if (branch1.empty()) continue; // save else branch code const std::string branch2 = scope.bodyEnd->tokAt(3)->stringifyList(scope.bodyEnd->linkAt(2)); ErrorPath errorPath; // check for duplicates if (branch1 == branch2) { duplicateBranchError(scope.classDef, scope.bodyEnd->next(), errorPath); continue; } // check for duplicates using isSameExpression const Token * branchTop1 = getSingleExpressionInBlock(scope.bodyStart->next()); const Token * branchTop2 = getSingleExpressionInBlock(scope.bodyEnd->tokAt(3)); if (!branchTop1 || !branchTop2) continue; if (branchTop1->str() != branchTop2->str()) continue; if (isSameExpression(mTokenizer->isCPP(), false, branchTop1->astOperand1(), branchTop2->astOperand1(), mSettings->library, true, true, &errorPath) && isSameExpression(mTokenizer->isCPP(), false, branchTop1->astOperand2(), branchTop2->astOperand2(), mSettings->library, true, true, &errorPath)) duplicateBranchError(scope.classDef, scope.bodyEnd->next(), errorPath); } } } void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors) { errors.emplace_back(tok2, ""); errors.emplace_back(tok1, ""); reportError(errors, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n" "Finding the same code in an 'if' and related 'else' branch is suspicious and " "might indicate a cut and paste or logic error. Please examine this code " "carefully to determine if it is correct.", CWE398, true); } //----------------------------------------------------------------------------- // Check for a free() of an invalid address // char* p = malloc(100); // free(p + 10); //----------------------------------------------------------------------------- void CheckOther::checkInvalidFree() { std::map inconclusive; std::map allocation; const bool printInconclusive = mSettings->inconclusive; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // Keep track of which variables were assigned addresses to newly-allocated memory if (Token::Match(tok, "%var% = malloc|g_malloc|new")) { allocation.insert(std::make_pair(tok->varId(), tok->strAt(2))); inconclusive.insert(std::make_pair(tok->varId(), false)); } // If a previously-allocated pointer is incremented or decremented, any subsequent // free involving pointer arithmetic may or may not be invalid, so we should only // report an inconclusive result. else if (Token::Match(tok, "%var% = %name% +|-") && tok->varId() == tok->tokAt(2)->varId() && allocation.find(tok->varId()) != allocation.end()) { if (printInconclusive) inconclusive[tok->varId()] = true; else { allocation.erase(tok->varId()); inconclusive.erase(tok->varId()); } } // If a previously-allocated pointer is assigned a completely new value, // we can't know if any subsequent free() on that pointer is valid or not. else if (Token::Match(tok, "%var% =")) { allocation.erase(tok->varId()); inconclusive.erase(tok->varId()); } // If a variable that was previously assigned a newly-allocated memory location is // added or subtracted from when used to free the memory, report an error. else if (Token::Match(tok, "free|g_free|delete ( %any% +|-") || Token::Match(tok, "delete [ ] ( %any% +|-") || Token::Match(tok, "delete %any% +|- %any%")) { const int varIndex = tok->strAt(1) == "(" ? 2 : tok->strAt(3) == "(" ? 4 : 1; const int var1 = tok->tokAt(varIndex)->varId(); const int var2 = tok->tokAt(varIndex + 2)->varId(); const std::map::const_iterator alloc1 = inconclusive.find(var1); const std::map::const_iterator alloc2 = inconclusive.find(var2); if (alloc1 != inconclusive.end()) { invalidFreeError(tok, allocation[var1], alloc1->second); } else if (alloc2 != inconclusive.end()) { invalidFreeError(tok, allocation[var2], alloc2->second); } } // If the previously-allocated variable is passed in to another function // as a parameter, it might be modified, so we shouldn't report an error // if it is later used to free memory else if (Token::Match(tok, "%name% (") && !mSettings->library.isFunctionConst(tok->str(), true)) { const Token* tok2 = Token::findmatch(tok->next(), "%var%", tok->linkAt(1)); while (tok2 != nullptr) { allocation.erase(tok->varId()); inconclusive.erase(tok2->varId()); tok2 = Token::findmatch(tok2->next(), "%var%", tok->linkAt(1)); } } } } } void CheckOther::invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive) { std::string alloc = allocation; if (alloc != "new") alloc += "()"; std::string deallocated = (alloc == "new") ? "deleted" : "freed"; reportError(tok, Severity::error, "invalidFree", "Mismatching address is " + deallocated + ". The address you get from " + alloc + " must be " + deallocated + " without offset.", CWE(0U), inconclusive); } //--------------------------------------------------------------------------- // check for the same expression on both sides of an operator // (x == x), (x && x), (x || x) // (x.y == x.y), (x.y && x.y), (x.y || x.y) //--------------------------------------------------------------------------- namespace { bool notconst(const Function* func) { return !func->isConst(); } void getConstFunctions(const SymbolDatabase *symbolDatabase, std::list &constFunctions) { for (const Scope &scope : symbolDatabase->scopeList) { // only add const functions that do not have a non-const overloaded version // since it is pretty much impossible to tell which is being called. typedef std::map > StringFunctionMap; StringFunctionMap functionsByName; for (const Function &func : scope.functionList) { functionsByName[func.tokenDef->str()].push_back(&func); } for (StringFunctionMap::iterator it = functionsByName.begin(); it != functionsByName.end(); ++it) { const std::list::const_iterator nc = std::find_if(it->second.begin(), it->second.end(), notconst); if (nc == it->second.end()) { // ok to add all of them constFunctions.splice(constFunctions.end(), it->second); } } } } } void CheckOther::checkDuplicateExpression() { const bool styleEnabled = mSettings->isEnabled(Settings::STYLE); const bool warningEnabled = mSettings->isEnabled(Settings::WARNING); if (!styleEnabled && !warningEnabled) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); std::list constFunctions; getConstFunctions(symbolDatabase, constFunctions); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "=" && Token::Match(tok->astOperand1(), "%var%")) { const Token * endStatement = Token::findsimplematch(tok, ";"); if (Token::Match(endStatement, "; %type% %var% ;")) { endStatement = endStatement->tokAt(4); } if (Token::Match(endStatement, "%var% %assign%")) { const Token * nextAssign = endStatement->tokAt(1); const Token * var1 = tok->astOperand1(); const Token * var2 = nextAssign->astOperand1(); if (var1 && var2 && Token::Match(var1->previous(), ";|{|} %var%") && Token::Match(var2->previous(), ";|{|} %var%") && var2->valueType() && var1->valueType() && var2->valueType()->originalTypeName == var1->valueType()->originalTypeName && var2->valueType()->pointer == var1->valueType()->pointer && var2->valueType()->constness == var1->valueType()->constness && var2->varId() != var1->varId() && ( tok->astOperand2()->isArithmeticalOp() || tok->astOperand2()->str() == "." || Token::Match(tok->astOperand2()->previous(), "%name% (") ) && tok->next()->tokType() != Token::eType && isSameExpression(mTokenizer->isCPP(), true, tok->next(), nextAssign->next(), mSettings->library, true, false) && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), nextAssign->astOperand2(), mSettings->library, true, false) && tok->astOperand2()->expressionString() == nextAssign->astOperand2()->expressionString()) { bool assigned = false; const Scope * varScope = var1->scope() ? var1->scope() : scope; for (const Token *assignTok = Token::findsimplematch(var2, ";"); assignTok && assignTok != varScope->bodyEnd; assignTok = assignTok->next()) { if (Token::Match(assignTok, "%varid% = %var%", var1->varId()) && Token::Match(assignTok, "%var% = %varid%", var2->varId())) { assigned = true; } if (Token::Match(assignTok, "%varid% = %var%", var2->varId()) && Token::Match(assignTok, "%var% = %varid%", var1->varId())) { assigned = true; } } if (!assigned && !isUniqueExpression(tok->astOperand2())) duplicateAssignExpressionError(var1, var2, false); else if (mSettings->inconclusive) duplicateAssignExpressionError(var1, var2, true); } } } ErrorPath errorPath; if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|*|<<|>>|+=|*=|<<=|>>=")) { if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1(), true)) continue; const bool followVar = !isConstVarExpression(tok) || Token::Match(tok, "%comp%|%oror%|&&"); if (isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, followVar, &errorPath)) { if (isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) { const bool assignment = tok->str() == "="; if (assignment && warningEnabled) selfAssignmentError(tok, tok->astOperand1()->expressionString()); else if (styleEnabled) { if (mTokenizer->isCPP() && mSettings->standards.cpp==Standards::CPP11 && tok->str() == "==") { const Token* parent = tok->astParent(); while (parent && parent->astParent()) { parent = parent->astParent(); } if (parent && parent->previous() && parent->previous()->str() == "static_assert") { continue; } } duplicateExpressionError(tok->astOperand1(), tok->astOperand2(), tok, errorPath); } } } else if (tok->str() == "=" && Token::simpleMatch(tok->astOperand2(), "=") && isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), tok->astOperand2()->astOperand1(), mSettings->library, true, false)) { if (warningEnabled && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) { selfAssignmentError(tok, tok->astOperand1()->expressionString()); } } else if (styleEnabled && isOppositeExpression(mTokenizer->isCPP(), tok->astOperand1(), tok->astOperand2(), mSettings->library, false, true, &errorPath) && !Token::Match(tok, "=|-|-=|/|/=") && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand1())) { oppositeExpressionError(tok, errorPath); } else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative if (styleEnabled && tok->astOperand2() && tok->str() == tok->astOperand1()->str() && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand2(), tok->astOperand1()->astOperand2(), mSettings->library, true, followVar, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), tok->astOperand2())) duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath); else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library, true, mTokenizer->isCPP())) { const Token *ast1 = tok->astOperand1(); while (ast1 && tok->str() == ast1->str()) { if (isSameExpression(mTokenizer->isCPP(), true, ast1->astOperand1(), tok->astOperand2(), mSettings->library, true, true, &errorPath) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand1()) && isWithoutSideEffects(mTokenizer->isCPP(), ast1->astOperand2())) // Probably the message should be changed to 'duplicate expressions X in condition or something like that'. duplicateExpressionError(ast1->astOperand1(), tok->astOperand2(), tok, errorPath); ast1 = ast1->astOperand1(); } } } } else if (styleEnabled && tok->astOperand1() && tok->astOperand2() && tok->str() == ":" && tok->astParent() && tok->astParent()->str() == "?") { if (!tok->astOperand1()->values().empty() && !tok->astOperand2()->values().empty() && isEqualKnownValue(tok->astOperand1(), tok->astOperand2())) duplicateValueTernaryError(tok); else if (isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, false, true, &errorPath)) duplicateExpressionTernaryError(tok, errorPath); } } } } void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors) { errors.emplace_back(opTok, ""); const std::string& op = opTok ? opTok->str() : "&&"; reportError(errors, Severity::style, "oppositeExpression", "Opposite expression on both sides of \'" + op + "\'.\n" "Finding the opposite expression on both sides of an operator is suspicious and might " "indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct.", CWE398, false); } void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors) { errors.emplace_back(opTok, ""); const std::string& expr1 = tok1 ? tok1->expressionString() : "x"; const std::string& expr2 = tok2 ? tok2->expressionString() : "x"; const std::string& op = opTok ? opTok->str() : "&&"; std::string msg = "Same expression on both sides of \'" + op + "\'"; std::string id = "duplicateExpression"; if (expr1 != expr2) { id = "knownConditionTrueFalse"; std::string exprMsg = "The expression \'" + expr1 + " " + op + " " + expr2 + "\' is always "; if (Token::Match(opTok, "==|>=|<=")) msg = exprMsg + "true"; else if (Token::Match(opTok, "!=|>|<")) msg = exprMsg + "false"; if (!Token::Match(tok1, "%num%|NULL|nullptr") && !Token::Match(tok2, "%num%|NULL|nullptr")) msg += " because '" + expr1 + "' and '" + expr2 + "' represent the same value"; } reportError(errors, Severity::style, id.c_str(), msg + ".\n" "Finding the same expression on both sides of an operator is suspicious and might " "indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct.", CWE398, false); } void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive) { const std::list toks = { tok2, tok1 }; const std::string& var1 = tok1 ? tok1->str() : "x"; const std::string& var2 = tok2 ? tok2->str() : "x"; reportError(toks, Severity::style, "duplicateAssignExpression", "Same expression used in consecutive assignments of '" + var1 + "' and '" + var2 + "'.\n" "Finding variables '" + var1 + "' and '" + var2 + "' that are assigned the same expression " "is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to " "determine if it is correct.", CWE398, inconclusive); } void CheckOther::duplicateExpressionTernaryError(const Token *tok, ErrorPath errors) { errors.emplace_back(tok, ""); reportError(errors, Severity::style, "duplicateExpressionTernary", "Same expression in both branches of ternary operator.\n" "Finding the same expression in both branches of ternary operator is suspicious as " "the same code is executed regardless of the condition.", CWE398, false); } void CheckOther::duplicateValueTernaryError(const Token *tok) { reportError(tok, Severity::style, "duplicateValueTernary", "Same value in both branches of ternary operator.\n" "Finding the same value in both branches of ternary operator is suspicious as " "the same code is executed regardless of the condition.", CWE398, false); } void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "selfAssignment", "$symbol:" + varname + "\n" "Redundant assignment of '$symbol' to itself.", CWE398, false); } //----------------------------------------------------------------------------- // Check is a comparison of two variables leads to condition, which is // always true or false. // For instance: int a = 1; if(isless(a,a)){...} // In this case isless(a,a) always evaluates to false. // // Reference: // - http://www.cplusplus.com/reference/cmath/ //----------------------------------------------------------------------------- void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->isName() && Token::Match(tok, "isgreater|isless|islessgreater|isgreaterequal|islessequal ( %var% , %var% )")) { const int varidLeft = tok->tokAt(2)->varId();// get the left varid const int varidRight = tok->tokAt(4)->varId();// get the right varid // compare varids: if they are not zero but equal // --> the comparison function is called with the same variables if (varidLeft == varidRight) { const std::string& functionName = tok->str(); // store function name const std::string& varNameLeft = tok->strAt(2); // get the left variable name if (functionName == "isgreater" || functionName == "isless" || functionName == "islessgreater") { // e.g.: isgreater(x,x) --> (x)>(x) --> false checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, false); } else { // functionName == "isgreaterequal" || functionName == "islessequal" // e.g.: isgreaterequal(x,x) --> (x)>=(x) --> true checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, true); } } } } } } void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result) { const std::string strResult = result ? "true" : "false"; const struct CWE cweResult = result ? CWE571 : CWE570; reportError(tok, Severity::warning, "comparisonFunctionIsAlwaysTrueOrFalse", "$symbol:" + functionName + "\n" "Comparison of two identical variables with $symbol(" + varName + "," + varName + ") always evaluates to " + strResult + ".\n" "The function $symbol is designed to compare two variables. Calling this function with one variable (" + varName + ") " "for both parameters leads to a statement which is always " + strResult + ".", cweResult, false); } //--------------------------------------------------------------------------- // Check testing sign of unsigned variables and pointers. //--------------------------------------------------------------------------- void CheckOther::checkSignOfUnsignedVariable() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { // check all the code in the function for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) continue; const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0); const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0); if (Token::Match(tok, "<|<=") && v2 && v2->isKnown()) { const ValueType* vt = tok->astOperand1()->valueType(); if (vt && vt->pointer) pointerLessThanZeroError(tok, v2); if (vt && vt->sign == ValueType::UNSIGNED) unsignedLessThanZeroError(tok, v2, tok->astOperand1()->expressionString()); } else if (Token::Match(tok, ">|>=") && v1 && v1->isKnown()) { const ValueType* vt = tok->astOperand2()->valueType(); if (vt && vt->pointer) pointerLessThanZeroError(tok, v1); if (vt && vt->sign == ValueType::UNSIGNED) unsignedLessThanZeroError(tok, v1, tok->astOperand2()->expressionString()); } else if (Token::simpleMatch(tok, ">=") && v2 && v2->isKnown()) { const ValueType* vt = tok->astOperand1()->valueType(); if (vt && vt->pointer) pointerPositiveError(tok, v2); if (vt && vt->sign == ValueType::UNSIGNED) unsignedPositiveError(tok, v2, tok->astOperand1()->expressionString()); } else if (Token::simpleMatch(tok, "<=") && v1 && v1->isKnown()) { const ValueType* vt = tok->astOperand2()->valueType(); if (vt && vt->pointer) pointerPositiveError(tok, v1); if (vt && vt->sign == ValueType::UNSIGNED) unsignedPositiveError(tok, v1, tok->astOperand2()->expressionString()); } } } } void CheckOther::unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) { reportError(getErrorPath(tok, v, "Unsigned less than zero"), Severity::style, "unsignedLessThanZero", "$symbol:" + varname + "\n" "Checking if unsigned expression '$symbol' is less than zero.\n" "The unsigned expression '$symbol' will never be negative so it " "is either pointless or an error to check if it is.", CWE570, false); } void CheckOther::pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v) { reportError(getErrorPath(tok, v, "Pointer less than zero"), Severity::style, "pointerLessThanZero", "A pointer can not be negative so it is either pointless or an error to check if it is.", CWE570, false); } void CheckOther::unsignedPositiveError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) { reportError(getErrorPath(tok, v, "Unsigned positive"), Severity::style, "unsignedPositive", "$symbol:" + varname + "\n" "Unsigned expression '$symbol' can't be negative so it is unnecessary to test it.", CWE570, false); } void CheckOther::pointerPositiveError(const Token *tok, const ValueFlow::Value * v) { reportError(getErrorPath(tok, v, "Pointer positive"), Severity::style, "pointerPositive", "A pointer can not be negative so it is either pointless or an error to check if it is not.", CWE570, false); } /* check if a constructor in given class scope takes a reference */ static bool constructorTakesReference(const Scope * const classScope) { for (const Function &constructor : classScope->functionList) { if (constructor.isConstructor()) { for (int argnr = 0U; argnr < constructor.argCount(); argnr++) { const Variable * const argVar = constructor.getArgumentVar(argnr); if (argVar && argVar->isReference()) { return true; } } } } return false; } //--------------------------------------------------------------------------- // This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &". // In most scenarios, "const A & a = getA()" will be more efficient. //--------------------------------------------------------------------------- void CheckOther::checkRedundantCopy() { if (!mSettings->isEnabled(Settings::PERFORMANCE) || mTokenizer->isC() || !mSettings->inconclusive) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || var->isReference() || !var->isConst() || var->isPointer() || (!var->type() && !var->isStlType())) // bailout if var is of standard type, if it is a pointer or non-const continue; const Token* startTok = var->nameToken(); if (startTok->strAt(1) == "=") // %type% %name% = ... ; ; else if (startTok->strAt(1) == "(" && var->isClass() && var->typeScope()) { // Object is instantiated. Warn if constructor takes arguments by value. if (constructorTakesReference(var->typeScope())) continue; } else continue; const Token* tok = startTok->next()->astOperand2(); if (!tok) continue; if (!Token::Match(tok->previous(), "%name% (")) continue; if (!Token::Match(tok->link(), ") )| ;")) // bailout for usage like "const A a = getA()+3" continue; const Function* func = tok->previous()->function(); if (func && func->tokenDef->strAt(-1) == "&") { redundantCopyError(startTok, startTok->str()); } } } void CheckOther::redundantCopyError(const Token *tok,const std::string& varname) { reportError(tok, Severity::performance, "redundantCopyLocalConst", "$symbol:" + varname + "\n" "Use const reference for '$symbol' to avoid unnecessary data copying.\n" "The const variable '$symbol' is assigned a copy of the data. You can avoid " "the unnecessary data copying by converting '$symbol' to const reference.", CWE398, true); // since #5618 that check became inconclusive } //--------------------------------------------------------------------------- // Checking for shift by negative values //--------------------------------------------------------------------------- static bool isNegative(const Token *tok, const Settings *settings) { return tok->valueType() && tok->valueType()->sign == ValueType::SIGNED && tok->getValueLE(-1LL, settings); } void CheckOther::checkNegativeBitwiseShift() { const bool portability = mSettings->isEnabled(Settings::PORTABILITY); for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->astOperand1() || !tok->astOperand2()) continue; if (!Token::Match(tok, "<<|>>|<<=|>>=")) continue; // don't warn if lhs is a class. this is an overloaded operator then if (mTokenizer->isCPP()) { const ValueType * lhsType = tok->astOperand1()->valueType(); if (!lhsType || !lhsType->isIntegral()) continue; } // bailout if operation is protected by ?: bool ternary = false; for (const Token *parent = tok; parent; parent = parent->astParent()) { if (Token::Match(parent, "?|:")) { ternary = true; break; } } if (ternary) continue; // Get negative rhs value. preferably a value which doesn't have 'condition'. if (portability && isNegative(tok->astOperand1(), mSettings)) negativeBitwiseShiftError(tok, 1); else if (isNegative(tok->astOperand2(), mSettings)) negativeBitwiseShiftError(tok, 2); } } void CheckOther::negativeBitwiseShiftError(const Token *tok, int op) { if (op == 1) // LHS - this is used by intention in various software, if it // is used often in a project and works as expected then this is // a portability issue reportError(tok, Severity::portability, "shiftNegativeLHS", "Shifting a negative value is technically undefined behaviour", CWE758, false); else // RHS reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value is undefined behaviour", CWE758, false); } //--------------------------------------------------------------------------- // Check for incompletely filled buffers. //--------------------------------------------------------------------------- void CheckOther::checkIncompleteArrayFill() { if (!mSettings->inconclusive) return; const bool printWarning = mSettings->isEnabled(Settings::WARNING); const bool printPortability = mSettings->isEnabled(Settings::PORTABILITY); if (!printPortability && !printWarning) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "memset|memcpy|memmove ( %var% ,") && Token::Match(tok->linkAt(1)->tokAt(-2), ", %num% )")) { const Variable *var = tok->tokAt(2)->variable(); if (!var || !var->isArray() || var->dimensions().empty() || !var->dimension(0)) continue; if (MathLib::toLongNumber(tok->linkAt(1)->strAt(-1)) == var->dimension(0)) { int size = mTokenizer->sizeOfType(var->typeStartToken()); if (size == 0 && var->valueType()->pointer) size = mSettings->sizeof_pointer; if ((size != 1 && size != 100 && size != 0) || var->isPointer()) { if (printWarning) incompleteArrayFillError(tok, var->name(), tok->str(), false); } else if (var->valueType()->type == ValueType::Type::BOOL && printPortability) // sizeof(bool) is not 1 on all platforms incompleteArrayFillError(tok, var->name(), tok->str(), true); } } } } } void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean) { if (boolean) reportError(tok, Severity::portability, "incompleteArrayFill", "$symbol:" + buffer + "\n" "$symbol:" + function + "\n" "Array '" + buffer + "' might be filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, true); else reportError(tok, Severity::warning, "incompleteArrayFill", "$symbol:" + buffer + "\n" "$symbol:" + function + "\n" "Array '" + buffer + "' is filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, true); } //--------------------------------------------------------------------------- // Detect NULL being passed to variadic function. //--------------------------------------------------------------------------- void CheckOther::checkVarFuncNullUB() { if (!mSettings->isEnabled(Settings::PORTABILITY)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { // Is NULL passed to a function? if (Token::Match(tok,"[(,] NULL [,)]")) { // Locate function name in this function call. const Token *ftok = tok; int argnr = 1; while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); else if (ftok->str() == ",") ++argnr; ftok = ftok->previous(); } ftok = ftok ? ftok->previous() : nullptr; if (ftok && ftok->isName()) { // If this is a variadic function then report error const Function *f = ftok->function(); if (f && f->argCount() <= argnr) { const Token *tok2 = f->argDef; tok2 = tok2 ? tok2->link() : nullptr; // goto ')' if (tok2 && Token::simpleMatch(tok2->tokAt(-1), "...")) varFuncNullUBError(tok); } } } } } } void CheckOther::varFuncNullUBError(const Token *tok) { reportError(tok, Severity::portability, "varFuncNullUB", "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" "The C99 standard, in section 7.15.1.1, states that if the type used by va_arg() is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined.\n" "The value of the NULL macro is an implementation-defined null pointer constant (7.17), which can be any integer constant expression with the value 0, or such an expression casted to (void*) (6.3.2.3). This includes values like 0, 0L, or even 0LL.\n" "In practice on common architectures, this will cause real crashes if sizeof(int) != sizeof(void*), and NULL is defined to 0 or any other null pointer constant that promotes to int.\n" "To reproduce you might be able to use this little code example on 64bit platforms. If the output includes \"ERROR\", the sentinel had only 4 out of 8 bytes initialized to zero and was not detected as the final argument to stop argument processing via va_arg(). Changing the 0 to (void*)0 or 0L will make the \"ERROR\" output go away.\n" "#include \n" "#include \n" "\n" "void f(char *s, ...) {\n" " va_list ap;\n" " va_start(ap,s);\n" " for (;;) {\n" " char *p = va_arg(ap,char*);\n" " printf(\"%018p, %s\\n\", p, (long)p & 255 ? p : \"\");\n" " if(!p) break;\n" " }\n" " va_end(ap);\n" "}\n" "\n" "void g() {\n" " char *s2 = \"x\";\n" " char *s3 = \"ERROR\";\n" "\n" " // changing 0 to 0L for the 7th argument (which is intended to act as sentinel) makes the error go away on x86_64\n" " f(\"first\", s2, s2, s2, s2, s2, 0, s3, (char*)0);\n" "}\n" "\n" "void h() {\n" " int i;\n" " volatile unsigned char a[1000];\n" " for (i = 0; iisEnabled(Settings::STYLE)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->isUnaryOp("&") || !tok->astOperand1()->isUnaryOp("*")) continue; // variable const Token *varTok = tok->astOperand1()->astOperand1(); if (!varTok || varTok->isExpandedMacro()) continue; const Variable *var = varTok->variable(); if (!var || !var->isPointer()) continue; redundantPointerOpError(tok, var->name(), false); } } void CheckOther::redundantPointerOpError(const Token* tok, const std::string &varname, bool inconclusive) { reportError(tok, Severity::style, "redundantPointerOp", "$symbol:" + varname + "\n" "Redundant pointer operation on '$symbol' - it's already a pointer.", CWE398, inconclusive); } void CheckOther::checkInterlockedDecrement() { if (!mSettings->isWindowsPlatform()) { return; } for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->isName() && Token::Match(tok, "InterlockedDecrement ( & %name% ) ; if ( %name%|!|0")) { const Token* interlockedVarTok = tok->tokAt(3); const Token* checkStartTok = interlockedVarTok->tokAt(5); if ((Token::Match(checkStartTok, "0 %comp% %name% )") && checkStartTok->strAt(2) == interlockedVarTok->str()) || (Token::Match(checkStartTok, "! %name% )") && checkStartTok->strAt(1) == interlockedVarTok->str()) || (Token::Match(checkStartTok, "%name% )") && checkStartTok->str() == interlockedVarTok->str()) || (Token::Match(checkStartTok, "%name% %comp% 0 )") && checkStartTok->str() == interlockedVarTok->str())) { raceAfterInterlockedDecrementError(checkStartTok); } } else if (Token::Match(tok, "if ( ::| InterlockedDecrement ( & %name%")) { const Token* condEnd = tok->next()->link(); const Token* funcTok = tok->tokAt(2); const Token* firstAccessTok = funcTok->str() == "::" ? funcTok->tokAt(4) : funcTok->tokAt(3); if (condEnd && condEnd->next() && condEnd->next()->link()) { const Token* ifEndTok = condEnd->next()->link(); if (Token::Match(ifEndTok, "} return %name%")) { const Token* secondAccessTok = ifEndTok->tokAt(2); if (secondAccessTok->str() == firstAccessTok->str()) { raceAfterInterlockedDecrementError(secondAccessTok); } } else if (Token::Match(ifEndTok, "} else { return %name%")) { const Token* secondAccessTok = ifEndTok->tokAt(4); if (secondAccessTok->str() == firstAccessTok->str()) { raceAfterInterlockedDecrementError(secondAccessTok); } } } } } } void CheckOther::raceAfterInterlockedDecrementError(const Token* tok) { reportError(tok, Severity::error, "raceAfterInterlockedDecrement", "Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.", CWE362, false); } void CheckOther::checkUnusedLabel() { if (!mSettings->isEnabled(Settings::STYLE) && !mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (!tok->scope()->isExecutable()) tok = tok->scope()->bodyEnd; if (Token::Match(tok, "{|}|; %name% :") && tok->strAt(1) != "default") { if (!Token::findsimplematch(scope->bodyStart->next(), ("goto " + tok->strAt(1)).c_str(), scope->bodyEnd->previous())) unusedLabelError(tok->next(), tok->next()->scope()->type == Scope::eSwitch); } } } } void CheckOther::unusedLabelError(const Token* tok, bool inSwitch) { if (inSwitch) { if (!tok || mSettings->isEnabled(Settings::WARNING)) reportError(tok, Severity::warning, "unusedLabelSwitch", "$symbol:" + (tok ? tok->str() : emptyString) + "\n" "Label '$symbol' is not used. Should this be a 'case' of the enclosing switch()?", CWE398, false); } else { if (!tok || mSettings->isEnabled(Settings::STYLE)) reportError(tok, Severity::style, "unusedLabel", "$symbol:" + (tok ? tok->str() : emptyString) + "\n" "Label '$symbol' is not used.", CWE398, false); } } void CheckOther::checkEvaluationOrder() { // This checker is not written according to C++11 sequencing rules if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * functionScope : symbolDatabase->functionScopes) { for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "++|--") && !tok->isAssignmentOp()) continue; if (!tok->astOperand1()) continue; for (const Token *tok2 = tok;; tok2 = tok2->astParent()) { // If ast parent is a sequence point then break const Token * const parent = tok2->astParent(); if (!parent) break; if (Token::Match(parent, "%oror%|&&|?|:|;")) break; if (parent->str() == ",") { const Token *par = parent; while (Token::simpleMatch(par,",")) par = par->astParent(); // not function or in a while clause => break if (!(par && par->str() == "(" && par->astOperand2() && par->strAt(-1) != "while")) break; // control flow (if|while|etc) => break if (Token::simpleMatch(par->link(),") {")) break; // sequence point in function argument: dostuff((1,2),3) => break par = par->next(); while (par && (par->previous() != parent)) par = par->nextArgument(); if (!par) break; } if (parent->str() == "(" && parent->astOperand2()) break; // self assignment.. if (tok2 == tok && tok->str() == "=" && parent->str() == "=" && isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) { if (mSettings->isEnabled(Settings::WARNING) && isSameExpression(mTokenizer->isCPP(), true, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) selfAssignmentError(parent, tok->astOperand1()->expressionString()); break; } // Is expression used? bool foundError = false; visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), [&](const Token *tok3) { if (tok3->str() == "&" && !tok3->astOperand2()) return ChildrenToVisit::none; // don't handle address-of for now if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) return ChildrenToVisit::none; // don't care about sizeof usage if (isSameExpression(mTokenizer->isCPP(), false, tok->astOperand1(), tok3, mSettings->library, true, false)) foundError = true; return foundError ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (foundError) { unknownEvaluationOrder(parent); break; } } } } } void CheckOther::unknownEvaluationOrder(const Token* tok) { reportError(tok, Severity::error, "unknownEvaluationOrder", "Expression '" + (tok ? tok->expressionString() : std::string("x = x++;")) + "' depends on order of evaluation of side effects", CWE768, false); } void CheckOther::checkAccessOfMovedVariable() { if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11 || !mSettings->isEnabled(Settings::WARNING)) return; CheckUninitVar checkUninitVar(mTokenizer, mSettings, mErrorLogger); const bool reportInconclusive = mSettings->inconclusive; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { const Token * scopeStart = scope->bodyStart; if (scope->function) { const Token * memberInitializationStart = scope->function->constructorMemberInitialization(); if (memberInitializationStart) scopeStart = memberInitializationStart; } for (const Token* tok = scopeStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const ValueFlow::Value * movedValue = tok->getMovedValue(); if (!movedValue || movedValue->moveKind == ValueFlow::Value::MoveKind::NonMovedVariable) continue; if (movedValue->isInconclusive() && !reportInconclusive) continue; bool inconclusive = false; bool accessOfMoved = false; if (tok->strAt(1) == ".") { if (tok->next()->originalName() == "->") accessOfMoved = true; else inconclusive = true; } else { const bool isVariableChanged = isVariableChangedByFunctionCall(tok, 0, mSettings, &inconclusive); accessOfMoved = !isVariableChanged && checkUninitVar.isVariableUsage(tok, false, CheckUninitVar::NO_ALLOC); if (inconclusive) { accessOfMoved = !isMovedParameterAllowedForInconclusiveFunction(tok); if (accessOfMoved) inconclusive = false; } } if (accessOfMoved || (inconclusive && reportInconclusive)) accessMovedError(tok, tok->str(), movedValue, inconclusive || movedValue->isInconclusive()); } } } bool CheckOther::isMovedParameterAllowedForInconclusiveFunction(const Token * tok) { if (Token::simpleMatch(tok->tokAt(-4), "std :: move (")) return false; const Token * tokAtM2 = tok->tokAt(-2); if (Token::simpleMatch(tokAtM2, "> (") && tokAtM2->link()) { const Token * leftAngle = tokAtM2->link(); if (Token::simpleMatch(leftAngle->tokAt(-3), "std :: forward <")) return false; } return true; } void CheckOther::accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) { if (!tok) { reportError(tok, Severity::warning, "accessMoved", "Access of moved variable 'v'.", CWE672, false); reportError(tok, Severity::warning, "accessForwarded", "Access of forwarded variable 'v'.", CWE672, false); return; } const char * errorId = nullptr; std::string kindString; switch (value->moveKind) { case ValueFlow::Value::MoveKind::MovedVariable: errorId = "accessMoved"; kindString = "moved"; break; case ValueFlow::Value::MoveKind::ForwardedVariable: errorId = "accessForwarded"; kindString = "forwarded"; break; default: return; } const std::string errmsg("$symbol:" + varname + "\nAccess of " + kindString + " variable '$symbol'."); const ErrorPath errorPath = getErrorPath(tok, value, errmsg); reportError(errorPath, Severity::warning, errorId, errmsg, CWE672, inconclusive); } void CheckOther::checkFuncArgNamesDifferent() { const bool style = mSettings->isEnabled(Settings::STYLE); const bool inconclusive = mSettings->inconclusive; const bool warning = mSettings->isEnabled(Settings::WARNING); if (!(warning || (style && inconclusive))) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // check every function for (const Scope *scope : symbolDatabase->functionScopes) { const Function * function = scope->function; // only check functions with arguments if (!function || function->argCount() == 0) continue; // only check functions with separate declarations and definitions if (function->argDef == function->arg) continue; // get the function argument name tokens std::vector declarations(function->argCount()); std::vector definitions(function->argCount()); const Token * decl = function->argDef->next(); for (int j = 0; j < function->argCount(); ++j) { declarations[j] = nullptr; definitions[j] = nullptr; // get the definition const Variable * variable = function->getArgumentVar(j); if (variable) { definitions[j] = variable->nameToken(); } // get the declaration (search for first token with varId) while (decl && !Token::Match(decl, ",|)|;")) { // skip everything after the assignment because // it could also have a varId or be the first // token with a varId if there is no name token if (decl->str() == "=") { decl = decl->nextArgument(); break; } // skip over template if (decl->link()) decl = decl->link(); else if (decl->varId()) declarations[j] = decl; decl = decl->next(); } if (Token::simpleMatch(decl, ",")) decl = decl->next(); } // check for different argument order if (warning) { bool order_different = false; for (int j = 0; j < function->argCount(); ++j) { if (!declarations[j] || !definitions[j] || declarations[j]->str() == definitions[j]->str()) continue; for (int k = 0; k < function->argCount(); ++k) { if (j != k && definitions[k] && declarations[j]->str() == definitions[k]->str()) { order_different = true; break; } } } if (order_different) { funcArgOrderDifferent(function->name(), function->argDef->next(), function->arg->next(), declarations, definitions); continue; } } // check for different argument names if (style && inconclusive) { for (int j = 0; j < function->argCount(); ++j) { if (declarations[j] && definitions[j] && declarations[j]->str() != definitions[j]->str()) funcArgNamesDifferent(function->name(), j, declarations[j], definitions[j]); } } } } void CheckOther::funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition) { std::list tokens = { declaration,definition }; reportError(tokens, Severity::style, "funcArgNamesDifferent", "$symbol:" + functionName + "\n" "Function '$symbol' argument " + MathLib::toString(index + 1) + " names different: declaration '" + (declaration ? declaration->str() : std::string("A")) + "' definition '" + (definition ? definition->str() : std::string("B")) + "'.", CWE628, true); } void CheckOther::funcArgOrderDifferent(const std::string & functionName, const Token* declaration, const Token* definition, const std::vector & declarations, const std::vector & definitions) { std::list tokens = { declarations.size() ? declarations[0] ? declarations[0] : declaration : nullptr, definitions.size() ? definitions[0] ? definitions[0] : definition : nullptr }; std::string msg = "$symbol:" + functionName + "\nFunction '$symbol' argument order different: declaration '"; for (int i = 0; i < declarations.size(); ++i) { if (i != 0) msg += ", "; if (declarations[i]) msg += declarations[i]->str(); } msg += "' definition '"; for (int i = 0; i < definitions.size(); ++i) { if (i != 0) msg += ", "; if (definitions[i]) msg += definitions[i]->str(); } msg += "'"; reportError(tokens, Severity::warning, "funcArgOrderDifferent", msg, CWE683, false); } static const Token *findShadowed(const Scope *scope, const std::string &varname, int linenr) { if (!scope) return nullptr; for (const Variable &var : scope->varlist) { if (scope->isExecutable() && var.nameToken()->linenr() > linenr) continue; if (var.name() == varname) return var.nameToken(); } for (const Function &f : scope->functionList) { if (f.name() == varname) return f.tokenDef; } if (scope->type == Scope::eLambda) return nullptr; return findShadowed(scope->nestedIn, varname, linenr); } void CheckOther::checkShadowVariables() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope & scope : symbolDatabase->scopeList) { if (!scope.isExecutable() || scope.type == Scope::eLambda) continue; const Scope *functionScope = &scope; while (functionScope && functionScope->type != Scope::ScopeType::eFunction && functionScope->type != Scope::ScopeType::eLambda) functionScope = functionScope->nestedIn; for (const Variable &var : scope.varlist) { if (var.nameToken() && var.nameToken()->isExpandedMacro()) // #8903 continue; if (functionScope && functionScope->type == Scope::ScopeType::eFunction && functionScope->function) { bool shadowArg = false; for (const Variable &arg : functionScope->function->argumentList) { if (arg.nameToken() && var.name() == arg.name()) { shadowError(var.nameToken(), arg.nameToken(), "argument"); shadowArg = true; break; } } if (shadowArg) continue; } const Token *shadowed = findShadowed(scope.nestedIn, var.name(), var.nameToken()->linenr()); if (!shadowed) continue; if (scope.type == Scope::eFunction && scope.className == var.name()) continue; shadowError(var.nameToken(), shadowed, (shadowed->varId() != 0) ? "variable" : "function"); } } } void CheckOther::shadowError(const Token *var, const Token *shadowed, std::string type) { ErrorPath errorPath; errorPath.push_back(ErrorPathItem(shadowed, "Shadowed declaration")); errorPath.push_back(ErrorPathItem(var, "Shadow variable")); const std::string &varname = var ? var->str() : type; const std::string Type = char(std::toupper(type[0])) + type.substr(1); const std::string id = "shadow" + Type; const std::string message = "$symbol:" + varname + "\nLocal variable \'$symbol\' shadows outer " + type; reportError(errorPath, Severity::style, id.c_str(), message, CWE398, false); } static bool isVariableExpression(const Token* tok) { if (Token::Match(tok, "%var%")) return true; if (Token::simpleMatch(tok, ".")) return isVariableExpression(tok->astOperand1()) && isVariableExpression(tok->astOperand2()); return false; } void CheckOther::checkConstArgument() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok->astParent(), "(")) continue; if (!Token::Match(tok->astParent()->previous(), "%name%")) continue; if (Token::Match(tok->astParent()->previous(), "if|while|switch|sizeof")) continue; if (tok == tok->astParent()->previous()) continue; if (!tok->hasKnownIntValue()) continue; if (Token::Match(tok, "++|--")) continue; if (isConstVarExpression(tok)) continue; const Token * tok2 = tok; if (isCPPCast(tok2)) tok2 = tok2->astOperand2(); if (isVariableExpression(tok2)) continue; constArgumentError(tok, tok->astParent()->previous(), &tok->values().front()); } } } void CheckOther::constArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value) { MathLib::bigint intvalue = value ? value->intvalue : 0; const std::string expr = tok ? tok->expressionString() : std::string("x"); const std::string fun = ftok ? ftok->str() : std::string("f"); const std::string errmsg = "Argument '" + expr + "' to function " + fun + " is always " + std::to_string(intvalue); const ErrorPath errorPath = getErrorPath(tok, value, errmsg); reportError(errorPath, Severity::style, "constArgument", errmsg, CWE570, false); } void CheckOther::checkComparePointers() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "<|>|<=|>=|-")) continue; const Token *tok1 = tok->astOperand1(); const Token *tok2 = tok->astOperand2(); if (!astIsPointer(tok1) || !astIsPointer(tok2)) continue; ValueFlow::Value v1 = getLifetimeObjValue(tok1); ValueFlow::Value v2 = getLifetimeObjValue(tok2); if (!v1.isLocalLifetimeValue() || !v2.isLocalLifetimeValue()) continue; const Variable *var1 = v1.tokvalue->variable(); const Variable *var2 = v2.tokvalue->variable(); if (!var1 || !var2) continue; if (v1.tokvalue->varId() == v2.tokvalue->varId()) continue; if (var1->isReference() || var2->isReference()) continue; if (var1->isRValueReference() || var2->isRValueReference()) continue; comparePointersError(tok, &v1, &v2); } } } void CheckOther::comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2) { ErrorPath errorPath; std::string verb = "Comparing"; if (Token::simpleMatch(tok, "-")) verb = "Subtracting"; if (v1) { errorPath.emplace_back(v1->tokvalue->variable()->nameToken(), "Variable declared here."); errorPath.insert(errorPath.end(), v1->errorPath.begin(), v1->errorPath.end()); } if (v2) { errorPath.emplace_back(v2->tokvalue->variable()->nameToken(), "Variable declared here."); errorPath.insert(errorPath.end(), v2->errorPath.begin(), v2->errorPath.end()); } errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "comparePointers", verb + " pointers that point to different objects", CWE570, false); } cppcheck-1.90/lib/checkother.h000066400000000000000000000463341357737443600163160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkotherH #define checkotherH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "valueflow.h" #include #include #include class ErrorLogger; class Settings; class Token; class Tokenizer; class Variable; /// @addtogroup Checks /// @{ /** @brief Various small checks */ class CPPCHECKLIB CheckOther : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckOther() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckOther(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckOther checkOther(tokenizer, settings, errorLogger); // Checks checkOther.warningOldStylePointerCast(); checkOther.invalidPointerCast(); checkOther.checkCharVariable(); checkOther.checkRedundantAssignment(); checkOther.checkRedundantAssignmentInSwitch(); checkOther.checkSuspiciousCaseInSwitch(); checkOther.checkDuplicateBranch(); checkOther.checkDuplicateExpression(); checkOther.checkUnreachableCode(); checkOther.checkSuspiciousSemicolon(); checkOther.checkVariableScope(); checkOther.checkSignOfUnsignedVariable(); // don't ignore casts (#3574) checkOther.checkIncompleteArrayFill(); checkOther.checkVarFuncNullUB(); checkOther.checkNanInArithmeticExpression(); checkOther.checkCommaSeparatedReturn(); checkOther.checkRedundantPointerOp(); checkOther.checkZeroDivision(); checkOther.checkNegativeBitwiseShift(); checkOther.checkInterlockedDecrement(); checkOther.checkUnusedLabel(); checkOther.checkEvaluationOrder(); checkOther.checkFuncArgNamesDifferent(); checkOther.checkShadowVariables(); checkOther.checkConstArgument(); checkOther.checkComparePointers(); checkOther.checkIncompleteStatement(); checkOther.checkPipeParameterSize(); checkOther.checkRedundantCopy(); checkOther.clarifyCalculation(); checkOther.checkPassByReference(); checkOther.checkConstVariable(); checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); checkOther.checkInvalidFree(); checkOther.clarifyStatement(); checkOther.checkCastIntToCharAndBack(); checkOther.checkMisusedScopedObject(); checkOther.checkAccessOfMovedVariable(); } /** @brief Clarify calculation for ".. a * b ? .." */ void clarifyCalculation(); /** @brief Suspicious statement like '*A++;' */ void clarifyStatement(); /** @brief Are there C-style pointer casts in a c++ file? */ void warningOldStylePointerCast(); /** @brief Check for pointer casts to a type with an incompatible binary data representation */ void invalidPointerCast(); /** @brief %Check scope of variables */ void checkVariableScope(); static bool checkInnerScope(const Token *tok, const Variable* var, bool& used); /** @brief %Check for comma separated statements in return */ void checkCommaSeparatedReturn(); /** @brief %Check for function parameters that should be passed by reference */ void checkPassByReference(); void checkConstVariable(); /** @brief Using char variable as array index / as operand in bit operation */ void checkCharVariable(); /** @brief Incomplete statement. A statement that only contains a constant or variable */ void checkIncompleteStatement(); /** @brief %Check zero division*/ void checkZeroDivision(); /** @brief Check for NaN (not-a-number) in an arithmetic expression */ void checkNanInArithmeticExpression(); /** @brief copying to memory or assigning to a variable twice */ void checkRedundantAssignment(); /** @brief %Check for assigning to the same variable twice in a switch statement*/ void checkRedundantAssignmentInSwitch(); /** @brief %Check for code like 'case A||B:'*/ void checkSuspiciousCaseInSwitch(); /** @brief %Check for objects that are destroyed immediately */ void checkMisusedScopedObject(); /** @brief %Check for suspicious code where if and else branch are the same (e.g "if (a) b = true; else b = true;") */ void checkDuplicateBranch(); /** @brief %Check for suspicious code with the same expression on both sides of operator (e.g "if (a && a)") */ void checkDuplicateExpression(); /** @brief %Check for code that gets never executed, such as duplicate break statements */ void checkUnreachableCode(); /** @brief %Check for testing sign of unsigned variable */ void checkSignOfUnsignedVariable(); /** @brief %Check for suspicious use of semicolon */ void checkSuspiciousSemicolon(); /** @brief %Check for free() operations on invalid memory locations */ void checkInvalidFree(); void invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive); /** @brief %Check for code creating redundant copies */ void checkRedundantCopy(); /** @brief %Check for bitwise shift with negative right operand */ void checkNegativeBitwiseShift(); /** @brief %Check for buffers that are filled incompletely with memset and similar functions */ void checkIncompleteArrayFill(); /** @brief %Check that variadic function calls don't use NULL. If NULL is \#defined as 0 and the function expects a pointer, the behaviour is undefined. */ void checkVarFuncNullUB(); /** @brief %Check that calling the POSIX pipe() system call is called with an integer array of size two. */ void checkPipeParameterSize(); /** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */ void checkCastIntToCharAndBack(); /** @brief %Check for using of comparison functions evaluating always to true or false. */ void checkComparisonFunctionIsAlwaysTrueOrFalse(); /** @brief %Check for redundant pointer operations */ void checkRedundantPointerOp(); /** @brief %Check for race condition with non-interlocked access after InterlockedDecrement() */ void checkInterlockedDecrement(); /** @brief %Check for unused labels */ void checkUnusedLabel(); /** @brief %Check for expression that depends on order of evaluation of side effects */ void checkEvaluationOrder(); /** @brief %Check for access of moved or forwarded variable */ void checkAccessOfMovedVariable(); /** @brief %Check if function declaration and definition argument names different */ void checkFuncArgNamesDifferent(); /** @brief %Check for shadow variables. Less noisy than gcc/clang -Wshadow. */ void checkShadowVariables(); void checkConstArgument(); void checkComparePointers(); private: // Error messages.. void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result); void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName); void checkPipeParameterSizeError(const Token *tok, const std::string &strVarName, const std::string &strDim); void clarifyCalculationError(const Token *tok, const std::string &op); void clarifyStatementError(const Token* tok); void cstyleCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); void passedByValueError(const Token *tok, const std::string &parname, bool inconclusive); void constVariableError(const Variable *var); void constStatementError(const Token *tok, const std::string &type, bool inconclusive); void signedCharArrayIndexError(const Token *tok); void unknownSignCharArrayIndexError(const Token *tok); void charBitOpError(const Token *tok); void variableScopeError(const Token *tok, const std::string &varname); void zerodivError(const Token *tok, const ValueFlow::Value *value); void nanInArithmeticExpressionError(const Token *tok); void redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); void redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); void redundantAssignmentInSwitchError(const Token *tok1, const Token *tok2, const std::string &var); void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var); void redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var); void redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname); void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString); void selfAssignmentError(const Token *tok, const std::string &varname); void misusedScopeObjectError(const Token *tok, const std::string &varname); void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors); void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive); void oppositeExpressionError(const Token *opTok, ErrorPath errors); void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors); void duplicateValueTernaryError(const Token *tok); void duplicateExpressionTernaryError(const Token *tok, ErrorPath errors); void duplicateBreakError(const Token *tok, bool inconclusive); void unreachableCodeError(const Token* tok, bool inconclusive); void unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); void pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v); void unsignedPositiveError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); void pointerPositiveError(const Token *tok, const ValueFlow::Value *v); void suspiciousSemicolonError(const Token *tok); void negativeBitwiseShiftError(const Token *tok, int op); void redundantCopyError(const Token *tok, const std::string &varname); void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean); void varFuncNullUBError(const Token *tok); void commaSeparatedReturnError(const Token *tok); void redundantPointerOpError(const Token* tok, const std::string& varname, bool inconclusive); void raceAfterInterlockedDecrementError(const Token* tok); void unusedLabelError(const Token* tok, bool inSwitch); void unknownEvaluationOrder(const Token* tok); static bool isMovedParameterAllowedForInconclusiveFunction(const Token * tok); void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); void funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition); void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector & declarations, const std::vector & definitions); void shadowError(const Token *var, const Token *shadowed, std::string type); void constArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value); void comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckOther c(nullptr, settings, errorLogger); ErrorPath errorPath; // error c.zerodivError(nullptr, nullptr); c.misusedScopeObjectError(nullptr, "varname"); c.invalidPointerCastError(nullptr, "float *", "double *", false, false); c.negativeBitwiseShiftError(nullptr, 1); c.negativeBitwiseShiftError(nullptr, 2); c.checkPipeParameterSizeError(nullptr, "varname", "dimension"); c.raceAfterInterlockedDecrementError(nullptr); c.invalidFreeError(nullptr, "malloc", false); //performance c.redundantCopyError(nullptr, "varname"); c.redundantCopyError(nullptr, nullptr, "var"); // style/warning c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); c.checkCastIntToCharAndBackError(nullptr, "func_name"); c.cstyleCastError(nullptr); c.passedByValueError(nullptr, "parametername", false); c.constVariableError(nullptr); c.constStatementError(nullptr, "type", false); c.signedCharArrayIndexError(nullptr); c.unknownSignCharArrayIndexError(nullptr); c.charBitOpError(nullptr); c.variableScopeError(nullptr, "varname"); c.redundantAssignmentInSwitchError(nullptr, nullptr, "var"); c.redundantCopyInSwitchError(nullptr, nullptr, "var"); c.suspiciousCaseInSwitchError(nullptr, "||"); c.selfAssignmentError(nullptr, "varname"); c.clarifyCalculationError(nullptr, "+"); c.clarifyStatementError(nullptr); c.duplicateBranchError(nullptr, nullptr, errorPath); c.duplicateAssignExpressionError(nullptr, nullptr, true); c.oppositeExpressionError(nullptr, errorPath); c.duplicateExpressionError(nullptr, nullptr, nullptr, errorPath); c.duplicateValueTernaryError(nullptr); c.duplicateExpressionTernaryError(nullptr, errorPath); c.duplicateBreakError(nullptr, false); c.unreachableCodeError(nullptr, false); c.unsignedLessThanZeroError(nullptr, nullptr, "varname"); c.unsignedPositiveError(nullptr, nullptr, "varname"); c.pointerLessThanZeroError(nullptr, nullptr); c.pointerPositiveError(nullptr, nullptr); c.suspiciousSemicolonError(nullptr); c.incompleteArrayFillError(nullptr, "buffer", "memset", false); c.varFuncNullUBError(nullptr); c.nanInArithmeticExpressionError(nullptr); c.commaSeparatedReturnError(nullptr); c.redundantPointerOpError(nullptr, "varname", false); c.unusedLabelError(nullptr, true); c.unusedLabelError(nullptr, false); c.unknownEvaluationOrder(nullptr); c.accessMovedError(nullptr, "v", nullptr, false); c.funcArgNamesDifferent("function", 1, nullptr, nullptr); c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); c.shadowError(nullptr, nullptr, "variable"); c.shadowError(nullptr, nullptr, "function"); c.shadowError(nullptr, nullptr, "argument"); c.constArgumentError(nullptr, nullptr, nullptr); c.comparePointersError(nullptr, nullptr, nullptr); c.redundantAssignmentError(nullptr, nullptr, "var", false); c.redundantInitializationError(nullptr, nullptr, "var", false); const std::vector nullvec; c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); } static std::string myName() { return "Other"; } std::string classInfo() const OVERRIDE { return "Other checks\n" // error "- division with zero\n" "- scoped object destroyed immediately after construction\n" "- assignment in an assert statement\n" "- free() or delete of an invalid memory location\n" "- bitwise operation with negative right operand\n" "- provide wrong dimensioned array to pipe() system command (--std=posix)\n" "- cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n" "- race condition with non-interlocked access after InterlockedDecrement() call\n" "- expression 'x = x++;' depends on order of evaluation of side effects\n" // warning "- either division by zero or useless condition\n" "- access of moved or forwarded variable.\n" // performance "- redundant data copying for const variable\n" "- subsequent assignment or copying to a variable or buffer\n" "- passing parameter by value\n" // portability "- Passing NULL pointer to function with variable number of arguments leads to UB.\n" // style "- C-style pointer cast in C++ code\n" "- casting between incompatible pointer types\n" "- [Incomplete statement](IncompleteStatement)\n" "- [check how signed char variables are used](CharVar)\n" "- variable scope can be limited\n" "- unusual pointer arithmetic. For example: \"abc\" + 'd'\n" "- redundant assignment, increment, or bitwise operation in a switch statement\n" "- redundant strcpy in a switch statement\n" "- Suspicious case labels in switch()\n" "- assignment of a variable to itself\n" "- Comparison of values leading always to true or false\n" "- Clarify calculation with parentheses\n" "- suspicious comparison of '\\0' with a char\\* variable\n" "- duplicate break statement\n" "- unreachable code\n" "- testing if unsigned variable is negative/positive\n" "- Suspicious use of ; at the end of 'if/for/while' statement.\n" "- Array filled incompletely using memset/memcpy/memmove.\n" "- NaN (not a number) value used in arithmetic expression.\n" "- comma in return statement (the comma can easily be misread as a semicolon).\n" "- prefer erfc, expm1 or log1p to avoid loss of precision.\n" "- identical code in both branches of if/else or ternary operator.\n" "- redundant pointer operation on pointer like &\\*some_ptr.\n" "- find unused 'goto' labels.\n" "- function declaration and definition argument names different.\n" "- function declaration and definition argument order different.\n" "- shadow variable.\n" "- variable can be declared const.\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkotherH cppcheck-1.90/lib/checkpostfixoperator.cpp000066400000000000000000000064061357737443600207740ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- // You should use ++ and -- as prefix whenever possible as these are more // efficient than postfix operators //--------------------------------------------------------------------------- #include "checkpostfixoperator.h" #include "errorlogger.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckPostfixOperator instance; } // CWE ids used static const struct CWE CWE398(398U); // Indicator of Poor Code Quality void CheckPostfixOperator::postfixOperator() { if (!mSettings->isEnabled(Settings::PERFORMANCE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const Variable *var = tok->variable(); if (!var || !Token::Match(tok, "%var% ++|--")) continue; const Token* parent = tok->next()->astParent(); if (!parent || parent->str() == ";" || (parent->str() == "," && (!parent->astParent() || parent->astParent()->str() != "("))) { if (var->isPointer() || var->isArray()) continue; if (Token::Match(var->nameToken()->previous(), "iterator|const_iterator|reverse_iterator|const_reverse_iterator")) { // the variable is an iterator postfixOperatorError(tok); } else if (var->type()) { // the variable is an instance of class postfixOperatorError(tok); } } } } } //--------------------------------------------------------------------------- void CheckPostfixOperator::postfixOperatorError(const Token *tok) { reportError(tok, Severity::performance, "postfixOperator", "Prefer prefix ++/-- operators for non-primitive types.\n" "Prefix ++/-- operators should be preferred for non-primitive types. " "Pre-increment/decrement can be more efficient than " "post-increment/decrement. Post-increment/decrement usually " "involves keeping a copy of the previous value around and " "adds a little extra code.", CWE398, false); } cppcheck-1.90/lib/checkpostfixoperator.h000066400000000000000000000051151357737443600204350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkpostfixoperatorH #define checkpostfixoperatorH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "tokenize.h" #include class ErrorLogger; class Settings; class Token; /// @addtogroup Checks /// @{ /** * @brief Using postfix operators ++ or -- rather than postfix operator. */ class CPPCHECKLIB CheckPostfixOperator : public Check { public: /** This constructor is used when registering the CheckPostfixOperator */ CheckPostfixOperator() : Check(myName()) { } /** This constructor is used when running checks. */ CheckPostfixOperator(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (tokenizer->isC()) return; CheckPostfixOperator checkPostfixOperator(tokenizer, settings, errorLogger); checkPostfixOperator.postfixOperator(); } /** Check postfix operators */ void postfixOperator(); private: /** Report Error */ void postfixOperatorError(const Token *tok); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckPostfixOperator c(nullptr, settings, errorLogger); c.postfixOperatorError(nullptr); } static std::string myName() { return "Using postfix operators"; } std::string classInfo() const OVERRIDE { return "Warn if using postfix operators ++ or -- rather than prefix operator\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkpostfixoperatorH cppcheck-1.90/lib/checksizeof.cpp000066400000000000000000000500741357737443600170230ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checksizeof.h" #include "errorlogger.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckSizeof instance; } // CWE IDs used: static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE467(467U); // Use of sizeof() on a Pointer Type static const struct CWE CWE682(682U); // Incorrect Calculation //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckSizeof::checkSizeofForNumericParameter() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %num% )") || Token::Match(tok, "sizeof %num%")) { sizeofForNumericParameterError(tok); } } } } void CheckSizeof::sizeofForNumericParameterError(const Token *tok) { reportError(tok, Severity::warning, "sizeofwithnumericparameter", "Suspicious usage of 'sizeof' with a numeric constant as parameter.\n" "It is unusual to use a constant value with sizeof. For example, 'sizeof(10)'" " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10. 'sizeof('A')'" " and 'sizeof(char)' can return different results.", CWE682, false); } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void CheckSizeof::checkSizeofForArrayParameter() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof ( %var% )") || Token::Match(tok, "sizeof %var% !![")) { const Token* varTok = tok->next(); if (varTok->str() == "(") { varTok = varTok->next(); } const Variable *var = varTok->variable(); if (var && var->isArray() && var->isArgument() && !var->isReference()) sizeofForArrayParameterError(tok); } } } } void CheckSizeof::sizeofForArrayParameterError(const Token *tok) { reportError(tok, Severity::warning, "sizeofwithsilentarraypointer", "Using 'sizeof' on array given as function argument " "returns size of a pointer.\n" "Using 'sizeof' for array given as function argument returns the size of a pointer. " "It does not return the size of the whole array in bytes as might be " "expected. For example, this code:\n" " int f(char a[100]) {\n" " return sizeof(a);\n" " }\n" "returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the " "size of the array in bytes).", CWE467, false ); } void CheckSizeof::checkSizeofForPointerSize() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { const Token* tokSize; const Token* tokFunc; const Token *variable = nullptr; const Token *variable2 = nullptr; // Find any function that may use sizeof on a pointer // Once leaving those tests, it is mandatory to have: // - variable matching the used pointer // - tokVar pointing on the argument where sizeof may be used if (Token::Match(tok->tokAt(2), "malloc|alloca|calloc (")) { if (Token::Match(tok, "%var% =")) variable = tok; else if (tok->strAt(1) == ")" && Token::Match(tok->linkAt(1)->tokAt(-2), "%var% =")) variable = tok->linkAt(1)->tokAt(-2); else if (tok->link() && Token::Match(tok, "> ( malloc|alloca|calloc (") && Token::Match(tok->link()->tokAt(-3), "%var% =")) variable = tok->link()->tokAt(-3); tokSize = tok->tokAt(4); tokFunc = tok->tokAt(2); } else if (Token::simpleMatch(tok, "memset (") && tok->strAt(-1) != ".") { variable = tok->tokAt(2); tokSize = variable->nextArgument(); if (tokSize) tokSize = tokSize->nextArgument(); tokFunc = tok; } else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (") && tok->strAt(-1) != ".") { variable = tok->tokAt(2); variable2 = variable->nextArgument(); if (!variable2) continue; tokSize = variable2->nextArgument(); tokFunc = tok; } else { continue; } if (tokSize && tokFunc->str() == "calloc") tokSize = tokSize->nextArgument(); if (tokSize) { const Token * const paramsListEndTok = tokFunc->linkAt(1); for (const Token* tok2 = tokSize; tok2 != paramsListEndTok; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "/ sizeof")) { // Allow division with sizeof(char) if (Token::simpleMatch(tok2->next(), "sizeof (")) { const Token *sztok = tok2->tokAt(2)->astOperand2(); const ValueType *vt = ((sztok != nullptr) ? sztok->valueType() : nullptr); if (vt && vt->type == ValueType::CHAR && vt->pointer == 0) continue; } divideBySizeofError(tok2, tokFunc->str()); } } } if (!variable || !tokSize) continue; while (Token::Match(variable, "%var% ::|.")) variable = variable->tokAt(2); while (Token::Match(variable2, "%var% ::|.")) variable2 = variable2->tokAt(2); // Ensure the variables are in the symbol database // Also ensure the variables are pointers // Only keep variables which are pointers const Variable *var = variable->variable(); if (!var || !var->isPointer() || var->isArray()) { variable = nullptr; } if (variable2) { var = variable2->variable(); if (!var || !var->isPointer() || var->isArray()) { variable2 = nullptr; } } // If there are no pointer variable at this point, there is // no need to continue if (variable == nullptr && variable2 == nullptr) { continue; } // Jump to the next sizeof token in the function and in the parameter // This is to allow generic operations with sizeof for (; tokSize && tokSize->str() != ")" && tokSize->str() != "," && tokSize->str() != "sizeof"; tokSize = tokSize->next()) {} if (tokSize->str() != "sizeof") continue; // Now check for the sizeof usage: Does the level of pointer indirection match? if (tokSize->linkAt(1)->strAt(-1) == "*") { if (variable && variable->valueType() && variable->valueType()->pointer == 1 && variable->valueType()->type != ValueType::VOID) sizeofForPointerError(variable, variable->str()); else if (variable2 && variable2->valueType() && variable2->valueType()->pointer == 1 && variable2->valueType()->type != ValueType::VOID) sizeofForPointerError(variable2, variable2->str()); } if (Token::simpleMatch(tokSize, "sizeof ( &")) tokSize = tokSize->tokAt(3); else if (Token::Match(tokSize, "sizeof (|&")) tokSize = tokSize->tokAt(2); else tokSize = tokSize->next(); while (Token::Match(tokSize, "%var% ::|.")) tokSize = tokSize->tokAt(2); if (Token::Match(tokSize, "%var% [|(")) continue; // Now check for the sizeof usage again. Once here, everything using sizeof(varid) or sizeof(&varid) // looks suspicious if (variable && tokSize->varId() == variable->varId()) sizeofForPointerError(variable, variable->str()); if (variable2 && tokSize->varId() == variable2->varId()) sizeofForPointerError(variable2, variable2->str()); } } } void CheckSizeof::sizeofForPointerError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "pointerSize", "Size of pointer '" + varname + "' used instead of size of its data.\n" "Size of pointer '" + varname + "' used instead of size of its data. " "This is likely to lead to a buffer overflow. You probably intend to " "write 'sizeof(*" + varname + ")'.", CWE467, false); } void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfunc) { reportError(tok, Severity::warning, "sizeofDivisionMemfunc", "Division by result of sizeof(). " + memfunc + "() expects a size in bytes, did you intend to multiply instead?", CWE682, false); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CheckSizeof::sizeofsizeof() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof (| sizeof")) { sizeofsizeofError(tok); tok = tok->next(); } } } void CheckSizeof::sizeofsizeofError(const Token *tok) { reportError(tok, Severity::warning, "sizeofsizeof", "Calling 'sizeof' on 'sizeof'.\n" "Calling sizeof for 'sizeof looks like a suspicious code and " "most likely there should be just one 'sizeof'. The current " "code is equivalent to 'sizeof(size_t)'", CWE682, false); } //----------------------------------------------------------------------------- void CheckSizeof::sizeofCalculation() { if (!mSettings->isEnabled(Settings::WARNING)) return; const bool printInconclusive = mSettings->inconclusive; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "sizeof (")) continue; // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro if (tok->isExpandedMacro() && tok->previous()) { const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok; if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") || Token::simpleMatch(cast_end->previous(), "static_cast")) { continue; } } const Token *argument = tok->next()->astOperand2(); if (!argument || !argument->isCalculation()) continue; bool inconclusive = false; if (argument->isExpandedMacro()) inconclusive = true; else if (tok->next()->isExpandedMacro()) inconclusive = true; if (!inconclusive || printInconclusive) sizeofCalculationError(argument, inconclusive); } } void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) { reportError(tok, Severity::warning, "sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive); } //----------------------------------------------------------------------------- void CheckSizeof::sizeofFunction() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro if (tok->isExpandedMacro() && tok->previous()) { const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok; if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") || Token::simpleMatch(cast_end->previous(), "static_cast")) { continue; } } if (const Token *argument = tok->next()->astOperand2()) { const Token *checkToken = argument->previous(); if (checkToken->tokType() == Token::eName) break; const Function * fun = checkToken->function(); // Don't report error if the function is overloaded if (fun && fun->nestedIn->functionMap.count(checkToken->str()) == 1) { sizeofFunctionError(tok); } } } } } void CheckSizeof::sizeofFunctionError(const Token *tok) { reportError(tok, Severity::warning, "sizeofFunctionCall", "Found function call inside sizeof().", CWE682, false); } //----------------------------------------------------------------------------- // Check for code like sizeof()*sizeof() or sizeof(ptr)/value //----------------------------------------------------------------------------- void CheckSizeof::suspiciousSizeofCalculation() { if (!mSettings->isEnabled(Settings::WARNING) || !mSettings->inconclusive) return; // TODO: Use AST here. This should be possible as soon as sizeof without brackets is correctly parsed for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { const Token* const end = tok->linkAt(1); const Variable* var = end->previous()->variable(); if (end->strAt(-1) == "*" || (var && var->isPointer() && !var->isArray())) { if (end->strAt(1) == "/") divideSizeofError(tok); } else if (Token::simpleMatch(end, ") * sizeof") && end->next()->astOperand1() == tok->next()) multiplySizeofError(tok); } } } void CheckSizeof::multiplySizeofError(const Token *tok) { reportError(tok, Severity::warning, "multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", CWE682, true); } void CheckSizeof::divideSizeofError(const Token *tok) { reportError(tok, Severity::warning, "divideSizeof", "Division of result of sizeof() on pointer type.\n" "Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, " "not the size of the memory area it points to.", CWE682, true); } void CheckSizeof::sizeofVoid() { if (!mSettings->isEnabled(Settings::PORTABILITY)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof ( )")) { // "sizeof(void)" gets simplified to sizeof ( ) sizeofVoidError(tok); } else if (Token::simpleMatch(tok, "sizeof (") && tok->next()->astOperand2()) { const ValueType *vt = tok->next()->astOperand2()->valueType(); if (vt && vt->type == ValueType::Type::VOID && vt->pointer == 0U) sizeofDereferencedVoidPointerError(tok, tok->strAt(3)); } else if (tok->str() == "-") { // only warn for: 'void *' - 'integral' const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; const bool op1IsvoidPointer = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); const bool op2IsIntegral = (vt2 && vt2->isIntegral() && vt2->pointer == 0U); if (op1IsvoidPointer && op2IsIntegral) arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str()); } else if (Token::Match(tok, "+|++|--|+=|-=")) { // Arithmetic operations on variable of type "void*" const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; const bool voidpointer1 = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); const bool voidpointer2 = (vt2 && vt2->type == ValueType::Type::VOID && vt2->pointer == 1U); if (voidpointer1) arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str()); if (!tok->isAssignmentOp() && voidpointer2) arithOperationsOnVoidPointerError(tok, tok->astOperand2()->expressionString(), vt2->str()); } } } void CheckSizeof::sizeofVoidError(const Token *tok) { const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard."; const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose, CWE682, false); } void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname) { const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard."; const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose, CWE682, false); } void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype) { const std::string message = "'$symbol' is of type '" + vartype + "'. When using void pointers in calculations, the behaviour is undefined."; const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", "$symbol:" + varname + '\n' + message + '\n' + verbose, CWE467, false); } cppcheck-1.90/lib/checksizeof.h000066400000000000000000000123021357737443600164600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checksizeofH #define checksizeofH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** @brief checks on usage of sizeof() operator */ class CPPCHECKLIB CheckSizeof : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckSizeof() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckSizeof(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) OVERRIDE { CheckSizeof checkSizeof(tokenizer, settings, errorLogger); // Checks checkSizeof.sizeofsizeof(); checkSizeof.sizeofCalculation(); checkSizeof.sizeofFunction(); checkSizeof.suspiciousSizeofCalculation(); checkSizeof.checkSizeofForArrayParameter(); checkSizeof.checkSizeofForPointerSize(); checkSizeof.checkSizeofForNumericParameter(); checkSizeof.sizeofVoid(); } /** @brief %Check for 'sizeof sizeof ..' */ void sizeofsizeof(); /** @brief %Check for calculations inside sizeof */ void sizeofCalculation(); /** @brief %Check for function call inside sizeof */ void sizeofFunction(); /** @brief %Check for suspicious calculations with sizeof results */ void suspiciousSizeofCalculation(); /** @brief %Check for using sizeof with array given as function argument */ void checkSizeofForArrayParameter(); /** @brief %Check for using sizeof of a variable when allocating it */ void checkSizeofForPointerSize(); /** @brief %Check for using sizeof with numeric given as function argument */ void checkSizeofForNumericParameter(); /** @brief %Check for using sizeof(void) */ void sizeofVoid(); private: // Error messages.. void sizeofsizeofError(const Token* tok); void sizeofCalculationError(const Token* tok, bool inconclusive); void sizeofFunctionError(const Token* tok); void multiplySizeofError(const Token* tok); void divideSizeofError(const Token* tok); void sizeofForArrayParameterError(const Token* tok); void sizeofForPointerError(const Token* tok, const std::string &varname); void divideBySizeofError(const Token* tok, const std::string &memfunc); void sizeofForNumericParameterError(const Token* tok); void sizeofVoidError(const Token *tok); void sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname); void arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype); void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE { CheckSizeof c(nullptr, settings, errorLogger); c.sizeofForArrayParameterError(nullptr); c.sizeofForPointerError(nullptr, "varname"); c.divideBySizeofError(nullptr, "memset"); c.sizeofForNumericParameterError(nullptr); c.sizeofsizeofError(nullptr); c.sizeofCalculationError(nullptr, false); c.sizeofFunctionError(nullptr); c.multiplySizeofError(nullptr); c.divideSizeofError(nullptr); c.sizeofVoidError(nullptr); c.sizeofDereferencedVoidPointerError(nullptr, "varname"); c.arithOperationsOnVoidPointerError(nullptr, "varname", "vartype"); } static std::string myName() { return "Sizeof"; } std::string classInfo() const OVERRIDE { return "sizeof() usage checks\n" "- sizeof for array given as function argument\n" "- sizeof for numeric given as function argument\n" "- using sizeof(pointer) instead of the size of pointed data\n" "- look for 'sizeof sizeof ..'\n" "- look for calculations inside sizeof()\n" "- look for function calls inside sizeof()\n" "- look for suspicious calculations with sizeof()\n" "- using 'sizeof(void)' which is undefined\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checksizeofH cppcheck-1.90/lib/checkstl.cpp000066400000000000000000003150531357737443600163270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkstl.h" #include "checknullpointer.h" #include "errorlogger.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "utils.h" #include "astutils.h" #include "pathanalysis.h" #include #include #include #include #include #include // Register this check class (by creating a static instance of it) namespace { CheckStl instance; } // CWE IDs used: static const struct CWE CWE398(398U); // Indicator of Poor Code Quality static const struct CWE CWE597(597U); // Use of Wrong Operator in String Comparison static const struct CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const struct CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime static const struct CWE CWE704(704U); // Incorrect Type Conversion or Cast static const struct CWE CWE762(762U); // Mismatched Memory Management Routines static const struct CWE CWE786(786U); // Access of Memory Location Before Start of Buffer static const struct CWE CWE788(788U); // Access of Memory Location After End of Buffer static const struct CWE CWE825(825U); // Expired Pointer Dereference static const struct CWE CWE834(834U); // Excessive Iteration void CheckStl::outOfBounds() { for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { const Library::Container *container = getLibraryContainer(tok); if (!container) continue; const Token * parent = astParentSkipParens(tok); for (const ValueFlow::Value &value : tok->values()) { if (!value.isContainerSizeValue()) continue; if (value.isImpossible()) continue; if (value.isInconclusive() && !mSettings->inconclusive) continue; if (!value.errorSeverity() && !mSettings->isEnabled(Settings::WARNING)) continue; if (value.intvalue == 0 && Token::Match(parent, ". %name% (") && container->getYield(parent->strAt(1)) == Library::Container::Yield::ITEM) { outOfBoundsError(parent->tokAt(2), tok->expressionString(), &value, parent->strAt(1), nullptr); continue; } if (Token::Match(tok, "%name% . %name% (") && container->getYield(tok->strAt(2)) == Library::Container::Yield::START_ITERATOR) { const Token *fparent = tok->tokAt(3)->astParent(); const Token *other = nullptr; if (Token::simpleMatch(fparent, "+") && fparent->astOperand1() == tok->tokAt(3)) other = fparent->astOperand2(); else if (Token::simpleMatch(fparent, "+") && fparent->astOperand2() == tok->tokAt(3)) other = fparent->astOperand1(); if (other && other->hasKnownIntValue() && other->getKnownIntValue() > value.intvalue) { outOfBoundsError(fparent, tok->expressionString(), &value, other->expressionString(), &other->values().back()); continue; } else if (other && !other->hasKnownIntValue() && value.isKnown() && value.intvalue==0) { outOfBoundsError(fparent, tok->expressionString(), &value, other->expressionString(), nullptr); continue; } } if (!container->arrayLike_indexOp && !container->stdStringLike) continue; if (value.intvalue == 0 && Token::Match(parent, "[") && tok == parent->astOperand1()) { outOfBoundsError(parent, tok->expressionString(), &value, "", nullptr); continue; } if (container->arrayLike_indexOp && Token::Match(parent, "[")) { const ValueFlow::Value *indexValue = parent->astOperand2() ? parent->astOperand2()->getMaxValue(false) : nullptr; if (indexValue && indexValue->intvalue >= value.intvalue) { outOfBoundsError(parent, tok->expressionString(), &value, parent->astOperand2()->expressionString(), indexValue); continue; } if (mSettings->isEnabled(Settings::WARNING)) { indexValue = parent->astOperand2() ? parent->astOperand2()->getMaxValue(true) : nullptr; if (indexValue && indexValue->intvalue >= value.intvalue) { outOfBoundsError(parent, tok->expressionString(), &value, parent->astOperand2()->expressionString(), indexValue); continue; } } } } } } } void CheckStl::outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue) { // Do not warn if both the container size and index value are possible if (containerSize && indexValue && containerSize->isPossible() && indexValue->isPossible()) return; const std::string expression = tok ? tok->expressionString() : (containerName+"[x]"); std::string errmsg; if (!containerSize) errmsg = "Out of bounds access in expression '" + expression + "'"; else if (containerSize->intvalue == 0) { if (containerSize->condition) errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or expression '" + expression + "' cause access out of bounds."; else if (indexValue == nullptr && !index.empty()) errmsg = "Out of bounds access in expression '" + expression + "' because '$symbol' is empty and '" + index + "' may be non-zero."; else errmsg = "Out of bounds access in expression '" + expression + "' because '$symbol' is empty."; } else if (indexValue) { if (containerSize->condition) errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or $symbol size can be " + MathLib::toString(containerSize->intvalue) + ". Expression '" + expression + "' cause access out of bounds."; else if (indexValue->condition) errmsg = ValueFlow::eitherTheConditionIsRedundant(indexValue->condition) + " or '" + index + "' can have the value " + MathLib::toString(indexValue->intvalue) + ". Expression '" + expression + "' cause access out of bounds."; else errmsg = "Out of bounds access in '" + expression + "', if '$symbol' size is " + MathLib::toString(containerSize->intvalue) + " and '" + index + "' is " + MathLib::toString(indexValue->intvalue); } else { // should not happen return; } ErrorPath errorPath; if (!indexValue) errorPath = getErrorPath(tok, containerSize, "Access out of bounds"); else { ErrorPath errorPath1 = getErrorPath(tok, containerSize, "Access out of bounds"); ErrorPath errorPath2 = getErrorPath(tok, indexValue, "Access out of bounds"); if (errorPath1.size() <= 1) errorPath = errorPath2; else if (errorPath2.size() <= 1) errorPath = errorPath1; else { errorPath = errorPath1; errorPath.splice(errorPath.end(), errorPath2); } } reportError(errorPath, (containerSize && !containerSize->errorSeverity()) || (indexValue && !indexValue->errorSeverity()) ? Severity::warning : Severity::error, "containerOutOfBounds", "$symbol:" + containerName +"\n" + errmsg, CWE398, (containerSize && containerSize->isInconclusive()) || (indexValue && indexValue->isInconclusive())); } bool CheckStl::isContainerSize(const Token *containerToken, const Token *expr) const { if (!Token::simpleMatch(expr, "( )")) return false; if (!Token::Match(expr->astOperand1(), ". %name% (")) return false; if (!isSameExpression(mTokenizer->isCPP(), false, containerToken, expr->astOperand1()->astOperand1(), mSettings->library, false, false)) return false; return containerToken->valueType()->container->getYield(expr->previous()->str()) == Library::Container::Yield::SIZE; } bool CheckStl::isContainerSizeGE(const Token * containerToken, const Token *expr) const { if (!expr) return false; if (isContainerSize(containerToken, expr)) return true; if (expr->str() == "*") { const Token *mul; if (isContainerSize(containerToken, expr->astOperand1())) mul = expr->astOperand2(); else if (isContainerSize(containerToken, expr->astOperand2())) mul = expr->astOperand1(); else return false; return mul && (!mul->hasKnownIntValue() || mul->values().front().intvalue != 0); } if (expr->str() == "+") { const Token *op; if (isContainerSize(containerToken, expr->astOperand1())) op = expr->astOperand2(); else if (isContainerSize(containerToken, expr->astOperand2())) op = expr->astOperand1(); else return false; return op && op->getValueGE(0, mSettings); } return false; } void CheckStl::outOfBoundsIndexExpression() { for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { if (!tok->isName() || !tok->valueType()) continue; const Library::Container *container = tok->valueType()->container; if (!container) continue; if (!container->arrayLike_indexOp && !container->stdStringLike) continue; if (!Token::Match(tok, "%name% [")) continue; if (isContainerSizeGE(tok, tok->next()->astOperand2())) outOfBoundsIndexExpressionError(tok, tok->next()->astOperand2()); } } } void CheckStl::outOfBoundsIndexExpressionError(const Token *tok, const Token *index) { const std::string varname = tok ? tok->str() : std::string("var"); const std::string i = index ? index->expressionString() : std::string(varname + ".size()"); std::string errmsg = "Out of bounds access of $symbol, index '" + i + "' is out of bounds."; reportError(tok, Severity::error, "containerOutOfBoundsIndexExpression", "$symbol:" + varname +"\n" + errmsg, CWE398, false); } // Error message for bad iterator usage.. void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName) { reportError(tok, Severity::error, "invalidIterator1", "$symbol:"+iteratorName+"\nInvalid iterator: $symbol", CWE664, false); } void CheckStl::iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2) { reportError(tok, Severity::error, "iterators1", "$symbol:" + containerName1 + "\n" "$symbol:" + containerName2 + "\n" "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false); } void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2) { std::list callstack = { tok, containerTok }; reportError(callstack, Severity::error, "iterators2", "$symbol:" + containerName1 + "\n" "$symbol:" + containerName2 + "\n" "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false); } void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName) { std::list callstack = { tok, containerTok }; reportError(callstack, Severity::error, "iterators3", "$symbol:" + containerName + "\n" "Same iterator is used with containers '" + containerName + "' that are defined in different scopes.", CWE664, false); } void CheckStl::iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName1, const std::string& containerName2) { std::list callstack = { cmpOperatorTok, containerTok1, containerTok2 }; reportError(callstack, Severity::error, "iteratorsCmp1", "$symbol:" + containerName1 + "\n" "$symbol:" + containerName2 + "\n" "Comparison of iterators from containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, false); } void CheckStl::iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName) { std::list callstack = { cmpOperatorTok, containerTok1, containerTok2 }; reportError(callstack, Severity::error, "iteratorsCmp2", "$symbol:" + containerName + "\n" "Comparison of iterators from containers '" + containerName + "' that are defined in different scopes.", CWE664, false); } // Error message used when dereferencing an iterator that has been erased.. void CheckStl::dereferenceErasedError(const Token *erased, const Token* deref, const std::string &itername, bool inconclusive) { if (erased) { std::list callstack = { deref, erased }; reportError(callstack, Severity::error, "eraseDereference", "$symbol:" + itername + "\n" "Iterator '$symbol' used after element has been erased.\n" "The iterator '$symbol' is invalid after the element it pointed to has been erased. " "Dereferencing or comparing it with another iterator is invalid operation.", CWE664, inconclusive); } else { reportError(deref, Severity::error, "eraseDereference", "$symbol:" + itername + "\n" "Invalid iterator '$symbol' used.\n" "The iterator '$symbol' is invalid before being assigned. " "Dereferencing or comparing it with another iterator is invalid operation.", CWE664, inconclusive); } } static const Token *skipMembers(const Token *tok) { while (Token::Match(tok, "%name% .")) tok = tok->tokAt(2); return tok; } static bool isIterator(const Variable *var, bool& inconclusiveType) { // Check that its an iterator if (!var || !var->isLocal() || !Token::Match(var->typeEndToken(), "iterator|const_iterator|reverse_iterator|const_reverse_iterator|auto")) return false; inconclusiveType = false; if (var->typeEndToken()->str() == "auto") return (var->nameToken()->valueType() && var->nameToken()->valueType()->type == ValueType::Type::ITERATOR); if (var->type()) { // If it is defined, ensure that it is defined like an iterator // look for operator* and operator++ const Function* end = var->type()->getFunction("operator*"); const Function* incOperator = var->type()->getFunction("operator++"); if (!end || end->argCount() > 0 || !incOperator) { return false; } else { inconclusiveType = true; // heuristics only } } return true; } static std::string getContainerName(const Token *containerToken) { if (!containerToken) return std::string(); std::string ret(containerToken->str()); for (const Token *nametok = containerToken; nametok; nametok = nametok->tokAt(-2)) { if (!Token::Match(nametok->tokAt(-2), "%name% .")) break; ret = nametok->strAt(-2) + '.' + ret; } return ret; } enum OperandPosition { Left, Right }; static const Token* findIteratorContainer(const Token* start, const Token* end, nonneg int id) { const Token* containerToken = nullptr; for (const Token* tok = start; tok != end; tok = tok->next()) { if (Token::Match(tok, "%varid% = %name% . %name% (", id)) { // Iterator is assigned to value if (tok->tokAt(5)->valueType() && tok->tokAt(5)->valueType()->type == ValueType::Type::ITERATOR) { containerToken = tok->tokAt(2); } } else if (Token::Match(tok, "%varid% = %name% (", id)) { // Prevent FP: iterator is assigned to something // TODO: Fix it in future containerToken = nullptr; } } return containerToken; } static bool isVector(const Token* tok) { if (!tok) return false; const Variable *var = tok->variable(); const Token *decltok = var ? var->typeStartToken() : nullptr; return Token::simpleMatch(decltok, "std :: vector"); } void CheckStl::iterators() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // Filling map of iterators id and their scope begin std::map iteratorScopeBeginInfo; for (const Variable* var : symbolDatabase->variableList()) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) continue; const int iteratorId = var->declarationId(); if (iteratorId != 0) iteratorScopeBeginInfo[iteratorId] = var->nameToken(); } // Storage to save found comparison problems to avoid duplicate error messages std::set foundOperatorErrors; for (const Variable* var : symbolDatabase->variableList()) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) continue; if (inconclusiveType && !mSettings->inconclusive) continue; const int iteratorId = var->declarationId(); // the validIterator flag says if the iterator has a valid value or not bool validIterator = Token::Match(var->nameToken()->next(), "[(=:{]"); const Scope* invalidationScope = nullptr; // The container this iterator can be used with const Token* containerToken = nullptr; const Scope* containerAssignScope = nullptr; // When "validatingToken" is reached the validIterator is set to true const Token* validatingToken = nullptr; const Token* eraseToken = nullptr; // Scan through the rest of the code and see if the iterator is // used against other containers. for (const Token *tok2 = var->nameToken(); tok2 && tok2 != var->scope()->bodyEnd; tok2 = tok2->next()) { if (invalidationScope && tok2 == invalidationScope->bodyEnd) validIterator = true; // Assume that the iterator becomes valid again if (containerAssignScope && tok2 == containerAssignScope->bodyEnd) containerToken = nullptr; // We don't know which containers might be used with the iterator if (tok2 == validatingToken) { validIterator = true; eraseToken = nullptr; invalidationScope = nullptr; } // Is comparison expression? // Check whether iterator compared against different container or iterator of different container? if (tok2->isComparisonOp() && tok2->astOperand1() && tok2->astOperand2() && (foundOperatorErrors.find(tok2) == foundOperatorErrors.end()) && compareIteratorAgainstDifferentContainer(tok2, containerToken, iteratorId, iteratorScopeBeginInfo)) { foundOperatorErrors.insert(tok2); } // Is the iterator used in a insert/erase operation? else if (Token::Match(tok2, "%name% . insert|erase ( *| %varid% )|,", iteratorId) && !isVector(tok2)) { const Token* itTok = tok2->tokAt(4); if (itTok->str() == "*") { if (tok2->strAt(2) == "insert") continue; itTok = itTok->next(); } // It is bad to insert/erase an invalid iterator if (!validIterator) invalidIteratorError(tok2, itTok->str()); // If insert/erase is used on different container then // report an error if (containerToken && tok2->varId() != containerToken->varId()) { // skip error message if container is a set.. const Variable *variableInfo = tok2->variable(); const Token *decltok = variableInfo ? variableInfo->typeStartToken() : nullptr; if (Token::simpleMatch(decltok, "std :: set")) continue; // No warning // skip error message if the iterator is erased/inserted by value if (itTok->previous()->str() == "*") continue; // inserting iterator range.. if (tok2->strAt(2) == "insert") { const Token *par2 = itTok->nextArgument(); if (!par2 || par2->nextArgument()) continue; while (par2->str() != ")") { if (par2->varId() == containerToken->varId()) break; bool inconclusiveType2=false; if (isIterator(par2->variable(), inconclusiveType2)) break; // TODO: check if iterator points at same container if (par2->str() == "(") par2 = par2->link(); par2 = par2->next(); } if (par2->str() != ")") continue; } // Not different containers if a reference is used.. if (containerToken && containerToken->variable() && containerToken->variable()->isReference()) { const Token *nameToken = containerToken->variable()->nameToken(); if (Token::Match(nameToken, "%name% =")) { const Token *name1 = nameToken->tokAt(2); const Token *name2 = tok2; while (Token::Match(name1, "%name%|.|::") && name2 && name1->str() == name2->str()) { name1 = name1->next(); name2 = name2->next(); } if (!Token::simpleMatch(name1, ";") || !Token::Match(name2, "[;,()=]")) continue; } } // Show error message, mismatching iterator is used. iteratorsError(tok2, getContainerName(containerToken), getContainerName(tok2)); } // invalidate the iterator if it is erased else if (tok2->strAt(2) == "erase" && (tok2->strAt(4) != "*" || (containerToken && tok2->varId() == containerToken->varId()))) { validIterator = false; eraseToken = tok2; invalidationScope = tok2->scope(); } // skip the operation tok2 = itTok->next(); } // it = foo.erase(.. // taking the result of an erase is ok else if (Token::Match(tok2, "%varid% = %name% .", iteratorId) && Token::simpleMatch(skipMembers(tok2->tokAt(2)), "erase (")) { // the returned iterator is valid validatingToken = skipMembers(tok2->tokAt(2))->linkAt(1); tok2 = validatingToken->link(); } // Reassign the iterator else if (Token::Match(tok2, "%varid% = %name% .", iteratorId) && Token::Match(skipMembers(tok2->tokAt(2)), "begin|rbegin|cbegin|crbegin|find (")) { validatingToken = skipMembers(tok2->tokAt(2))->linkAt(1); containerToken = skipMembers(tok2->tokAt(2))->tokAt(-2); if (containerToken->varId() == 0 || Token::simpleMatch(validatingToken, ") .")) containerToken = nullptr; containerAssignScope = tok2->scope(); // skip ahead tok2 = validatingToken->link(); } // Reassign the iterator else if (Token::Match(tok2, "%varid% =", iteratorId)) { break; } // Passing iterator to function. Iterator might be initialized else if (Token::Match(tok2, "%varid% ,|)", iteratorId)) { validIterator = true; } // Dereferencing invalid iterator? else if (!validIterator && Token::Match(tok2, "* %varid%", iteratorId)) { dereferenceErasedError(eraseToken, tok2, tok2->strAt(1), inconclusiveType); tok2 = tok2->next(); } else if (!validIterator && Token::Match(tok2, "%varid% . %name%", iteratorId)) { dereferenceErasedError(eraseToken, tok2, tok2->str(), inconclusiveType); tok2 = tok2->tokAt(2); } // bailout handling. Assume that the iterator becomes valid if we see return/break. // TODO: better handling else if (tok2->scope() == invalidationScope && Token::Match(tok2, "return|break|continue")) { validatingToken = Token::findsimplematch(tok2->next(), ";"); } // bailout handling. Assume that the iterator becomes valid if we see else. // TODO: better handling else if (tok2->str() == "else") { validIterator = true; } } } } bool CheckStl::compareIteratorAgainstDifferentContainer(const Token* operatorTok, const Token* containerTok, const nonneg int iteratorId, const std::map& iteratorScopeBeginInfo) { if (!containerTok) return false; const Token *otherOperand = nullptr; OperandPosition operandPosition; if (operatorTok->astOperand1()->varId() == iteratorId) { otherOperand = operatorTok->astOperand2(); operandPosition = OperandPosition::Right; } else if (operatorTok->astOperand2()->varId() == iteratorId) { otherOperand = operatorTok->astOperand1(); operandPosition = OperandPosition::Left; } if (!otherOperand) return false; const Token * const otherExprPart = otherOperand->tokAt(-3); if (Token::Match(otherExprPart, "%name% . end|rend|cend|crend ( )") && otherExprPart->varId() != containerTok->varId()) { const std::string& firstContainerName = getContainerName(containerTok); const std::string& secondContainerName = getContainerName(otherExprPart); if (firstContainerName != secondContainerName) { if (operandPosition == OperandPosition::Right) iteratorsError(operatorTok, containerTok, firstContainerName, secondContainerName); else iteratorsError(operatorTok, containerTok, secondContainerName, firstContainerName); } else { iteratorsError(operatorTok, containerTok, firstContainerName); } return true; } else { const int otherId = otherOperand->varId(); auto it = iteratorScopeBeginInfo.find(otherId); if (it != iteratorScopeBeginInfo.end()) { const Token* otherContainerToken = findIteratorContainer(it->second, operatorTok->astOperand1(), otherId); if (otherContainerToken && otherContainerToken->varId() != containerTok->varId()) { const std::string& firstContainerName = getContainerName(containerTok); const std::string& secondContainerName = getContainerName(otherContainerToken); if (firstContainerName != secondContainerName) { if (operandPosition == OperandPosition::Right) iteratorsCmpError(operatorTok, containerTok, otherContainerToken, firstContainerName, secondContainerName); else iteratorsCmpError(operatorTok, containerTok, otherContainerToken, secondContainerName, firstContainerName); } else { iteratorsCmpError(operatorTok, containerTok, otherContainerToken, firstContainerName); } return true; } } } return false; } // Error message for bad iterator usage.. void CheckStl::mismatchingContainersError(const Token *tok) { reportError(tok, Severity::error, "mismatchingContainers", "Iterators of different containers are used together.", CWE664, false); } void CheckStl::mismatchingContainerExpressionError(const Token *tok1, const Token *tok2) { const std::string expr1(tok1 ? tok1->expressionString() : std::string("v1")); const std::string expr2(tok2 ? tok2->expressionString() : std::string("v2")); reportError(tok1, Severity::warning, "mismatchingContainerExpression", "Iterators to containers from different expressions '" + expr1 + "' and '" + expr2 + "' are used together.", CWE664, false); } void CheckStl::sameIteratorExpressionError(const Token *tok) { reportError(tok, Severity::style, "sameIteratorExpression", "Same iterators expression are used for algorithm.", CWE664, false); } static const std::set algorithm2 = { // func(begin1, end1 "binary_search", "copy", "copy_if", "equal_range" , "generate", "is_heap", "is_heap_until", "is_partitioned" , "is_permutation", "is_sorted", "is_sorted_until", "lower_bound", "make_heap", "max_element", "minmax_element" , "min_element", "mismatch", "move", "move_backward", "next_permutation", "partition", "partition_copy" , "partition_point", "pop_heap", "prev_permutation", "push_heap", "random_shuffle", "remove", "remove_copy" , "remove_copy_if", "remove_if", "replace", "replace_copy", "replace_copy_if", "replace_if", "reverse", "reverse_copy" , "shuffle", "sort", "sort_heap", "stable_partition", "stable_sort", "swap_ranges", "transform", "unique" , "unique_copy", "upper_bound", "string", "wstring", "u16string", "u32string" }; static const std::set algorithm22 = { // func(begin1, end1, begin2, end2 "includes", "lexicographical_compare", "merge", "partial_sort_copy" , "set_difference", "set_intersection", "set_symmetric_difference", "set_union" }; static const std::set algorithm1x1 = { // func(begin1, x, end1 "nth_element", "partial_sort", "rotate", "rotate_copy" }; static const std::string iteratorBeginFuncPattern = "begin|cbegin|rbegin|crbegin"; static const std::string iteratorEndFuncPattern = "end|cend|rend|crend"; static const std::string pattern1x1_1 = "%name% . " + iteratorBeginFuncPattern + " ( ) , "; static const std::string pattern1x1_2 = "%name% . " + iteratorEndFuncPattern + " ( ) ,|)"; static const std::string pattern2 = pattern1x1_1 + pattern1x1_2; static const Variable *getContainer(const Token *argtok) { while (argtok && argtok->astOperand1()) argtok = argtok->astOperand1(); if (!Token::Match(argtok, "%var% . begin|end|rbegin|rend ( )")) // TODO: use Library yield return nullptr; const Variable *var = argtok->variable(); if (var && Token::simpleMatch(var->typeStartToken(), "std ::")) return var; return nullptr; } static const Token * getIteratorExpression(const Token * tok) { if (!tok) return nullptr; if (tok->isUnaryOp("*")) return nullptr; if (!tok->isName()) { const Token *iter1 = getIteratorExpression(tok->astOperand1()); if (iter1) return iter1; if (tok->str() == "(") return nullptr; const Token *iter2 = getIteratorExpression(tok->astOperand2()); if (iter2) return iter2; } else if (Token::Match(tok, "begin|cbegin|rbegin|crbegin|end|cend|rend|crend (")) { if (Token::Match(tok->previous(), ". %name% ( ) !!.")) return tok->previous()->astOperand1(); if (!Token::simpleMatch(tok->previous(), ".") && Token::Match(tok, "%name% ( !!)") && !Token::simpleMatch(tok->linkAt(1), ") .")) return tok->next()->astOperand2(); } return nullptr; } void CheckStl::mismatchingContainers() { // Check if different containers are used in various calls of standard functions const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%comp%|-")) { const Token * iter1 = getIteratorExpression(tok->astOperand1()); const Token * iter2 = getIteratorExpression(tok->astOperand2()); if (iter1 && iter2 && !isSameExpression(true, false, iter1, iter2, mSettings->library, false, false)) { mismatchingContainerExpressionError(iter1, iter2); continue; } } if (!Token::Match(tok, "%name% ( !!)")) continue; const Token * const ftok = tok; const Token * firstArg = nullptr; const std::vector args = getArguments(ftok); if (args.size() < 2) continue; std::map containerNr; for (int argnr = 1; argnr <= args.size(); ++argnr) { const Library::ArgumentChecks::IteratorInfo *i = mSettings->library.getArgIteratorInfo(ftok, argnr); if (!i) continue; const Token * const argTok = args[argnr - 1]; if (i->first) { firstArg = argTok; } if (i->last && firstArg && argTok && isSameExpression(true, false, firstArg, argTok, mSettings->library, false, false)) { sameIteratorExpressionError(firstArg); } const Variable *c = getContainer(argTok); if (c) { std::map::const_iterator it = containerNr.find(c); if (it == containerNr.end()) { for (it = containerNr.begin(); it != containerNr.end(); ++it) { if (it->second == i->container) { mismatchingContainersError(argTok); break; } } containerNr[c] = i->container; } else if (it->second != i->container) { mismatchingContainersError(argTok); } } else { if (i->last && firstArg && argTok) { const Token * iter1 = getIteratorExpression(firstArg); const Token * iter2 = getIteratorExpression(argTok); if (iter1 && iter2 && !isSameExpression(true, false, iter1, iter2, mSettings->library, false, false)) { mismatchingContainerExpressionError(iter1, iter2); } } } } const int ret = mSettings->library.returnValueContainer(ftok); if (ret != -1 && Token::Match(ftok->next()->astParent(), "==|!=")) { const Token *parent = ftok->next()->astParent(); const Token *other = (parent->astOperand1() == ftok->next()) ? parent->astOperand2() : parent->astOperand1(); const Variable *c = getContainer(other); if (c) { const std::map::const_iterator it = containerNr.find(c); if (it == containerNr.end() || it->second != ret) mismatchingContainersError(other); } } } } for (const Variable *var : symbolDatabase->variableList()) { if (var && var->isStlStringType() && Token::Match(var->nameToken(), "%var% (") && Token::Match(var->nameToken()->tokAt(2), pattern2.c_str())) { if (var->nameToken()->strAt(2) != var->nameToken()->strAt(8)) { mismatchingContainersError(var->nameToken()); } } } } static bool isInvalidMethod(const Token * tok) { if (Token::Match(tok->next(), ". assign|clear")) return true; if (Token::Match(tok->next(), "%assign%")) return true; if (isVector(tok) && Token::Match(tok->next(), ". insert|emplace|emplace_back|push_back|erase|pop_back|reserve (")) return true; return false; } static bool isVariableDecl(const Token* tok) { if (!tok) return false; const Variable* var = tok->variable(); if (!var) return false; if (var->nameToken() == tok) return true; if (Token::Match(var->declEndToken(), "; %var%") && var->declEndToken()->next() == tok) return true; return false; } void CheckStl::invalidContainer() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); const Library& library = mSettings->library; for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%var%")) continue; if (tok->varId() == 0) continue; if (!astIsContainer(tok)) continue; if (!isInvalidMethod(tok)) continue; // Skip if the variable is assigned to unsigned int skipVarId = 0; if (Token::Match(tok->astTop(), "%assign%") && Token::Match(tok->astTop()->previous(), "%var%")) skipVarId = tok->astTop()->previous()->varId(); const Token * endToken = nextAfterAstRightmostLeaf(tok->next()->astParent()); if (!endToken) endToken = tok->next(); const ValueFlow::Value* v = nullptr; ErrorPath errorPath; PathAnalysis::Info info = PathAnalysis{endToken, library} .forwardFind([&](const PathAnalysis::Info& info) { if (!info.tok->variable()) return false; if (info.tok->varId() == skipVarId) return false; if (info.tok->variable()->isReference() && !isVariableDecl(info.tok) && reaches(info.tok->variable()->nameToken(), tok, library, nullptr)) { ErrorPath ep; bool addressOf = false; const Variable* var = getLifetimeVariable(info.tok, ep, &addressOf); // Check the reference is created before the change if (var && var->declarationId() == tok->varId() && !addressOf) { // An argument always reaches if (var->isArgument() || (!var->isReference() && !var->isRValueReference() && !isVariableDecl(tok) && reaches(var->nameToken(), tok, library, &ep))) { errorPath = ep; return true; } } } for (const ValueFlow::Value& val:info.tok->values()) { if (!val.isLocalLifetimeValue()) continue; if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::Address) continue; if (!val.tokvalue->variable()) continue; if (val.tokvalue->varId() != tok->varId()) continue; ErrorPath ep; // Check the iterator is created before the change if (reaches(val.tokvalue, tok, library, &ep)) { v = &val; errorPath = ep; return true; } } return false; }); if (!info.tok) continue; errorPath.insert(errorPath.end(), info.errorPath.begin(), info.errorPath.end()); if (v) { invalidContainerError(info.tok, tok, v, errorPath); } else { invalidContainerReferenceError(info.tok, tok, errorPath); } } } } void CheckStl::invalidContainerError(const Token *tok, const Token * contTok, const ValueFlow::Value *val, ErrorPath errorPath) { const bool inconclusive = val ? val->isInconclusive() : false; std::string method = contTok ? contTok->strAt(2) : "erase"; errorPath.emplace_back(contTok, "After calling '" + method + "', iterators or references to the container's data may be invalid ."); if (val) errorPath.insert(errorPath.begin(), val->errorPath.begin(), val->errorPath.end()); std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "invalidContainer", msg + " that may be invalid.", CWE664, inconclusive); } void CheckStl::invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath) { std::string method = contTok ? contTok->strAt(2) : "erase"; std::string name = contTok ? contTok->expressionString() : "x"; errorPath.emplace_back( contTok, "After calling '" + method + "', iterators or references to the container's data may be invalid ."); std::string msg = "Reference to " + name; errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "invalidContainerReference", msg + " that may be invalid.", CWE664, false); } void CheckStl::stlOutOfBounds() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // Scan through all scopes.. for (const Scope &scope : symbolDatabase->scopeList) { const Token* tok = scope.classDef; // only interested in conditions if ((scope.type != Scope::eFor && scope.type != Scope::eWhile && scope.type != Scope::eIf && scope.type != Scope::eDo) || !tok) continue; const Token *condition = nullptr; if (scope.type == Scope::eFor) { if (Token::simpleMatch(tok->next()->astOperand2(), ";") && Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) condition = tok->next()->astOperand2()->astOperand2()->astOperand1(); } else if (Token::simpleMatch(tok, "do {") && Token::simpleMatch(tok->linkAt(1), "} while (")) condition = tok->linkAt(1)->tokAt(2)->astOperand2(); else condition = tok->next()->astOperand2(); if (!condition) continue; std::vector conds; visitAstNodes(condition, [&](const Token *cond) { if (Token::Match(cond, "%oror%|&&")) return ChildrenToVisit::op1_and_op2; if (cond->isComparisonOp()) conds.emplace_back(cond); return ChildrenToVisit::none; }); for (const Token *cond : conds) { const Token *vartok; const Token *containerToken; if (Token::Match(cond, "<= %var% . %name% ( )") && Token::Match(cond->astOperand1(), "%var%")) { vartok = cond->astOperand1(); containerToken = cond->next(); } else { continue; } // Is it a array like container? const Library::Container* container = containerToken->valueType() ? containerToken->valueType()->container : nullptr; if (!container) continue; if (container->getYield(containerToken->strAt(2)) != Library::Container::Yield::SIZE) continue; // variable id for loop variable. const int numId = vartok->varId(); // variable id for the container variable const int declarationId = containerToken->varId(); const std::string &containerName = containerToken->str(); for (const Token *tok3 = scope.bodyStart; tok3 && tok3 != scope.bodyEnd; tok3 = tok3->next()) { if (tok3->varId() == declarationId) { tok3 = tok3->next(); if (Token::Match(tok3, ". %name% ( )")) { if (container->getYield(tok3->strAt(1)) == Library::Container::Yield::SIZE) break; } else if (container->arrayLike_indexOp && Token::Match(tok3, "[ %varid% ]", numId)) stlOutOfBoundsError(tok3, tok3->strAt(1), containerName, false); else if (Token::Match(tok3, ". %name% ( %varid% )", numId)) { const Library::Container::Yield yield = container->getYield(tok3->strAt(1)); if (yield == Library::Container::Yield::AT_INDEX) stlOutOfBoundsError(tok3, tok3->strAt(3), containerName, true); } } } } } } void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at) { if (at) reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol.at(" + num + ") is out of bounds.", CWE788, false); else reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol[" + num + "] is out of bounds.", CWE788, false); } void CheckStl::negativeIndex() { // Negative index is out of bounds.. const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%var% [") || WRONG_DATA(!tok->next()->astOperand2(), tok)) continue; const Variable * const var = tok->variable(); if (!var || tok == var->nameToken()) continue; const Library::Container * const container = mSettings->library.detectContainer(var->typeStartToken()); if (!container || !container->arrayLike_indexOp) continue; const ValueFlow::Value *index = tok->next()->astOperand2()->getValueLE(-1, mSettings); if (!index) continue; negativeIndexError(tok, *index); } } } void CheckStl::negativeIndexError(const Token *tok, const ValueFlow::Value &index) { const ErrorPath errorPath = getErrorPath(tok, &index, "Negative array index"); std::ostringstream errmsg; if (index.condition) errmsg << ValueFlow::eitherTheConditionIsRedundant(index.condition) << ", otherwise there is negative array index " << index.intvalue << "."; else errmsg << "Array index " << index.intvalue << " is out of bounds."; reportError(errorPath, index.errorSeverity() ? Severity::error : Severity::warning, "negativeContainerIndex", errmsg.str(), CWE786, index.isInconclusive()); } void CheckStl::erase() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type == Scope::eFor && Token::simpleMatch(scope.classDef, "for (")) { const Token *tok = scope.classDef->linkAt(1); if (!Token::Match(tok->tokAt(-3), "; ++| %var% ++| ) {")) continue; tok = tok->previous(); if (!tok->isName()) tok = tok->previous(); eraseCheckLoopVar(scope, tok->variable()); } else if (scope.type == Scope::eWhile && Token::Match(scope.classDef, "while ( %var% !=")) { eraseCheckLoopVar(scope, scope.classDef->tokAt(2)->variable()); } } } void CheckStl::eraseCheckLoopVar(const Scope &scope, const Variable *var) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) return; for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { if (tok->str() != "(") continue; if (!Token::Match(tok->tokAt(-2), ". erase ( ++| %varid% )", var->declarationId())) continue; // Vector erases are handled by invalidContainer check if (isVector(tok->tokAt(-3))) continue; if (Token::simpleMatch(tok->astParent(), "=")) continue; // Iterator is invalid.. int indentlevel = 0U; const Token *tok2 = tok->link(); for (; tok2 != scope.bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") { ++indentlevel; continue; } if (tok2->str() == "}") { if (indentlevel > 0U) --indentlevel; else if (Token::simpleMatch(tok2, "} else {")) tok2 = tok2->linkAt(2); continue; } if (tok2->varId() == var->declarationId()) { if (Token::simpleMatch(tok2->next(), "=")) break; dereferenceErasedError(tok, tok2, tok2->str(), inconclusiveType); break; } if (indentlevel == 0U && Token::Match(tok2, "break|return|goto")) break; } if (tok2 == scope.bodyEnd) dereferenceErasedError(tok, scope.classDef, var->nameToken()->str(), inconclusiveType); } } void CheckStl::stlBoundaries() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->scope() || !var->scope()->isExecutable()) continue; const Library::Container* container = mSettings->library.detectContainer(var->typeStartToken(), true); if (!container || container->opLessAllowed) continue; const Token* const end = var->scope()->bodyEnd; for (const Token *tok = var->nameToken(); tok != end; tok = tok->next()) { if (Token::Match(tok, "!!* %varid% <", var->declarationId())) { stlBoundariesError(tok); } else if (Token::Match(tok, "> %varid% !!.", var->declarationId())) { stlBoundariesError(tok); } } } } // Error message for bad boundary usage.. void CheckStl::stlBoundariesError(const Token *tok) { reportError(tok, Severity::error, "stlBoundaries", "Dangerous comparison using operator< on iterator.\n" "Iterator compared with operator<. This is dangerous since the order of items in the " "container is not guaranteed. One should use operator!= instead to compare iterators.", CWE664, false); } static bool if_findCompare(const Token * const tokBack) { const Token *tok = tokBack->astParent(); if (!tok) return true; if (tok->isComparisonOp()) return (!tok->astOperand1()->isNumber() && !tok->astOperand2()->isNumber()); if (tok->isArithmeticalOp()) // result is used in some calculation return true; // TODO: check if there is a comparison of the result somewhere if (tok->str() == ".") return true; // Dereferencing is OK, the programmer might know that the element exists - TODO: An inconclusive warning might be appropriate if (tok->isAssignmentOp()) return if_findCompare(tok); // Go one step upwards in the AST return false; } void CheckStl::if_find() { const bool printWarning = mSettings->isEnabled(Settings::WARNING); const bool printPerformance = mSettings->isEnabled(Settings::PERFORMANCE); if (!printWarning && !printPerformance) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if ((scope.type != Scope::eIf && scope.type != Scope::eWhile) || !scope.classDef) continue; for (const Token *tok = scope.classDef->next(); tok->str() != "{"; tok = tok->next()) { const Token* funcTok = nullptr; const Library::Container* container = nullptr; if (Token::Match(tok, "%name% (")) tok = tok->linkAt(1); else if (tok->variable() && Token::Match(tok, "%var% . %name% (")) { container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); funcTok = tok->tokAt(2); } // check also for vector-like or pointer containers else if (tok->variable() && tok->astParent() && (tok->astParent()->str() == "*" || tok->astParent()->str() == "[")) { const Token *tok2 = tok->astParent(); if (!Token::Match(tok2->astParent(), ". %name% (")) continue; funcTok = tok2->astParent()->next(); if (tok->variable()->isArrayOrPointer()) container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); else { // Container of container - find the inner container container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); // outer container tok2 = Token::findsimplematch(tok->variable()->typeStartToken(), "<", tok->variable()->typeEndToken()); if (container && container->type_templateArgNo >= 0 && tok2) { tok2 = tok2->next(); for (int j = 0; j < container->type_templateArgNo; j++) tok2 = tok2->nextTemplateArgument(); container = mSettings->library.detectContainer(tok2); // innner container } else container = nullptr; } } if (container && container->getAction(funcTok->str()) == Library::Container::Action::FIND) { if (if_findCompare(funcTok->next())) continue; if (printWarning && container->getYield(funcTok->str()) == Library::Container::Yield::ITERATOR) if_findError(tok, false); else if (printPerformance && container->stdStringLike && funcTok->str() == "find") if_findError(tok, true); } else if (printWarning && Token::Match(tok, "std :: find|find_if (")) { // check that result is checked properly if (!if_findCompare(tok->tokAt(3))) { if_findError(tok, false); } } } } } void CheckStl::if_findError(const Token *tok, bool str) { if (str && mSettings->standards.cpp >= Standards::CPP20) reportError(tok, Severity::performance, "stlIfStrFind", "Inefficient usage of string::find() in condition; string::starts_with() could be faster.\n" "Either inefficient or wrong usage of string::find(). string::starts_with() will be faster if " "string::find's result is compared with 0, because it will not scan the whole " "string. If your intention is to check that there are no findings in the string, " "you should compare with std::string::npos.", CWE597, false); if (!str) reportError(tok, Severity::warning, "stlIfFind", "Suspicious condition. The result of find() is an iterator, but it is not properly checked.", CWE398, false); } static std::pair isMapFind(const Token *tok) { if (!Token::simpleMatch(tok, "(")) return {}; if (!Token::simpleMatch(tok->astOperand1(), ".")) return {}; if (!astIsContainer(tok->astOperand1()->astOperand1())) return {}; const Token * contTok = tok->astOperand1()->astOperand1(); const Library::Container * container = contTok->valueType()->container; if (!container) return {}; if (!container->stdAssociativeLike) return {}; if (!Token::Match(tok->astOperand1(), ". find|count (")) return {}; if (!tok->astOperand2()) return {}; return {contTok, tok->astOperand2()}; } static const Token *skipLocalVars(const Token *tok) { if (!tok) return tok; if (Token::simpleMatch(tok, "{")) return skipLocalVars(tok->next()); const Scope *scope = tok->scope(); const Token *top = tok->astTop(); if (!top) { const Token *semi = Token::findsimplematch(tok, ";"); if (!semi) return tok; if (!Token::Match(semi->previous(), "%var% ;")) return tok; const Token *varTok = semi->previous(); const Variable *var = varTok->variable(); if (!var) return tok; if (var->nameToken() != varTok) return tok; return skipLocalVars(semi->next()); } if (Token::Match(top, "%assign%")) { const Token *varTok = top->astOperand1(); if (!Token::Match(varTok, "%var%")) return tok; const Variable *var = varTok->variable(); if (!var) return tok; if (var->scope() != scope) return tok; const Token *endTok = nextAfterAstRightmostLeaf(top); if (!endTok) return tok; return skipLocalVars(endTok->next()); } return tok; } static const Token *findInsertValue(const Token *tok, const Token *containerTok, const Token *keyTok, const Library &library) { const Token *startTok = skipLocalVars(tok); const Token *top = startTok->astTop(); const Token *icontainerTok = nullptr; const Token *ikeyTok = nullptr; const Token *ivalueTok = nullptr; if (Token::simpleMatch(top, "=") && Token::simpleMatch(top->astOperand1(), "[")) { icontainerTok = top->astOperand1()->astOperand1(); ikeyTok = top->astOperand1()->astOperand2(); ivalueTok = top->astOperand2(); } if (Token::simpleMatch(top, "(") && Token::Match(top->astOperand1(), ". insert|emplace (") && !astIsIterator(top->astOperand1()->tokAt(2))) { icontainerTok = top->astOperand1()->astOperand1(); const Token *itok = top->astOperand1()->tokAt(2)->astOperand2(); if (Token::simpleMatch(itok, ",")) { ikeyTok = itok->astOperand1(); ivalueTok = itok->astOperand2(); } else { ikeyTok = itok; } } if (!ikeyTok || !icontainerTok) return nullptr; if (isSameExpression(true, true, containerTok, icontainerTok, library, true, false) && isSameExpression(true, true, keyTok, ikeyTok, library, true, true)) { if (ivalueTok) return ivalueTok; else return ikeyTok; } return nullptr; } void CheckStl::checkFindInsert() { if (!mSettings->isEnabled(Settings::PERFORMANCE)) return; const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::simpleMatch(tok, "if (")) continue; if (!Token::simpleMatch(tok->next()->link(), ") {")) continue; if (!Token::Match(tok->next()->astOperand2(), "%comp%")) continue; const Token *condTok = tok->next()->astOperand2(); const Token *containerTok; const Token *keyTok; std::tie(containerTok, keyTok) = isMapFind(condTok->astOperand1()); if (!containerTok) continue; const Token *thenTok = tok->next()->link()->next(); const Token *valueTok = findInsertValue(thenTok, containerTok, keyTok, mSettings->library); if (!valueTok) continue; if (Token::simpleMatch(thenTok->link(), "} else {")) { const Token *valueTok2 = findInsertValue(thenTok->link()->tokAt(2), containerTok, keyTok, mSettings->library); if (!valueTok2) continue; if (isSameExpression(true, true, valueTok, valueTok2, mSettings->library, true, true)) { checkFindInsertError(valueTok); } } else { checkFindInsertError(valueTok); } } } } void CheckStl::checkFindInsertError(const Token *tok) { reportError( tok, Severity::performance, "stlFindInsert", "Searching before insertion is not necessary.", CWE398, false); } /** * Is container.size() slow? */ static bool isCpp03ContainerSizeSlow(const Token *tok) { if (!tok) return false; const Variable* var = tok->variable(); return var && var->isStlType("list"); } void CheckStl::size() { if (!mSettings->isEnabled(Settings::PERFORMANCE)) return; if (mSettings->standards.cpp >= Standards::CPP11) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%var% . size ( )") || Token::Match(tok, "%name% . %var% . size ( )")) { // get the variable const Token *varTok = tok; if (tok->strAt(2) != "size") varTok = varTok->tokAt(2); const Token* const end = varTok->tokAt(5); // check for comparison to zero if ((tok->previous() && !tok->previous()->isArithmeticalOp() && Token::Match(end, "==|<=|!=|> 0")) || (end->next() && !end->next()->isArithmeticalOp() && Token::Match(tok->tokAt(-2), "0 ==|>=|!=|<"))) { if (isCpp03ContainerSizeSlow(varTok)) { sizeError(varTok); continue; } } // check for comparison to one if ((tok->previous() && !tok->previous()->isArithmeticalOp() && Token::Match(end, ">=|< 1") && !end->tokAt(2)->isArithmeticalOp()) || (end->next() && !end->next()->isArithmeticalOp() && Token::Match(tok->tokAt(-2), "1 <=|>") && !tok->tokAt(-3)->isArithmeticalOp())) { if (isCpp03ContainerSizeSlow(varTok)) sizeError(varTok); } // check for using as boolean expression else if ((Token::Match(tok->tokAt(-2), "if|while (") && end->str() == ")") || (tok->previous()->tokType() == Token::eLogicalOp && Token::Match(end, "&&|)|,|;|%oror%"))) { if (isCpp03ContainerSizeSlow(varTok)) sizeError(varTok); } } } } } void CheckStl::sizeError(const Token *tok) { const std::string varname = tok ? tok->str() : std::string("list"); reportError(tok, Severity::performance, "stlSize", "$symbol:" + varname + "\n" "Possible inefficient checking for '$symbol' emptiness.\n" "Checking for '$symbol' emptiness might be inefficient. " "Using $symbol.empty() instead of $symbol.size() can be faster. " "$symbol.size() can take linear time but $symbol.empty() is " "guaranteed to take constant time.", CWE398, false); } void CheckStl::redundantCondition() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eIf) continue; const Token* tok = scope.classDef->tokAt(2); if (!Token::Match(tok, "%name% . find ( %any% ) != %name% . end|rend|cend|crend ( ) ) { %name% . remove|erase ( %any% ) ;")) continue; // Get tokens for the fields %name% and %any% const Token *var1 = tok; const Token *any1 = var1->tokAt(4); const Token *var2 = any1->tokAt(3); const Token *var3 = var2->tokAt(7); const Token *any2 = var3->tokAt(4); // Check if all the "%name%" fields are the same and if all the "%any%" are the same.. if (var1->str() == var2->str() && var2->str() == var3->str() && any1->str() == any2->str()) { redundantIfRemoveError(tok); } } } void CheckStl::redundantIfRemoveError(const Token *tok) { reportError(tok, Severity::style, "redundantIfRemove", "Redundant checking of STL container element existence before removing it.\n" "Redundant checking of STL container element existence before removing it. " "It is safe to call the remove method on a non-existing element.", CWE398, false); } void CheckStl::missingComparison() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eFor || !scope.classDef) continue; for (const Token *tok2 = scope.classDef->tokAt(2); tok2 != scope.bodyStart; tok2 = tok2->next()) { if (tok2->str() == ";") break; if (!Token::Match(tok2, "%var% = %name% . begin|rbegin|cbegin|crbegin ( ) ; %name% != %name% . end|rend|cend|crend ( ) ; ++| %name% ++| ) {")) continue; // same container if (tok2->strAt(2) != tok2->strAt(10)) break; const int iteratorId(tok2->varId()); // same iterator if (iteratorId == tok2->tokAt(10)->varId()) break; // increment iterator if (!Token::Match(tok2->tokAt(16), "++ %varid% )", iteratorId) && !Token::Match(tok2->tokAt(16), "%varid% ++ )", iteratorId)) { break; } const Token *incrementToken = nullptr; // Parse loop.. for (const Token *tok3 = scope.bodyStart; tok3 != scope.bodyEnd; tok3 = tok3->next()) { if (Token::Match(tok3, "%varid% ++", iteratorId)) incrementToken = tok3; else if (Token::Match(tok3->previous(), "++ %varid% !!.", iteratorId)) incrementToken = tok3; else if (Token::Match(tok3, "%varid% !=|==", iteratorId)) incrementToken = nullptr; else if (tok3->str() == "break" || tok3->str() == "return") incrementToken = nullptr; else if (Token::Match(tok3, "%varid% = %name% . insert ( ++| %varid% ++| ,", iteratorId)) { // skip insertion.. tok3 = tok3->linkAt(6); if (!tok3) break; } } if (incrementToken) missingComparisonError(incrementToken, tok2->tokAt(16)); } } } void CheckStl::missingComparisonError(const Token *incrementToken1, const Token *incrementToken2) { std::list callstack = { incrementToken1,incrementToken2 }; std::ostringstream errmsg; errmsg << "Missing bounds check for extra iterator increment in loop.\n" << "The iterator incrementing is suspicious - it is incremented at line "; if (incrementToken1) errmsg << incrementToken1->linenr(); errmsg << " and then at line "; if (incrementToken2) errmsg << incrementToken2->linenr(); errmsg << ". The loop might unintentionally skip an element in the container. " << "There is no comparison between these increments to prevent that the iterator is " << "incremented beyond the end."; reportError(callstack, Severity::warning, "StlMissingComparison", errmsg.str(), CWE834, false); } static bool isLocal(const Token *tok) { const Variable *var = tok->variable(); return var && !var->isStatic() && var->isLocal(); } namespace { const std::set stl_string_stream = { "istringstream", "ostringstream", "stringstream", "wstringstream" }; } void CheckStl::string_c_str() { const bool printInconclusive = mSettings->inconclusive; const bool printPerformance = mSettings->isEnabled(Settings::PERFORMANCE); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); // Find all functions that take std::string as argument std::multimap c_strFuncParam; if (printPerformance) { for (const Scope &scope : symbolDatabase->scopeList) { for (const Function &func : scope.functionList) { if (c_strFuncParam.erase(func.tokenDef->str()) != 0) { // Check if function with this name was already found c_strFuncParam.insert(std::make_pair(func.tokenDef->str(), 0)); // Disable, because there are overloads. TODO: Handle overloads continue; } int numpar = 0; c_strFuncParam.insert(std::make_pair(func.tokenDef->str(), numpar)); // Insert function as dummy, to indicate that there is at least one function with that name for (const Variable &var : func.argumentList) { numpar++; if (var.isStlStringType() && (!var.isReference() || var.isConst())) c_strFuncParam.insert(std::make_pair(func.tokenDef->str(), numpar)); } } } } // Try to detect common problems when using string::c_str() for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eFunction || !scope.function) continue; enum {charPtr, stdString, stdStringConstRef, Other} returnType = Other; if (Token::Match(scope.function->tokenDef->tokAt(-2), "char|wchar_t *")) returnType = charPtr; else if (Token::Match(scope.function->tokenDef->tokAt(-5), "const std :: string|wstring &")) returnType = stdStringConstRef; else if (Token::Match(scope.function->tokenDef->tokAt(-3), "std :: string|wstring !!&")) returnType = stdString; for (const Token *tok = scope.bodyStart; tok && tok != scope.bodyEnd; tok = tok->next()) { // Invalid usage.. if (Token::Match(tok, "throw %var% . c_str|data ( ) ;") && isLocal(tok->next()) && tok->next()->variable() && tok->next()->variable()->isStlStringType()) { string_c_strThrowError(tok); } else if (Token::Match(tok, "[;{}] %name% = %var% . str ( ) . c_str|data ( ) ;")) { const Variable* var = tok->next()->variable(); const Variable* var2 = tok->tokAt(3)->variable(); if (var && var->isPointer() && var2 && var2->isStlType(stl_string_stream)) string_c_strError(tok); } else if (Token::Match(tok, "[;{}] %var% = %name% (") && Token::Match(tok->linkAt(4), ") . c_str|data ( ) ;") && tok->tokAt(3)->function() && Token::Match(tok->tokAt(3)->function()->retDef, "std :: string|wstring %name%")) { const Variable* var = tok->next()->variable(); if (var && var->isPointer()) string_c_strError(tok); } else if (printPerformance && Token::Match(tok, "%name% ( !!)") && c_strFuncParam.find(tok->str()) != c_strFuncParam.end() && !Token::Match(tok->previous(), "::|.") && tok->varId() == 0 && tok->str() != scope.className) { // calling function. TODO: Add support for member functions const std::pair::const_iterator, std::multimap::const_iterator> range = c_strFuncParam.equal_range(tok->str()); for (std::multimap::const_iterator i = range.first; i != range.second; ++i) { if (i->second == 0) continue; const Token* tok2 = tok->tokAt(2); int j; for (j = 0; tok2 && j < i->second-1; j++) tok2 = tok2->nextArgument(); if (tok2) tok2 = tok2->nextArgument(); else break; if (!tok2 && j == i->second-1) tok2 = tok->next()->link(); else if (tok2) tok2 = tok2->previous(); else break; if (tok2 && Token::Match(tok2->tokAt(-4), ". c_str|data ( )")) { const Variable* var = tok2->tokAt(-5)->variable(); if (var && var->isStlStringType()) { string_c_strParam(tok, i->second); } else if (Token::Match(tok2->tokAt(-9), "%name% . str ( )")) { // Check ss.str().c_str() as parameter const Variable* ssVar = tok2->tokAt(-9)->variable(); if (ssVar && ssVar->isStlType(stl_string_stream)) string_c_strParam(tok, i->second); } } } } // Using c_str() to get the return value is only dangerous if the function returns a char* if ((returnType == charPtr || (printPerformance && (returnType == stdString || returnType == stdStringConstRef))) && tok->str() == "return") { bool err = false; const Token* tok2 = tok->next(); if (Token::Match(tok2, "std :: string|wstring (") && Token::Match(tok2->linkAt(3), ") . c_str|data ( ) ;")) { err = true; } else if (Token::simpleMatch(tok2, "(") && Token::Match(tok2->link(), ") . c_str|data ( ) ;")) { // Check for "+ localvar" or "+ std::string(" inside the bracket bool is_implicit_std_string = printInconclusive; const Token *search_end = tok2->link(); for (const Token *search_tok = tok2->next(); search_tok != search_end; search_tok = search_tok->next()) { if (Token::Match(search_tok, "+ %var%") && isLocal(search_tok->next()) && search_tok->next()->variable() && search_tok->next()->variable()->isStlStringType()) { is_implicit_std_string = true; break; } else if (Token::Match(search_tok, "+ std :: string|wstring (")) { is_implicit_std_string = true; break; } } if (is_implicit_std_string) err = true; } bool local = false; bool ptrOrRef = false; const Variable* lastVar = nullptr; const Function* lastFunc = nullptr; bool funcStr = false; if (Token::Match(tok2, "%var% .")) { local = isLocal(tok2); bool refToNonLocal = false; if (tok2->variable() && tok2->variable()->isReference()) { const Token *refTok = tok2->variable()->nameToken(); refToNonLocal = true; // safe assumption is default to avoid FPs if (Token::Match(refTok, "%var% = %var% .|;|[")) refToNonLocal = !isLocal(refTok->tokAt(2)); } ptrOrRef = refToNonLocal || (tok2->variable() && tok2->variable()->isPointer()); } while (tok2) { if (Token::Match(tok2, "%var% .|::")) { if (ptrOrRef) local = false; lastVar = tok2->variable(); tok2 = tok2->tokAt(2); } else if (Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") .")) { lastFunc = tok2->function(); local = false; funcStr = tok2->str() == "str"; tok2 = tok2->linkAt(1)->tokAt(2); } else break; } if (Token::Match(tok2, "c_str|data ( ) ;")) { if ((local || returnType != charPtr) && lastVar && lastVar->isStlStringType()) err = true; else if (funcStr && lastVar && lastVar->isStlType(stl_string_stream)) err = true; else if (lastFunc && Token::Match(lastFunc->tokenDef->tokAt(-3), "std :: string|wstring")) err = true; } if (err) { if (returnType == charPtr) string_c_strError(tok); else string_c_strReturn(tok); } } } } } void CheckStl::string_c_strThrowError(const Token* tok) { reportError(tok, Severity::error, "stlcstrthrow", "Dangerous usage of c_str(). The value returned by c_str() is invalid after throwing exception.\n" "Dangerous usage of c_str(). The string is destroyed after the c_str() call so the thrown pointer is invalid."); } void CheckStl::string_c_strError(const Token* tok) { reportError(tok, Severity::error, "stlcstr", "Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n" "Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted.", CWE664, false); } void CheckStl::string_c_strReturn(const Token* tok) { reportError(tok, Severity::performance, "stlcstrReturn", "Returning the result of c_str() in a function that returns std::string is slow and redundant.\n" "The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly returning the string.", CWE704, false); } void CheckStl::string_c_strParam(const Token* tok, nonneg int number) { std::ostringstream oss; oss << "Passing the result of c_str() to a function that takes std::string as argument no. " << number << " is slow and redundant.\n" "The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly passing the string."; reportError(tok, Severity::performance, "stlcstrParam", oss.str(), CWE704, false); } //--------------------------------------------------------------------------- // //--------------------------------------------------------------------------- namespace { const std::set stl_containers_with_empty_and_clear = { "deque", "forward_list", "list", "map", "multimap", "multiset", "set", "string", "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector", "wstring" }; } void CheckStl::uselessCalls() { const bool printPerformance = mSettings->isEnabled(Settings::PERFORMANCE); const bool printWarning = mSettings->isEnabled(Settings::WARNING); if (!printPerformance && !printWarning) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (printWarning && Token::Match(tok, "%var% . compare|find|rfind|find_first_not_of|find_first_of|find_last_not_of|find_last_of ( %name% [,)]") && tok->varId() == tok->tokAt(4)->varId()) { const Variable* var = tok->variable(); if (!var || !var->isStlType()) continue; uselessCallsReturnValueError(tok->tokAt(4), tok->str(), tok->strAt(2)); } else if (printPerformance && Token::Match(tok, "%var% . swap ( %name% )") && tok->varId() == tok->tokAt(4)->varId()) { const Variable* var = tok->variable(); if (!var || !var->isStlType()) continue; uselessCallsSwapError(tok, tok->str()); } else if (printPerformance && Token::Match(tok, "%var% . substr (") && tok->variable() && tok->variable()->isStlStringType()) { if (Token::Match(tok->tokAt(4), "0| )")) { uselessCallsSubstrError(tok, false); } else if (tok->strAt(4) == "0" && tok->linkAt(3)->strAt(-1) == "npos") { if (!tok->linkAt(3)->previous()->variable()) // Make sure that its no variable uselessCallsSubstrError(tok, false); } else if (Token::simpleMatch(tok->linkAt(3)->tokAt(-2), ", 0 )")) uselessCallsSubstrError(tok, true); } else if (printWarning && Token::Match(tok, "[{};] %var% . empty ( ) ;") && !tok->tokAt(4)->astParent() && tok->next()->variable() && tok->next()->variable()->isStlType(stl_containers_with_empty_and_clear)) uselessCallsEmptyError(tok->next()); else if (Token::Match(tok, "[{};] std :: remove|remove_if|unique (") && tok->tokAt(5)->nextArgument()) uselessCallsRemoveError(tok->next(), tok->strAt(3)); } } } void CheckStl::uselessCallsReturnValueError(const Token *tok, const std::string &varname, const std::string &function) { std::ostringstream errmsg; errmsg << "$symbol:" << varname << '\n'; errmsg << "$symbol:" << function << '\n'; errmsg << "It is inefficient to call '" << varname << "." << function << "(" << varname << ")' as it always returns 0.\n" << "'std::string::" << function << "()' returns zero when given itself as parameter " << "(" << varname << "." << function << "(" << varname << ")). As it is currently the " << "code is inefficient. It is possible either the string searched ('" << varname << "') or searched for ('" << varname << "') is wrong."; reportError(tok, Severity::warning, "uselessCallsCompare", errmsg.str(), CWE628, false); } void CheckStl::uselessCallsSwapError(const Token *tok, const std::string &varname) { reportError(tok, Severity::performance, "uselessCallsSwap", "$symbol:" + varname + "\n" "It is inefficient to swap a object with itself by calling '$symbol.swap($symbol)'\n" "The 'swap()' function has no logical effect when given itself as parameter " "($symbol.swap($symbol)). As it is currently the " "code is inefficient. Is the object or the parameter wrong here?", CWE628, false); } void CheckStl::uselessCallsSubstrError(const Token *tok, bool empty) { if (empty) reportError(tok, Severity::performance, "uselessCallsSubstr", "Ineffective call of function 'substr' because it returns an empty string.", CWE398, false); else reportError(tok, Severity::performance, "uselessCallsSubstr", "Ineffective call of function 'substr' because it returns a copy of the object. Use operator= instead.", CWE398, false); } void CheckStl::uselessCallsEmptyError(const Token *tok) { reportError(tok, Severity::warning, "uselessCallsEmpty", "Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead?", CWE398, false); } void CheckStl::uselessCallsRemoveError(const Token *tok, const std::string& function) { reportError(tok, Severity::warning, "uselessCallsRemove", "$symbol:" + function + "\n" "Return value of std::$symbol() ignored. Elements remain in container.\n" "The return value of std::$symbol() is ignored. This function returns an iterator to the end of the range containing those elements that should be kept. " "Elements past new end remain valid but with unspecified values. Use the erase method of the container to delete them.", CWE762, false); } // Check for iterators being dereferenced before being checked for validity. // E.g. if (*i && i != str.end()) { } void CheckStl::checkDereferenceInvalidIterator() { if (!mSettings->isEnabled(Settings::WARNING)) return; // Iterate over "if", "while", and "for" conditions where there may // be an iterator that is dereferenced before being checked for validity. for (const Scope &scope : mTokenizer->getSymbolDatabase()->scopeList) { if (!(scope.type == Scope::eIf || scope.type == Scope::eDo || scope.type == Scope::eWhile || scope.type == Scope::eFor)) continue; const Token* const tok = scope.classDef; const Token* startOfCondition = tok->next(); if (scope.type == Scope::eDo) startOfCondition = startOfCondition->link()->tokAt(2); if (!startOfCondition) // ticket #6626 invalid code continue; const Token* endOfCondition = startOfCondition->link(); if (!endOfCondition) continue; // For "for" loops, only search between the two semicolons if (scope.type == Scope::eFor) { startOfCondition = Token::findsimplematch(tok->tokAt(2), ";", endOfCondition); if (!startOfCondition) continue; endOfCondition = Token::findsimplematch(startOfCondition->next(), ";", endOfCondition); if (!endOfCondition) continue; } // Only consider conditions composed of all "&&" terms and // conditions composed of all "||" terms const bool isOrExpression = Token::findsimplematch(startOfCondition, "||", endOfCondition) != nullptr; const bool isAndExpression = Token::findsimplematch(startOfCondition, "&&", endOfCondition) != nullptr; // Look for a check of the validity of an iterator const Token* validityCheckTok = nullptr; if (!isOrExpression && isAndExpression) { validityCheckTok = Token::findmatch(startOfCondition, "&& %var% != %name% . end|rend|cend|crend ( )", endOfCondition); } else if (isOrExpression && !isAndExpression) { validityCheckTok = Token::findmatch(startOfCondition, "%oror% %var% == %name% . end|rend|cend|crend ( )", endOfCondition); } if (!validityCheckTok) continue; const int iteratorVarId = validityCheckTok->next()->varId(); // If the iterator dereference is to the left of the check for // the iterator's validity, report an error. const Token* const dereferenceTok = Token::findmatch(startOfCondition, "* %varid%", validityCheckTok, iteratorVarId); if (dereferenceTok) dereferenceInvalidIteratorError(dereferenceTok, dereferenceTok->strAt(1)); } } void CheckStl::dereferenceInvalidIteratorError(const Token* deref, const std::string &iterName) { reportError(deref, Severity::warning, "derefInvalidIterator", "$symbol:" + iterName + "\n" "Possible dereference of an invalid iterator: $symbol\n" "Possible dereference of an invalid iterator: $symbol. Make sure to check that the iterator is valid before dereferencing it - not after.", CWE825, false); } void CheckStl::readingEmptyStlContainer2() { for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { if (!tok->isName() || !tok->valueType()) continue; const Library::Container *container = tok->valueType()->container; if (!container) continue; const ValueFlow::Value *value = tok->getContainerSizeValue(0); if (!value) continue; if (value->isInconclusive() && !mSettings->inconclusive) continue; if (!value->errorSeverity() && !mSettings->isEnabled(Settings::WARNING)) continue; if (Token::Match(tok, "%name% . %name% (")) { if (container->getYield(tok->strAt(2)) == Library::Container::Yield::ITEM) readingEmptyStlContainerError(tok,value); } } } } void CheckStl::readingEmptyStlContainerError(const Token *tok, const ValueFlow::Value *value) { const std::string varname = tok ? tok->str() : std::string("var"); std::string errmsg; if (value && value->condition) errmsg = "Reading from container '$symbol'. " + ValueFlow::eitherTheConditionIsRedundant(value->condition) + " or '$symbol' can be empty."; else errmsg = "Reading from empty STL container '$symbol'"; const ErrorPath errorPath = getErrorPath(tok, value, "Reading from empty container"); reportError(errorPath, value ? (value->errorSeverity() ? Severity::error : Severity::warning) : Severity::style, "reademptycontainer", "$symbol:" + varname +"\n" + errmsg, CWE398, !value); } void CheckStl::useStlAlgorithmError(const Token *tok, const std::string &algoName) { reportError(tok, Severity::style, "useStlAlgorithm", "Consider using " + algoName + " algorithm instead of a raw loop.", CWE398, false); } static bool isEarlyExit(const Token *start) { if (start->str() != "{") return false; const Token *endToken = start->link(); const Token *tok = Token::findmatch(start, "return|throw|break", endToken); if (!tok) return false; const Token *endStatement = Token::findsimplematch(tok, "; }", endToken); if (!endStatement) return false; if (endStatement->next() != endToken) return false; return true; } static const Token *singleStatement(const Token *start) { if (start->str() != "{") return nullptr; const Token *endToken = start->link(); const Token *endStatement = Token::findsimplematch(start->next(), ";"); if (!Token::simpleMatch(endStatement, "; }")) return nullptr; if (endStatement->next() != endToken) return nullptr; return endStatement; } static const Token *singleAssignInScope(const Token *start, nonneg int varid, bool &input) { const Token *endStatement = singleStatement(start); if (!endStatement) return nullptr; if (!Token::Match(start->next(), "%var% %assign%")) return nullptr; const Token *assignTok = start->tokAt(2); if (isVariableChanged(assignTok->next(), endStatement, assignTok->astOperand1()->varId(), false, nullptr, true)) return nullptr; if (isVariableChanged(assignTok->next(), endStatement, varid, false, nullptr, true)) return nullptr; input = Token::findmatch(assignTok->next(), "%varid%", endStatement, varid) || !Token::Match(start->next(), "%var% ="); return assignTok; } static const Token *singleMemberCallInScope(const Token *start, nonneg int varid, bool &input) { if (start->str() != "{") return nullptr; const Token *endToken = start->link(); if (!Token::Match(start->next(), "%var% . %name% (")) return nullptr; if (!Token::simpleMatch(start->linkAt(4), ") ; }")) return nullptr; const Token *endStatement = start->linkAt(4)->next(); if (endStatement->next() != endToken) return nullptr; const Token *dotTok = start->tokAt(2); if (!Token::findmatch(dotTok->tokAt(2), "%varid%", endStatement, varid)) return nullptr; input = Token::Match(start->next(), "%var% . %name% ( %varid% )", varid); if (isVariableChanged(dotTok->next(), endStatement, dotTok->astOperand1()->varId(), false, nullptr, true)) return nullptr; return dotTok; } static const Token *singleIncrementInScope(const Token *start, nonneg int varid, bool &input) { if (start->str() != "{") return nullptr; const Token *varTok = nullptr; if (Token::Match(start->next(), "++ %var% ; }")) varTok = start->tokAt(2); else if (Token::Match(start->next(), "%var% ++ ; }")) varTok = start->tokAt(1); if (!varTok) return nullptr; input = varTok->varId() == varid; return varTok; } static const Token *singleConditionalInScope(const Token *start, nonneg int varid) { if (start->str() != "{") return nullptr; const Token *endToken = start->link(); if (!Token::simpleMatch(start->next(), "if (")) return nullptr; if (!Token::simpleMatch(start->linkAt(2), ") {")) return nullptr; const Token *bodyTok = start->linkAt(2)->next(); const Token *endBodyTok = bodyTok->link(); if (!Token::simpleMatch(endBodyTok, "} }")) return nullptr; if (endBodyTok->next() != endToken) return nullptr; if (!Token::findmatch(start, "%varid%", bodyTok, varid)) return nullptr; if (isVariableChanged(start, bodyTok, varid, false, nullptr, true)) return nullptr; return bodyTok; } static bool addByOne(const Token *tok, nonneg int varid) { if (Token::Match(tok, "+= %any% ;") && tok->tokAt(1)->hasKnownIntValue() && tok->tokAt(1)->getValue(1)) { return true; } if (Token::Match(tok, "= %varid% + %any% ;", varid) && tok->tokAt(3)->hasKnownIntValue() && tok->tokAt(3)->getValue(1)) { return true; } return false; } static bool accumulateBoolLiteral(const Token *tok, nonneg int varid) { if (Token::Match(tok, "%assign% %bool% ;") && tok->tokAt(1)->hasKnownIntValue()) { return true; } if (Token::Match(tok, "= %varid% %oror%|%or%|&&|& %bool% ;", varid) && tok->tokAt(3)->hasKnownIntValue()) { return true; } return false; } static bool accumulateBool(const Token *tok, nonneg int varid) { // Missing %oreq% so we have to check both manually if (Token::simpleMatch(tok, "&=") || Token::simpleMatch(tok, "|=")) { return true; } if (Token::Match(tok, "= %varid% %oror%|%or%|&&|&", varid)) { return true; } return false; } static bool hasVarIds(const Token *tok, nonneg int var1, nonneg int var2) { if (tok->astOperand1()->varId() == tok->astOperand2()->varId()) return false; if (tok->astOperand1()->varId() == var1 || tok->astOperand1()->varId() == var2) { if (tok->astOperand2()->varId() == var1 || tok->astOperand2()->varId() == var2) { return true; } } return false; } static std::string flipMinMax(const std::string &algo) { if (algo == "std::max_element") return "std::min_element"; if (algo == "std::min_element") return "std::max_element"; return algo; } static std::string minmaxCompare(const Token *condTok, nonneg int loopVar, nonneg int assignVar, bool invert = false) { if (!Token::Match(condTok, "<|<=|>=|>")) return "std::accumulate"; if (!hasVarIds(condTok, loopVar, assignVar)) return "std::accumulate"; std::string algo = "std::max_element"; if (Token::Match(condTok, "<|<=")) algo = "std::min_element"; if (condTok->astOperand1()->varId() == assignVar) algo = flipMinMax(algo); if (invert) algo = flipMinMax(algo); return algo; } void CheckStl::useStlAlgorithm() { if (!mSettings->isEnabled(Settings::STYLE)) return; for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { // Parse range-based for loop if (!Token::simpleMatch(tok, "for (")) continue; if (!Token::simpleMatch(tok->next()->link(), ") {")) continue; const Token *bodyTok = tok->next()->link()->next(); const Token *splitTok = tok->next()->astOperand2(); if (!Token::simpleMatch(splitTok, ":")) continue; const Token *loopVar = splitTok->previous(); if (!Token::Match(loopVar, "%var%")) continue; // Check for single assignment bool useLoopVarInAssign; const Token *assignTok = singleAssignInScope(bodyTok, loopVar->varId(), useLoopVarInAssign); if (assignTok) { int assignVarId = assignTok->astOperand1()->varId(); std::string algo; if (assignVarId == loopVar->varId()) { if (useLoopVarInAssign) algo = "std::transform"; else if (Token::Match(assignTok->next(), "%var%|%bool%|%num%|%char% ;")) algo = "std::fill"; else if (Token::Match(assignTok->next(), "%name% ( )")) algo = "std::generate"; else algo = "std::fill or std::generate"; } else { if (addByOne(assignTok, assignVarId)) algo = "std::distance"; else if (accumulateBool(assignTok, assignVarId)) algo = "std::any_of, std::all_of, std::none_of, or std::accumulate"; else if (Token::Match(assignTok, "= %var% <|<=|>=|> %var% ? %var% : %var%") && hasVarIds(assignTok->tokAt(6), loopVar->varId(), assignVarId)) algo = minmaxCompare(assignTok->tokAt(2), loopVar->varId(), assignVarId, assignTok->tokAt(5)->varId() == assignVarId); else algo = "std::accumulate"; } useStlAlgorithmError(assignTok, algo); continue; } // Check for container calls bool useLoopVarInMemCall; const Token *memberAccessTok = singleMemberCallInScope(bodyTok, loopVar->varId(), useLoopVarInMemCall); if (memberAccessTok) { const Token *memberCallTok = memberAccessTok->astOperand2(); const int contVarId = memberAccessTok->astOperand1()->varId(); if (contVarId == loopVar->varId()) continue; if (memberCallTok->str() == "push_back" || memberCallTok->str() == "push_front" || memberCallTok->str() == "emplace_back") { std::string algo; if (useLoopVarInMemCall) algo = "std::copy"; else algo = "std::transform"; useStlAlgorithmError(memberCallTok, algo); } continue; } // Check for increment in loop bool useLoopVarInIncrement; const Token *incrementTok = singleIncrementInScope(bodyTok, loopVar->varId(), useLoopVarInIncrement); if (incrementTok) { std::string algo; if (useLoopVarInIncrement) algo = "std::transform"; else algo = "std::distance"; useStlAlgorithmError(incrementTok, algo); continue; } // Check for conditionals const Token *condBodyTok = singleConditionalInScope(bodyTok, loopVar->varId()); if (condBodyTok) { // Check for single assign assignTok = singleAssignInScope(condBodyTok, loopVar->varId(), useLoopVarInAssign); if (assignTok) { const int assignVarId = assignTok->astOperand1()->varId(); std::string algo; if (assignVarId == loopVar->varId()) { if (useLoopVarInAssign) algo = "std::transform"; else algo = "std::replace_if"; } else { if (addByOne(assignTok, assignVarId)) algo = "std::count_if"; else if (accumulateBoolLiteral(assignTok, assignVarId)) algo = "std::any_of, std::all_of, std::none_of, or std::accumulate"; else algo = "std::accumulate"; } useStlAlgorithmError(assignTok, algo); continue; } // Check for container call memberAccessTok = singleMemberCallInScope(condBodyTok, loopVar->varId(), useLoopVarInMemCall); if (memberAccessTok) { const Token *memberCallTok = memberAccessTok->astOperand2(); const int contVarId = memberAccessTok->astOperand1()->varId(); if (contVarId == loopVar->varId()) continue; if (memberCallTok->str() == "push_back" || memberCallTok->str() == "push_front" || memberCallTok->str() == "emplace_back") { if (useLoopVarInMemCall) useStlAlgorithmError(memberAccessTok, "std::copy_if"); // There is no transform_if to suggest } continue; } // Check for increment in loop incrementTok = singleIncrementInScope(condBodyTok, loopVar->varId(), useLoopVarInIncrement); if (incrementTok) { std::string algo; if (useLoopVarInIncrement) algo = "std::transform"; else algo = "std::count_if"; useStlAlgorithmError(incrementTok, algo); continue; } // Check early return if (isEarlyExit(condBodyTok)) { const Token *loopVar2 = Token::findmatch(condBodyTok, "%varid%", condBodyTok->link(), loopVar->varId()); std::string algo; if (loopVar2) algo = "std::find_if"; else algo = "std::any_of"; useStlAlgorithmError(condBodyTok, algo); continue; } } } } } cppcheck-1.90/lib/checkstl.h000066400000000000000000000264111357737443600157710ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkstlH #define checkstlH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "library.h" #include "tokenize.h" #include #include class ErrorLogger; class Scope; class Settings; class Token; class Variable; /// @addtogroup Checks /// @{ /** @brief %Check STL usage (invalidation of iterators, mismatching containers, etc) */ class CPPCHECKLIB CheckStl : public Check { public: /** This constructor is used when registering the CheckClass */ CheckStl() : Check(myName()) { } /** This constructor is used when running checks. */ CheckStl(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** run checks, the token list is not simplified */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { if (!tokenizer->isCPP()) { return; } CheckStl checkStl(tokenizer, settings, errorLogger); checkStl.erase(); checkStl.if_find(); checkStl.checkFindInsert(); checkStl.iterators(); checkStl.mismatchingContainers(); checkStl.missingComparison(); checkStl.outOfBounds(); checkStl.outOfBoundsIndexExpression(); checkStl.redundantCondition(); checkStl.string_c_str(); checkStl.uselessCalls(); checkStl.useStlAlgorithm(); checkStl.stlOutOfBounds(); checkStl.negativeIndex(); checkStl.invalidContainer(); checkStl.mismatchingContainers(); checkStl.stlBoundaries(); checkStl.checkDereferenceInvalidIterator(); // Style check checkStl.size(); } /** Accessing container out of bounds using ValueFlow */ void outOfBounds(); /** Accessing container out of bounds, following index expression */ void outOfBoundsIndexExpression(); /** * Finds errors like this: * for (unsigned ii = 0; ii <= foo.size(); ++ii) */ void stlOutOfBounds(); /** * negative index for array like containers */ void negativeIndex(); /** * Finds errors like this: * for (it = foo.begin(); it != bar.end(); ++it) */ void iterators(); void invalidContainer(); /** * Mismatching containers: * std::find(foo.begin(), bar.end(), x) */ void mismatchingContainers(); /** * Dangerous usage of erase. The iterator is invalidated by erase so * it is bad to dereference it after the erase. */ void erase(); void eraseCheckLoopVar(const Scope& scope, const Variable* var); /** * bad condition.. "it < alist.end()" */ void stlBoundaries(); /** if (a.find(x)) - possibly incorrect condition */ void if_find(); void checkFindInsert(); /** * Suggest using empty() instead of checking size() against zero for containers. * Item 4 from Scott Meyers book "Effective STL". */ void size(); /** * Check for redundant condition 'if (ints.find(1) != ints.end()) ints.remove(123);' * */ void redundantCondition(); /** * @brief Missing inner comparison, when incrementing iterator inside loop * Dangers: * - may increment iterator beyond end * - may unintentionally skip elements in list/set etc */ void missingComparison(); /** Check for common mistakes when using the function string::c_str() */ void string_c_str(); /** @brief %Check calls that using them is useless */ void uselessCalls(); /** @brief %Check for dereferencing an iterator that is invalid */ void checkDereferenceInvalidIterator(); /** * Dereferencing an erased iterator * @param erased token where the erase occurs * @param deref token where the dereference occurs * @param itername iterator name * @param inconclusive inconclusive flag */ void dereferenceErasedError(const Token* erased, const Token* deref, const std::string& itername, bool inconclusive); /** @brief Reading from empty stl container (using valueflow) */ void readingEmptyStlContainer2(); /** @brief Look for loops that can replaced with std algorithms */ void useStlAlgorithm(); private: bool isContainerSize(const Token *containerToken, const Token *expr) const; bool isContainerSizeGE(const Token * containerToken, const Token *expr) const; void missingComparisonError(const Token* incrementToken1, const Token* incrementToken2); void string_c_strThrowError(const Token* tok); void string_c_strError(const Token* tok); void string_c_strReturn(const Token* tok); void string_c_strParam(const Token* tok, nonneg int number); void outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue); void outOfBoundsIndexExpressionError(const Token *tok, const Token *index); void stlOutOfBoundsError(const Token* tok, const std::string& num, const std::string& var, bool at); void negativeIndexError(const Token* tok, const ValueFlow::Value& index); void invalidIteratorError(const Token* tok, const std::string& iteratorName); void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2); void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2); void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName); void iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName1, const std::string& containerName2); void iteratorsCmpError(const Token* cmpOperatorTok, const Token* containerTok1, const Token* containerTok2, const std::string& containerName); void mismatchingContainersError(const Token* tok); void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2); void sameIteratorExpressionError(const Token *tok); void stlBoundariesError(const Token* tok); void if_findError(const Token* tok, bool str); void checkFindInsertError(const Token *tok); void sizeError(const Token* tok); void redundantIfRemoveError(const Token* tok); void invalidContainerError(const Token *tok, const Token * contTok, const ValueFlow::Value *val, ErrorPath errorPath); void invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath); void uselessCallsReturnValueError(const Token* tok, const std::string& varname, const std::string& function); void uselessCallsSwapError(const Token* tok, const std::string& varname); void uselessCallsSubstrError(const Token* tok, bool empty); void uselessCallsEmptyError(const Token* tok); void uselessCallsRemoveError(const Token* tok, const std::string& function); void dereferenceInvalidIteratorError(const Token* deref, const std::string& iterName); void readingEmptyStlContainerError(const Token* tok, const ValueFlow::Value *value=nullptr); void useStlAlgorithmError(const Token *tok, const std::string &algoName); bool compareIteratorAgainstDifferentContainer(const Token* operatorTok, const Token* containerTok, const nonneg int iteratorId, const std::map& iteratorScopeBeginInfo); void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE { ErrorPath errorPath; CheckStl c(nullptr, settings, errorLogger); c.outOfBoundsError(nullptr, "container", nullptr, "x", nullptr); c.invalidIteratorError(nullptr, "iterator"); c.iteratorsError(nullptr, "container1", "container2"); c.iteratorsError(nullptr, nullptr, "container0", "container1"); c.iteratorsError(nullptr, nullptr, "container"); c.iteratorsCmpError(nullptr, nullptr, nullptr, "container1", "container2"); c.iteratorsCmpError(nullptr, nullptr, nullptr, "container"); c.invalidContainerError(nullptr, nullptr, nullptr, errorPath); c.mismatchingContainersError(nullptr); c.mismatchingContainerExpressionError(nullptr, nullptr); c.sameIteratorExpressionError(nullptr); c.dereferenceErasedError(nullptr, nullptr, "iter", false); c.stlOutOfBoundsError(nullptr, "i", "foo", false); c.negativeIndexError(nullptr, ValueFlow::Value(-1)); c.stlBoundariesError(nullptr); c.if_findError(nullptr, false); c.if_findError(nullptr, true); c.checkFindInsertError(nullptr); c.string_c_strError(nullptr); c.string_c_strReturn(nullptr); c.string_c_strParam(nullptr, 0); c.string_c_strThrowError(nullptr); c.sizeError(nullptr); c.missingComparisonError(nullptr, nullptr); c.redundantIfRemoveError(nullptr); c.uselessCallsReturnValueError(nullptr, "str", "find"); c.uselessCallsSwapError(nullptr, "str"); c.uselessCallsSubstrError(nullptr, false); c.uselessCallsEmptyError(nullptr); c.uselessCallsRemoveError(nullptr, "remove"); c.dereferenceInvalidIteratorError(nullptr, "i"); c.readingEmptyStlContainerError(nullptr); c.useStlAlgorithmError(nullptr, ""); } static std::string myName() { return "STL usage"; } std::string classInfo() const OVERRIDE { return "Check for invalid usage of STL:\n" "- out of bounds errors\n" "- misuse of iterators when iterating through a container\n" "- mismatching containers in calls\n" "- same iterators in calls\n" "- dereferencing an erased iterator\n" "- for vectors: using iterator/pointer after push_back has been used\n" "- optimisation: use empty() instead of size() to guarantee fast code\n" "- suspicious condition when using find\n" "- unnecessary searching in associative containers\n" "- redundant condition\n" "- common mistakes when using string::c_str()\n" "- useless calls of string and STL functions\n" "- dereferencing an invalid iterator\n" "- reading from empty STL container\n" "- consider using an STL algorithm instead of raw loop\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkstlH cppcheck-1.90/lib/checkstring.cpp000066400000000000000000000546021357737443600170330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkstring.h" #include "astutils.h" #include "errorlogger.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "utils.h" #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckString instance; } // CWE ids used: static const struct CWE CWE570(570U); // Expression is Always False static const struct CWE CWE571(571U); // Expression is Always True static const struct CWE CWE595(595U); // Comparison of Object References Instead of Object Contents static const struct CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments static const struct CWE CWE665(665U); // Improper Initialization static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior //--------------------------------------------------------------------------- // Writing string literal is UB //--------------------------------------------------------------------------- void CheckString::stringLiteralWrite() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->variable() || !tok->variable()->isPointer()) continue; const Token *str = tok->getValueTokenMinStrSize(mSettings); if (!str) continue; if (Token::Match(tok, "%var% [") && Token::simpleMatch(tok->linkAt(1), "] =")) stringLiteralWriteError(tok, str); else if (Token::Match(tok->previous(), "* %var% =")) stringLiteralWriteError(tok, str); } } } void CheckString::stringLiteralWriteError(const Token *tok, const Token *strValue) { std::list callstack; callstack.push_back(tok); if (strValue) callstack.push_back(strValue); std::string errmsg("Modifying string literal"); if (strValue) { std::string s = strValue->str(); // 20 is an arbitrary value, the max string length shown in a warning message if (s.size() > 20U) s = s.substr(0,17) + "..\""; errmsg += " " + s; } errmsg += " directly or indirectly is undefined behaviour."; reportError(callstack, Severity::error, "stringLiteralWrite", errmsg, CWE758, false); } //--------------------------------------------------------------------------- // Check for string comparison involving two static strings. // if(strcmp("00FF00","00FF00")==0) // <- statement is always true //--------------------------------------------------------------------------- void CheckString::checkAlwaysTrueOrFalseStringCompare() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->isName() && tok->strAt(1) == "(" && Token::Match(tok, "memcmp|strncmp|strcmp|stricmp|strverscmp|bcmp|strcmpi|strcasecmp|strncasecmp|strncasecmp_l|strcasecmp_l|wcsncasecmp|wcscasecmp|wmemcmp|wcscmp|wcscasecmp_l|wcsncasecmp_l|wcsncmp|_mbscmp|_memicmp|_memicmp_l|_stricmp|_wcsicmp|_mbsicmp|_stricmp_l|_wcsicmp_l|_mbsicmp_l")) { if (Token::Match(tok->tokAt(2), "%str% , %str% ,|)")) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(4); if (!tok->isExpandedMacro() && !tok->tokAt(2)->isExpandedMacro() && !tok->tokAt(4)->isExpandedMacro()) alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(5); } else if (Token::Match(tok->tokAt(2), "%name% , %name% ,|)")) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(4); if (str1 == str2) alwaysTrueStringVariableCompareError(tok, str1, str2); tok = tok->tokAt(5); } else if (Token::Match(tok->tokAt(2), "%name% . c_str ( ) , %name% . c_str ( ) ,|)")) { const std::string &str1 = tok->strAt(2); const std::string &str2 = tok->strAt(8); if (str1 == str2) alwaysTrueStringVariableCompareError(tok, str1, str2); tok = tok->tokAt(13); } } else if (tok->isName() && Token::Match(tok, "QString :: compare ( %str% , %str% )")) { const std::string &str1 = tok->strAt(4); const std::string &str2 = tok->strAt(6); alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(7); } else if (Token::Match(tok, "!!+ %str% ==|!= %str% !!+")) { const std::string &str1 = tok->strAt(1); const std::string &str2 = tok->strAt(3); alwaysTrueFalseStringCompareError(tok, str1, str2); tok = tok->tokAt(5); } if (!tok) break; } } void CheckString::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2) { const std::size_t stringLen = 10; const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + ".."); const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + ".."); reportError(tok, Severity::warning, "staticStringCompare", "Unnecessary comparison of static strings.\n" "The compared strings, '" + string1 + "' and '" + string2 + "', are always " + (str1==str2?"identical":"unequal") + ". " "Therefore the comparison is unnecessary and looks suspicious.", (str1==str2)?CWE571:CWE570, false); } void CheckString::alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2) { reportError(tok, Severity::warning, "stringCompare", "Comparison of identical string variables.\n" "The compared strings, '" + str1 + "' and '" + str2 + "', are identical. " "This could be a logic bug.", CWE571, false); } //----------------------------------------------------------------------------- // Detect "str == '\0'" where "*str == '\0'" is correct. // Comparing char* with each other instead of using strcmp() //----------------------------------------------------------------------------- void CheckString::checkSuspiciousStringCompare() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->isComparisonOp()) continue; const Token* varTok = tok->astOperand1(); const Token* litTok = tok->astOperand2(); if (!varTok || !litTok) // <- failed to create AST for comparison continue; if (Token::Match(varTok, "%char%|%num%|%str%")) std::swap(varTok, litTok); else if (!Token::Match(litTok, "%char%|%num%|%str%")) continue; // Pointer addition? if (varTok->str() == "+" && mTokenizer->isC()) { const Token * const tokens[2] = { varTok->astOperand1(), varTok->astOperand2() }; for (const Token * t : tokens) { while (t && (t->str() == "." || t->str() == "::")) t = t->astOperand2(); if (t && t->variable() && t->variable()->isPointer()) varTok = t; } } if (varTok->str() == "*") { if (!mTokenizer->isC() || varTok->astOperand2() != nullptr || litTok->tokType() != Token::eString) continue; varTok = varTok->astOperand1(); } while (varTok && (varTok->str() == "." || varTok->str() == "::")) varTok = varTok->astOperand2(); if (!varTok || !varTok->isName()) continue; const Variable *var = varTok->variable(); while (Token::Match(varTok->astParent(), "[.*]")) varTok = varTok->astParent(); const std::string varname = varTok->expressionString(); const bool ischar(litTok->tokType() == Token::eChar); if (litTok->tokType() == Token::eString) { if (mTokenizer->isC() || (var && var->isArrayOrPointer())) suspiciousStringCompareError(tok, varname, litTok->isLong()); } else if (ischar && var && var->isPointer()) { suspiciousStringCompareError_char(tok, varname); } } } } void CheckString::suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong) { const std::string cmpFunc = isLong ? "wcscmp" : "strcmp"; reportError(tok, Severity::warning, "literalWithCharPtrCompare", "$symbol:" + var + "\nString literal compared with variable '$symbol'. Did you intend to use " + cmpFunc + "() instead?", CWE595, false); } void CheckString::suspiciousStringCompareError_char(const Token* tok, const std::string& var) { reportError(tok, Severity::warning, "charLiteralWithCharPtrCompare", "$symbol:" + var + "\nChar literal compared with pointer '$symbol'. Did you intend to dereference it?", CWE595, false); } //--------------------------------------------------------------------------- // Adding C-string and char with operator+ //--------------------------------------------------------------------------- static bool isChar(const Variable* var) { return (var && !var->isPointer() && !var->isArray() && (var->typeStartToken()->str() == "char" || var->typeStartToken()->str() == "wchar_t")); } void CheckString::strPlusChar() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "+") { if (tok->astOperand1() && (tok->astOperand1()->tokType() == Token::eString)) { // string literal... if (tok->astOperand2() && (tok->astOperand2()->tokType() == Token::eChar || isChar(tok->astOperand2()->variable()))) // added to char variable or char constant strPlusCharError(tok); } } } } } void CheckString::strPlusCharError(const Token *tok) { std::string charType = "char"; if (tok && tok->astOperand2() && tok->astOperand2()->variable()) charType = tok->astOperand2()->variable()->typeStartToken()->str(); else if (tok && tok->astOperand2() && tok->astOperand2()->tokType() == Token::eChar && tok->astOperand2()->isLong()) charType = "wchar_t"; reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic. A value of type '" + charType +"' is added to a string literal.", CWE665, false); } //--------------------------------------------------------------------------- // Implicit casts of string literals to bool // Comparing string literal with strlen() with wrong length //--------------------------------------------------------------------------- void CheckString::checkIncorrectStringCompare() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { // skip "assert(str && ..)" and "assert(.. && str)" if ((endsWith(tok->str(), "assert", 6) || endsWith(tok->str(), "ASSERT", 6)) && Token::Match(tok, "%name% (") && (Token::Match(tok->tokAt(2), "%str% &&") || Token::Match(tok->next()->link()->tokAt(-2), "&& %str% )"))) tok = tok->next()->link(); if (Token::simpleMatch(tok, ". substr (") && Token::Match(tok->tokAt(3)->nextArgument(), "%num% )")) { const MathLib::biguint clen = MathLib::toULongNumber(tok->linkAt(2)->strAt(-1)); const Token* begin = tok->previous(); for (;;) { // Find start of statement while (begin->link() && Token::Match(begin, "]|)|>")) begin = begin->link()->previous(); if (Token::Match(begin->previous(), ".|::")) begin = begin->tokAt(-2); else break; } begin = begin->previous(); const Token* end = tok->linkAt(2)->next(); if (Token::Match(begin->previous(), "%str% ==|!=") && begin->strAt(-2) != "+") { const std::size_t slen = Token::getStrLength(begin->previous()); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", begin->strAt(-1)); } } else if (Token::Match(end, "==|!= %str% !!+")) { const std::size_t slen = Token::getStrLength(end->next()); if (clen != slen) { incorrectStringCompareError(tok->next(), "substr", end->strAt(1)); } } } else if (Token::Match(tok, "&&|%oror%|( %str%|%char% &&|%oror%|)") && !Token::Match(tok, "( %str%|%char% )")) { incorrectStringBooleanError(tok->next(), tok->strAt(1)); } else if (Token::Match(tok, "if|while ( %str%|%char% )") && !tok->tokAt(2)->getValue(0)) { incorrectStringBooleanError(tok->tokAt(2), tok->strAt(2)); } else if (tok->str() == "?" && Token::Match(tok->astOperand1(), "%str%|%char%")) incorrectStringBooleanError(tok->astOperand1(), tok->astOperand1()->str()); } } } void CheckString::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string) { reportError(tok, Severity::warning, "incorrectStringCompare", "$symbol:" + func + "\nString literal " + string + " doesn't match length argument for $symbol().", CWE570, false); } void CheckString::incorrectStringBooleanError(const Token *tok, const std::string& string) { const bool charLiteral = isCharLiteral(string); const std::string literalType = charLiteral ? "char" : "string"; const std::string result = getCharLiteral(string) == "\\0" ? "false" : "true"; reportError(tok, Severity::warning, charLiteral ? "incorrectCharBooleanError" : "incorrectStringBooleanError", "Conversion of " + literalType + " literal " + string + " to bool always evaluates to " + result + '.', CWE571, false); } //--------------------------------------------------------------------------- // always true: strcmp(str,"a")==0 || strcmp(str,"b") // TODO: Library configuration for string comparison functions //--------------------------------------------------------------------------- void CheckString::overlappingStrcmp() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() != "||") continue; std::list equals0; std::list notEquals0; std::stack tokens; tokens.push(tok); while (!tokens.empty()) { const Token * const t = tokens.top(); tokens.pop(); if (!t) continue; if (t->str() == "||") { tokens.push(t->astOperand1()); tokens.push(t->astOperand2()); continue; } if (t->str() == "==") { if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0")) equals0.push_back(t->astOperand1()); else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0")) equals0.push_back(t->astOperand2()); continue; } if (t->str() == "!=") { if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0")) notEquals0.push_back(t->astOperand1()); else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0")) notEquals0.push_back(t->astOperand2()); continue; } if (t->str() == "!" && Token::simpleMatch(t->astOperand1(), "(")) equals0.push_back(t->astOperand1()); else if (t->str() == "(") notEquals0.push_back(t); } for (const Token *eq0 : equals0) { for (const Token * ne0 : notEquals0) { if (!Token::Match(eq0->previous(), "strcmp|wcscmp (")) continue; if (!Token::Match(ne0->previous(), "strcmp|wcscmp (")) continue; const std::vector args1 = getArguments(eq0->previous()); const std::vector args2 = getArguments(ne0->previous()); if (args1.size() != 2 || args2.size() != 2) continue; if (args1[1]->isLiteral() && args2[1]->isLiteral() && args1[1]->str() != args2[1]->str() && isSameExpression(mTokenizer->isCPP(), true, args1[0], args2[0], mSettings->library, true, false)) overlappingStrcmpError(eq0, ne0); } } } } } void CheckString::overlappingStrcmpError(const Token *eq0, const Token *ne0) { std::string eq0Expr(eq0 ? eq0->expressionString() : std::string("strcmp(x,\"abc\")")); if (eq0 && eq0->astParent()->str() == "!") eq0Expr = "!" + eq0Expr; else eq0Expr += " == 0"; const std::string ne0Expr = (ne0 ? ne0->expressionString() : std::string("strcmp(x,\"def\")")) + " != 0"; reportError(ne0, Severity::warning, "overlappingStrcmp", "The expression '" + ne0Expr + "' is suspicious. It overlaps '" + eq0Expr + "'."); } //--------------------------------------------------------------------------- // Overlapping source and destination passed to sprintf(). // TODO: Library configuration for overlapping arguments //--------------------------------------------------------------------------- void CheckString::sprintfOverlappingData() { const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "sprintf|snprintf|swprintf (")) continue; const std::vector args = getArguments(tok); const int formatString = Token::simpleMatch(tok, "sprintf") ? 1 : 2; for (unsigned int argnr = formatString + 1; argnr < args.size(); ++argnr) { const Token *dest = args[0]; while (dest->isCast()) dest = dest->astOperand2() ? dest->astOperand2() : dest->astOperand1(); const Token *arg = args[argnr]; if (!arg->valueType() || arg->valueType()->pointer != 1) continue; while (arg->isCast()) arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1(); const bool same = isSameExpression(mTokenizer->isCPP(), false, dest, arg, mSettings->library, true, false); if (same) { sprintfOverlappingDataError(tok, args[argnr], arg->expressionString()); } } } } } void CheckString::sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname) { const std::string func = funcTok ? funcTok->str() : "s[n]printf"; reportError(tok, Severity::error, "sprintfOverlappingData", "$symbol:" + varname + "\n" "Undefined behavior: Variable '$symbol' is used as parameter and destination in " + func + "().\n" + "The variable '$symbol' is used both as a parameter and as destination in " + func + "(). The origin and destination buffers overlap. Quote from glibc (C-library) " "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): " "\"If copying takes place between objects that overlap as a result of a call " "to sprintf() or snprintf(), the results are undefined.\"", CWE628, false); } cppcheck-1.90/lib/checkstring.h000066400000000000000000000125701357737443600164760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkstringH #define checkstringH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** @brief Detect misusage of C-style strings and related standard functions */ class CPPCHECKLIB CheckString : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckString() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckString(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckString checkString(tokenizer, settings, errorLogger); // Checks checkString.strPlusChar(); checkString.checkSuspiciousStringCompare(); checkString.stringLiteralWrite(); checkString.overlappingStrcmp(); checkString.checkIncorrectStringCompare(); checkString.sprintfOverlappingData(); checkString.checkAlwaysTrueOrFalseStringCompare(); } /** @brief undefined behaviour, writing string literal */ void stringLiteralWrite(); /** @brief str plus char (unusual pointer arithmetic) */ void strPlusChar(); /** @brief %Check for using bad usage of strncmp and substr */ void checkIncorrectStringCompare(); /** @brief %Check for comparison of a string literal with a char* variable */ void checkSuspiciousStringCompare(); /** @brief %Check for suspicious code that compares string literals for equality */ void checkAlwaysTrueOrFalseStringCompare(); /** @brief %Check for overlapping strcmp() */ void overlappingStrcmp(); /** @brief %Check for overlapping source and destination passed to sprintf() */ void sprintfOverlappingData(); private: void stringLiteralWriteError(const Token *tok, const Token *strValue); void sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname); void strPlusCharError(const Token *tok); void incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string); void incorrectStringBooleanError(const Token *tok, const std::string& string); void alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2); void alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2); void suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong); void suspiciousStringCompareError_char(const Token* tok, const std::string& var); void overlappingStrcmpError(const Token* eq0, const Token *ne0); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckString c(nullptr, settings, errorLogger); c.stringLiteralWriteError(nullptr, nullptr); c.sprintfOverlappingDataError(nullptr, nullptr, "varname"); c.strPlusCharError(nullptr); c.incorrectStringCompareError(nullptr, "substr", "\"Hello World\""); c.suspiciousStringCompareError(nullptr, "foo", false); c.suspiciousStringCompareError_char(nullptr, "foo"); c.incorrectStringBooleanError(nullptr, "\"Hello World\""); c.incorrectStringBooleanError(nullptr, "\'x\'"); c.alwaysTrueFalseStringCompareError(nullptr, "str1", "str2"); c.alwaysTrueStringVariableCompareError(nullptr, "varname1", "varname2"); c.overlappingStrcmpError(nullptr, nullptr); } static std::string myName() { return "String"; } std::string classInfo() const OVERRIDE { return "Detect misusage of C-style strings:\n" "- overlapping buffers passed to sprintf as source and destination\n" "- incorrect length arguments for 'substr' and 'strncmp'\n" "- suspicious condition (runtime comparison of string literals)\n" "- suspicious condition (string/char literals as boolean)\n" "- suspicious comparison of a string literal with a char\\* variable\n" "- suspicious comparison of '\\0' with a char\\* variable\n" "- overlapping strcmp() expression\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkstringH cppcheck-1.90/lib/checktype.cpp000066400000000000000000000451101357737443600165000ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checktype.h" #include "errorlogger.h" #include "mathlib.h" #include "platform.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckType instance; } //--------------------------------------------------------------------------- // Checking for shift by too many bits //--------------------------------------------------------------------------- // // CWE ids used: static const struct CWE CWE195(195U); // Signed to Unsigned Conversion Error static const struct CWE CWE197(197U); // Numeric Truncation Error static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const struct CWE CWE190(190U); // Integer Overflow or Wraparound void CheckType::checkTooBigBitwiseShift() { // unknown sizeof(int) => can't run this checker if (mSettings->platformType == Settings::Unspecified) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { // C++ and macro: OUT(x<isCPP() && Token::Match(tok, "[;{}] %name% (") && Token::simpleMatch(tok->linkAt(2), ") ;") && tok->next()->isUpperCaseName() && !tok->next()->function()) tok = tok->linkAt(2); if (!tok->astOperand1() || !tok->astOperand2()) continue; if (!Token::Match(tok, "<<|>>|<<=|>>=")) continue; // get number of bits of lhs const ValueType * const lhstype = tok->astOperand1()->valueType(); if (!lhstype || !lhstype->isIntegral() || lhstype->pointer >= 1) continue; // C11 Standard, section 6.5.7 Bitwise shift operators, states: // The integer promotions are performed on each of the operands. // The type of the result is that of the promoted left operand. int lhsbits; if ((lhstype->type == ValueType::Type::CHAR) || (lhstype->type == ValueType::Type::SHORT) || (lhstype->type == ValueType::Type::WCHAR_T) || (lhstype->type == ValueType::Type::BOOL) || (lhstype->type == ValueType::Type::INT)) lhsbits = mSettings->int_bit; else if (lhstype->type == ValueType::Type::LONG) lhsbits = mSettings->long_bit; else if (lhstype->type == ValueType::Type::LONGLONG) lhsbits = mSettings->long_long_bit; else continue; // Get biggest rhs value. preferably a value which doesn't have 'condition'. const ValueFlow::Value * value = tok->astOperand2()->getValueGE(lhsbits, mSettings); if (value && mSettings->isEnabled(value, false)) tooBigBitwiseShiftError(tok, lhsbits, *value); else if (lhstype->sign == ValueType::Sign::SIGNED) { value = tok->astOperand2()->getValueGE(lhsbits-1, mSettings); if (value && mSettings->isEnabled(value, false)) tooBigSignedBitwiseShiftError(tok, lhsbits, *value); } } } void CheckType::tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) { const char id[] = "shiftTooManyBits"; if (!tok) { reportError(tok, Severity::error, id, "Shifting 32-bit value by 40 bits is undefined behaviour", CWE758, false); return; } const ErrorPath errorPath = getErrorPath(tok, &rhsbits, "Shift"); std::ostringstream errmsg; errmsg << "Shifting " << lhsbits << "-bit value by " << rhsbits.intvalue << " bits is undefined behaviour"; if (rhsbits.condition) errmsg << ". See condition at line " << rhsbits.condition->linenr() << "."; reportError(errorPath, rhsbits.errorSeverity() ? Severity::error : Severity::warning, id, errmsg.str(), CWE758, rhsbits.isInconclusive()); } void CheckType::tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) { const char id[] = "shiftTooManyBitsSigned"; const bool cpp14 = mSettings->standards.cpp >= Standards::CPP14; std::string behaviour = "undefined"; if (cpp14) behaviour = "implementation-defined"; if (!tok) { reportError(tok, Severity::error, id, "Shifting signed 32-bit value by 31 bits is " + behaviour + " behaviour", CWE758, false); return; } const ErrorPath errorPath = getErrorPath(tok, &rhsbits, "Shift"); std::ostringstream errmsg; errmsg << "Shifting signed " << lhsbits << "-bit value by " << rhsbits.intvalue << " bits is " + behaviour + " behaviour"; if (rhsbits.condition) errmsg << ". See condition at line " << rhsbits.condition->linenr() << "."; Severity::SeverityType severity = rhsbits.errorSeverity() ? Severity::error : Severity::warning; if (cpp14) severity = Severity::portability; if ((severity == Severity::portability) && !mSettings->isEnabled(Settings::PORTABILITY)) return; reportError(errorPath, severity, id, errmsg.str(), CWE758, rhsbits.isInconclusive()); } //--------------------------------------------------------------------------- // Checking for integer overflow //--------------------------------------------------------------------------- void CheckType::checkIntegerOverflow() { // unknown sizeof(int) => can't run this checker if (mSettings->platformType == Settings::Unspecified || mSettings->int_bit >= MathLib::bigint_bits) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->isArithmeticalOp()) continue; // is result signed integer? const ValueType *vt = tok->valueType(); if (!vt || !vt->isIntegral() || vt->sign != ValueType::Sign::SIGNED) continue; unsigned int bits; if (vt->type == ValueType::Type::INT) bits = mSettings->int_bit; else if (vt->type == ValueType::Type::LONG) bits = mSettings->long_bit; else if (vt->type == ValueType::Type::LONGLONG) bits = mSettings->long_long_bit; else continue; if (bits >= MathLib::bigint_bits) continue; // max value according to platform settings. const MathLib::bigint maxvalue = (((MathLib::bigint)1) << (bits - 1)) - 1; // is there a overflow result value const ValueFlow::Value *value = tok->getValueGE(maxvalue + 1, mSettings); if (!value) value = tok->getValueLE(-maxvalue - 2, mSettings); if (!value || !mSettings->isEnabled(value,false)) continue; // For left shift, it's common practice to shift into the sign bit if (tok->str() == "<<" && value->intvalue > 0 && value->intvalue < (((MathLib::bigint)1) << bits)) continue; integerOverflowError(tok, *value); } } void CheckType::integerOverflowError(const Token *tok, const ValueFlow::Value &value) { const std::string expr(tok ? tok->expressionString() : ""); std::string msg; if (value.condition) msg = ValueFlow::eitherTheConditionIsRedundant(value.condition) + " or there is signed integer overflow for expression '" + expr + "'."; else msg = "Signed integer overflow for expression '" + expr + "'."; if (value.safe) msg = "Safe checks: " + msg; reportError(getErrorPath(tok, &value, "Integer overflow"), value.errorSeverity() ? Severity::error : Severity::warning, getMessageId(value, "integerOverflow").c_str(), msg, CWE190, value.isInconclusive()); } //--------------------------------------------------------------------------- // Checking for sign conversion when operand can be negative //--------------------------------------------------------------------------- void CheckType::checkSignConversion() { if (!mSettings->isEnabled(Settings::WARNING)) return; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->isArithmeticalOp() || Token::Match(tok,"+|-")) continue; // Is result unsigned? if (!(tok->valueType() && tok->valueType()->sign == ValueType::Sign::UNSIGNED)) continue; // Check if an operand can be negative.. std::stack tokens; tokens.push(tok->astOperand1()); tokens.push(tok->astOperand2()); while (!tokens.empty()) { const Token *tok1 = tokens.top(); tokens.pop(); if (!tok1) continue; const ValueFlow::Value *negativeValue = tok1->getValueLE(-1,mSettings); if (!negativeValue) continue; if (tok1->valueType() && tok1->valueType()->sign != ValueType::Sign::UNSIGNED) signConversionError(tok1, negativeValue, tok1->isNumber()); } } } void CheckType::signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue) { const std::string expr(tok ? tok->expressionString() : "var"); std::ostringstream msg; if (tok && tok->isName()) msg << "$symbol:" << expr << "\n"; if (constvalue) msg << "Expression '" << expr << "' has a negative value. That is converted to an unsigned value and used in an unsigned calculation."; else msg << "Expression '" << expr << "' can have a negative value. That is converted to an unsigned value and used in an unsigned calculation."; if (!negativeValue) reportError(tok, Severity::warning, "signConversion", msg.str(), CWE195, false); else { const ErrorPath &errorPath = getErrorPath(tok,negativeValue,"Negative value is converted to an unsigned value"); reportError(errorPath, Severity::warning, Check::getMessageId(*negativeValue, "signConversion").c_str(), msg.str(), CWE195, negativeValue->isInconclusive()); } } //--------------------------------------------------------------------------- // Checking for long cast of int result const long x = var1 * var2; //--------------------------------------------------------------------------- void CheckType::checkLongCast() { if (!mSettings->isEnabled(Settings::STYLE)) return; // Assignments.. for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "=" || !Token::Match(tok->astOperand2(), "*|<<")) continue; if (tok->astOperand2()->hasKnownIntValue()) { const ValueFlow::Value &v = tok->astOperand2()->values().front(); if (mSettings->isIntValue(v.intvalue)) continue; } const ValueType *lhstype = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; const ValueType *rhstype = tok->astOperand2()->valueType(); if (!lhstype || !rhstype) continue; // assign int result to long/longlong const nonpointer? if (rhstype->type == ValueType::Type::INT && rhstype->pointer == 0U && rhstype->originalTypeName.empty() && (lhstype->type == ValueType::Type::LONG || lhstype->type == ValueType::Type::LONGLONG) && lhstype->pointer == 0U && lhstype->constness == 1U && lhstype->originalTypeName.empty()) longCastAssignError(tok); } // Return.. const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { // function must return long data const Token * def = scope->classDef; bool islong = false; while (Token::Match(def, "%type%|::")) { if (def->str() == "long" && def->originalName().empty()) { islong = true; break; } def = def->previous(); } if (!islong) continue; // return statements const Token *ret = nullptr; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "return") { if (Token::Match(tok->astOperand1(), "<<|*")) { const ValueType *type = tok->astOperand1()->valueType(); if (type && type->type == ValueType::Type::INT && type->pointer == 0U && type->originalTypeName.empty()) ret = tok; } // All return statements must have problem otherwise no warning if (ret != tok) { ret = nullptr; break; } } } if (ret) longCastReturnError(ret); } } void CheckType::longCastAssignError(const Token *tok) { reportError(tok, Severity::style, "truncLongCastAssignment", "int result is assigned to long variable. If the variable is long to avoid loss of information, then you have loss of information.\n" "int result is assigned to long variable. If the variable is long to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'l = a * b;' => 'l = (long)a * b;'.", CWE197, false); } void CheckType::longCastReturnError(const Token *tok) { reportError(tok, Severity::style, "truncLongCastReturn", "int result is returned as long value. If the return value is long to avoid loss of information, then you have loss of information.\n" "int result is returned as long value. If the return value is long to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'return a*b;' => 'return (long)a*b'.", CWE197, false); } //--------------------------------------------------------------------------- // Checking for float to integer overflow //--------------------------------------------------------------------------- void CheckType::checkFloatToIntegerOverflow() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { const ValueType *vtint, *vtfloat; const std::list *floatValues; // Explicit cast if (Token::Match(tok, "( %name%") && tok->astOperand1() && !tok->astOperand2()) { vtint = tok->valueType(); vtfloat = tok->astOperand1()->valueType(); floatValues = &tok->astOperand1()->values(); checkFloatToIntegerOverflow(tok, vtint, vtfloat, floatValues); } // Assignment else if (tok->str() == "=" && tok->astOperand1() && tok->astOperand2()) { vtint = tok->astOperand1()->valueType(); vtfloat = tok->astOperand2()->valueType(); floatValues = &tok->astOperand2()->values(); checkFloatToIntegerOverflow(tok, vtint, vtfloat, floatValues); } else if (tok->str() == "return" && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isFloat()) { const Scope *scope = tok->scope(); while (scope && scope->type != Scope::ScopeType::eLambda && scope->type != Scope::ScopeType::eFunction) scope = scope->nestedIn; if (scope && scope->type == Scope::ScopeType::eFunction && scope->function && scope->function->retDef) { const ValueType &valueType = ValueType::parseDecl(scope->function->retDef, mSettings); vtfloat = tok->astOperand1()->valueType(); floatValues = &tok->astOperand1()->values(); checkFloatToIntegerOverflow(tok, &valueType, vtfloat, floatValues); } } } } void CheckType::checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list *floatValues) { // Conversion of float to integer? if (!vtint || !vtint->isIntegral()) return; if (!vtfloat || !vtfloat->isFloat()) return; for (const ValueFlow::Value &f : *floatValues) { if (f.valueType != ValueFlow::Value::ValueType::FLOAT) continue; if (!mSettings->isEnabled(&f, false)) continue; if (f.floatValue > ~0ULL) floatToIntegerOverflowError(tok, f); else if ((-f.floatValue) > (1ULL<<62)) floatToIntegerOverflowError(tok, f); else if (mSettings->platformType != Settings::Unspecified) { int bits = 0; if (vtint->type == ValueType::Type::CHAR) bits = mSettings->char_bit; else if (vtint->type == ValueType::Type::SHORT) bits = mSettings->short_bit; else if (vtint->type == ValueType::Type::INT) bits = mSettings->int_bit; else if (vtint->type == ValueType::Type::LONG) bits = mSettings->long_bit; else if (vtint->type == ValueType::Type::LONGLONG) bits = mSettings->long_long_bit; else continue; if (bits < MathLib::bigint_bits && f.floatValue >= (((MathLib::biguint)1) << bits)) floatToIntegerOverflowError(tok, f); } } } void CheckType::floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value) { std::ostringstream errmsg; errmsg << "Undefined behaviour: float (" << value.floatValue << ") to integer conversion overflow."; reportError(getErrorPath(tok, &value, "float to integer conversion"), value.errorSeverity() ? Severity::error : Severity::warning, "floatConversionOverflow", errmsg.str(), CWE190, value.isInconclusive()); } cppcheck-1.90/lib/checktype.h000066400000000000000000000111441357737443600161450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checktypeH #define checktypeH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "valueflow.h" class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** @brief Various small checks */ class CPPCHECKLIB CheckType : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckType() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckType(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { // These are not "simplified" because casts can't be ignored CheckType checkType(tokenizer, settings, errorLogger); checkType.checkTooBigBitwiseShift(); checkType.checkIntegerOverflow(); checkType.checkSignConversion(); checkType.checkLongCast(); checkType.checkFloatToIntegerOverflow(); } /** @brief %Check for bitwise shift with too big right operand */ void checkTooBigBitwiseShift(); /** @brief %Check for integer overflow */ void checkIntegerOverflow(); /** @brief %Check for dangerous sign conversion */ void checkSignConversion(); /** @brief %Check for implicit long cast of int result */ void checkLongCast(); /** @brief %Check for float to integer overflow */ void checkFloatToIntegerOverflow(); void checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list *floatValues); private: // Error messages.. void tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); void tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); void integerOverflowError(const Token *tok, const ValueFlow::Value &value); void signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue); void longCastAssignError(const Token *tok); void longCastReturnError(const Token *tok); void floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckType c(nullptr, settings, errorLogger); c.tooBigBitwiseShiftError(nullptr, 32, ValueFlow::Value(64)); c.tooBigSignedBitwiseShiftError(nullptr, 31, ValueFlow::Value(31)); c.integerOverflowError(nullptr, ValueFlow::Value(1LL<<32)); c.signConversionError(nullptr, nullptr, false); c.longCastAssignError(nullptr); c.longCastReturnError(nullptr); ValueFlow::Value f; f.valueType = ValueFlow::Value::ValueType::FLOAT; f.floatValue = 1E100; c.floatToIntegerOverflowError(nullptr, f); } static std::string myName() { return "Type"; } std::string classInfo() const OVERRIDE { return "Type checks\n" "- bitwise shift by too many bits (only enabled when --platform is used)\n" "- signed integer overflow (only enabled when --platform is used)\n" "- dangerous sign conversion, when signed value can be negative\n" "- possible loss of information when assigning int result to long variable\n" "- possible loss of information when returning int result as long return value\n" "- float conversion overflow\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checktypeH cppcheck-1.90/lib/checkuninitvar.cpp000066400000000000000000001650451357737443600175500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkuninitvar.h" #include "astutils.h" #include "checknullpointer.h" // CheckNullPointer::isPointerDeref #include "errorlogger.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckUninitVar instance; } //--------------------------------------------------------------------------- // CWE ids used: static const struct CWE CWE476(476U); // NULL Pointer Dereference static const struct CWE CWE676(676U); static const struct CWE CWE908(908U); static const struct CWE CWE825(825U); // get ast parent, skip possible address-of and casts static const Token *getAstParentSkipPossibleCastAndAddressOf(const Token *vartok, bool *unknown) { if (unknown) *unknown = false; if (!vartok) return nullptr; const Token *parent = vartok->astParent(); while (Token::Match(parent, ".|::")) parent = parent->astParent(); if (!parent) return nullptr; if (parent->isUnaryOp("&")) parent = parent->astParent(); else if (parent->str() == "&" && vartok == parent->astOperand2() && Token::Match(parent->astOperand1()->previous(), "( %type% )")) { parent = parent->astParent(); if (unknown) *unknown = true; } while (parent && parent->isCast()) parent = parent->astParent(); return parent; } void CheckUninitVar::check() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); std::set arrayTypeDefs; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%name% [") && tok->variable() && Token::Match(tok->variable()->typeStartToken(), "%type% %var% ;")) arrayTypeDefs.insert(tok->variable()->typeStartToken()->str()); } // check every executable scope for (const Scope &scope : symbolDatabase->scopeList) { if (scope.isExecutable()) { checkScope(&scope, arrayTypeDefs); } } } void CheckUninitVar::checkScope(const Scope* scope, const std::set &arrayTypeDefs) { for (const Variable &var : scope->varlist) { if ((mTokenizer->isCPP() && var.type() && !var.isPointer() && var.type()->needInitialization != Type::NeedInitialization::True) || var.isStatic() || var.isExtern() || var.isReference()) continue; // don't warn for try/catch exception variable if (var.isThrow()) continue; if (Token::Match(var.nameToken()->next(), "[({:]")) continue; if (Token::Match(var.nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not checkRhs(var.nameToken(), var, NO_ALLOC, 0U, emptyString); continue; } if (Token::Match(var.nameToken(), "%name% ) (") && Token::simpleMatch(var.nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not checkRhs(var.nameToken()->linkAt(2)->next(), var, NO_ALLOC, 0U, emptyString); continue; } if (var.isArray() || var.isPointerToArray()) { const Token *tok = var.nameToken()->next(); if (var.isPointerToArray()) tok = tok->next(); while (Token::simpleMatch(tok->link(), "] [")) tok = tok->link()->next(); if (Token::Match(tok->link(), "] =|{")) continue; } bool stdtype = mTokenizer->isC() && arrayTypeDefs.find(var.typeStartToken()->str()) == arrayTypeDefs.end(); const Token* tok = var.typeStartToken(); for (; tok != var.nameToken() && tok->str() != "<"; tok = tok->next()) { if (tok->isStandardType() || tok->isEnumType()) stdtype = true; } if (var.isArray() && !stdtype) continue; while (tok && tok->str() != ";") tok = tok->next(); if (!tok) continue; if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "for (") && checkLoopBody(tok->astParent()->link()->next(), var, var.isArray() ? ARRAY : NO_ALLOC, emptyString, true)) continue; if (var.isArray()) { Alloc alloc = ARRAY; const std::map variableValue; bool init = false; for (const Token *parent = var.nameToken(); parent; parent = parent->astParent()) { if (parent->str() == "=") init = true; } if (!init) checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); continue; } if (stdtype || var.isPointer()) { Alloc alloc = NO_ALLOC; const std::map variableValue; checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); } if (var.type()) checkStruct(tok, var); } if (scope->function) { for (const Variable &arg : scope->function->argumentList) { if (arg.declarationId() && Token::Match(arg.typeStartToken(), "%type% * %name% [,)]")) { // Treat the pointer as initialized until it is assigned by malloc for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "[;{}] %varid% = %name% (", arg.declarationId()) && mSettings->library.returnuninitdata.count(tok->strAt(3)) == 1U) { if (arg.typeStartToken()->strAt(-1) == "struct" || (arg.type() && arg.type()->isStructType())) checkStruct(tok, arg); else if (arg.typeStartToken()->isStandardType() || arg.typeStartToken()->isEnumType()) { Alloc alloc = NO_ALLOC; const std::map variableValue; checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue); } } } } } } } void CheckUninitVar::checkStruct(const Token *tok, const Variable &structvar) { const Token *typeToken = structvar.typeStartToken(); const SymbolDatabase * symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope2 : symbolDatabase->classAndStructScopes) { if (scope2->className == typeToken->str() && scope2->numConstructors == 0U) { for (const Variable &var : scope2->varlist) { if (var.isStatic() || var.hasDefault() || var.isArray() || (!mTokenizer->isC() && var.isClass() && (!var.type() || var.type()->needInitialization != Type::NeedInitialization::True))) continue; // is the variable declared in a inner union? bool innerunion = false; for (const Scope *innerScope : scope2->nestedList) { if (innerScope->type == Scope::eUnion) { if (var.typeStartToken()->linenr() >= innerScope->bodyStart->linenr() && var.typeStartToken()->linenr() <= innerScope->bodyEnd->linenr()) { innerunion = true; break; } } } if (!innerunion) { Alloc alloc = NO_ALLOC; const Token *tok2 = tok; if (tok->str() == "}") tok2 = tok2->next(); const std::map variableValue; checkScopeForVariable(tok2, structvar, nullptr, nullptr, &alloc, var.name(), variableValue); } } } } } static VariableValue operator!(VariableValue v) { v.notEqual = !v.notEqual; return v; } static bool operator==(const VariableValue & v, MathLib::bigint i) { return v.notEqual ? (i != v.value) : (i == v.value); } static bool operator!=(const VariableValue & v, MathLib::bigint i) { return v.notEqual ? (i == v.value) : (i != v.value); } static void conditionAlwaysTrueOrFalse(const Token *tok, const std::map &variableValue, bool *alwaysTrue, bool *alwaysFalse) { if (!tok) return; if (tok->hasKnownIntValue()) { if (tok->getKnownIntValue() == 0) *alwaysFalse = true; else *alwaysTrue = true; return; } if (tok->isName() || tok->str() == ".") { while (tok && tok->str() == ".") tok = tok->astOperand2(); const std::map::const_iterator it = variableValue.find(tok ? tok->varId() : ~0U); if (it != variableValue.end()) { *alwaysTrue = (it->second != 0LL); *alwaysFalse = (it->second == 0LL); } } else if (tok->isComparisonOp()) { if (tok->hasKnownIntValue()) { if (tok->values().front().intvalue) *alwaysTrue = true; else *alwaysFalse = true; return; } const Token *vartok, *numtok; if (tok->astOperand2() && tok->astOperand2()->isNumber()) { vartok = tok->astOperand1(); numtok = tok->astOperand2(); } else if (tok->astOperand1() && tok->astOperand1()->isNumber()) { vartok = tok->astOperand2(); numtok = tok->astOperand1(); } else { return; } while (vartok && vartok->str() == ".") vartok = vartok->astOperand2(); const std::map::const_iterator it = variableValue.find(vartok ? vartok->varId() : ~0U); if (it == variableValue.end()) return; if (tok->str() == "==") *alwaysTrue = (it->second == MathLib::toLongNumber(numtok->str())); else if (tok->str() == "!=") *alwaysTrue = (it->second != MathLib::toLongNumber(numtok->str())); else return; *alwaysFalse = !(*alwaysTrue); } else if (tok->str() == "!") { bool t=false,f=false; conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t, &f); if (t||f) { *alwaysTrue = !t; *alwaysFalse = !f; } } else if (tok->str() == "||") { bool t1=false, f1=false; conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1); bool t2=false, f2=false; if (!t1) conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2); *alwaysTrue = (t1 || t2); *alwaysFalse = (f1 && f2); } else if (tok->str() == "&&") { bool t1=false, f1=false; conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1); bool t2=false, f2=false; if (!f1) conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2); *alwaysTrue = (t1 && t2); *alwaysFalse = (f1 || f2); } } static bool isVariableUsed(const Token *tok, const Variable& var) { if (!tok) return false; if (tok->str() == "&" && !tok->astOperand2()) return false; if (tok->isConstOp()) return isVariableUsed(tok->astOperand1(),var) || isVariableUsed(tok->astOperand2(),var); if (tok->varId() != var.declarationId()) return false; if (!var.isArray()) return true; const Token *parent = tok->astParent(); while (Token::Match(parent, "[?:]")) parent = parent->astParent(); // no dereference, then array is not "used" if (!Token::Match(parent, "*|[")) return false; const Token *parent2 = parent->astParent(); // TODO: handle function calls. There is a TODO assertion in TestUninitVar::uninitvar_arrays return !parent2 || parent2->isConstOp() || (parent2->str() == "=" && parent2->astOperand2() == parent); } bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, Alloc* const alloc, const std::string &membervar, std::map variableValue) { const bool suppressErrors(possibleInit && *possibleInit); // Assume that this is a variable delaratkon, rather than a fundef const bool printDebug = mSettings->debugwarnings; if (possibleInit) *possibleInit = false; int number_of_if = 0; if (var.declarationId() == 0U) return true; for (; tok; tok = tok->next()) { // End of scope.. if (tok->str() == "}") { if (number_of_if && possibleInit) *possibleInit = true; // might be a noreturn function.. if (mTokenizer->isScopeNoReturn(tok)) { if (noreturn) *noreturn = true; return false; } break; } // Unconditional inner scope or try.. if (tok->str() == "{" && Token::Match(tok->previous(), ",|;|{|}|try")) { if (checkScopeForVariable(tok->next(), var, possibleInit, noreturn, alloc, membervar, variableValue)) return true; tok = tok->link(); continue; } // assignment with nonzero constant.. if (Token::Match(tok->previous(), "[;{}] %var% = - %name% ;")) variableValue[tok->varId()] = !VariableValue(0); // Inner scope.. else if (Token::simpleMatch(tok, "if (")) { bool alwaysTrue = false; bool alwaysFalse = false; // Is variable assigned in condition? if (!membervar.empty()) { for (const Token *cond = tok->linkAt(1); cond != tok; cond = cond->previous()) { if (cond->varId() == var.declarationId() && isMemberVariableAssignment(cond, membervar)) return true; } } conditionAlwaysTrueOrFalse(tok->next()->astOperand2(), variableValue, &alwaysTrue, &alwaysFalse); // initialization / usage in condition.. if (!alwaysTrue && checkIfForWhileHead(tok->next(), var, suppressErrors, bool(number_of_if == 0), *alloc, membervar)) return true; // checking if a not-zero variable is zero => bail out int condVarId = 0; VariableValue condVarValue(0); const Token *condVarTok = nullptr; if (alwaysFalse) ; else if (Token::simpleMatch(tok, "if (") && astIsVariableComparison(tok->next()->astOperand2(), "!=", "0", &condVarTok)) { const std::map::const_iterator it = variableValue.find(condVarTok->varId()); if (it != variableValue.end() && it->second != 0) return true; // this scope is not fully analysed => return true else { condVarId = condVarTok->varId(); condVarValue = !VariableValue(0); } } else if (Token::simpleMatch(tok, "if (") && Token::Match(tok->next()->astOperand2(), "==|!=")) { const Token *condition = tok->next()->astOperand2(); const Token *lhs = condition->astOperand1(); const Token *rhs = condition->astOperand2(); const Token *vartok = rhs && rhs->isNumber() ? lhs : rhs; const Token *numtok = rhs && rhs->isNumber() ? rhs : lhs; while (Token::simpleMatch(vartok, ".")) vartok = vartok->astOperand2(); if (vartok && vartok->varId() && numtok) { const std::map::const_iterator it = variableValue.find(vartok->varId()); if (it != variableValue.end() && it->second != MathLib::toLongNumber(numtok->str())) return true; // this scope is not fully analysed => return true else { condVarId = vartok->varId(); condVarValue = VariableValue(MathLib::toLongNumber(numtok->str())); if (condition->str() == "!=") condVarValue = !condVarValue; } } } // goto the { tok = tok->next()->link()->next(); if (!tok) break; if (tok->str() == "{") { bool possibleInitIf((!alwaysTrue && number_of_if > 0) || suppressErrors); bool noreturnIf = false; const bool initif = !alwaysFalse && checkScopeForVariable(tok->next(), var, &possibleInitIf, &noreturnIf, alloc, membervar, variableValue); // bail out for such code: // if (a) x=0; // conditional initialization // if (b) return; // cppcheck doesn't know if b can be false when a is false. // x++; // it's possible x is always initialized if (!alwaysTrue && noreturnIf && number_of_if > 0) { if (printDebug) { std::string condition; for (const Token *tok2 = tok->linkAt(-1); tok2 != tok; tok2 = tok2->next()) { condition += tok2->str(); if (tok2->isName() && tok2->next()->isName()) condition += ' '; } reportError(tok, Severity::debug, "debug", "bailout uninitialized variable checking for '" + var.name() + "'. can't determine if this condition can be false when previous condition is false: " + condition); } return true; } if (alwaysTrue && (initif || noreturnIf)) return true; std::map varValueIf; if (!alwaysFalse && !initif && !noreturnIf) { for (const Token *tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}.] %name% = - %name% ;")) varValueIf[tok2->next()->varId()] = !VariableValue(0); else if (Token::Match(tok2, "[;{}.] %name% = %num% ;")) varValueIf[tok2->next()->varId()] = VariableValue(MathLib::toLongNumber(tok2->strAt(3))); } } if (initif && condVarId > 0U) variableValue[condVarId] = !condVarValue; // goto the } tok = tok->link(); if (!Token::simpleMatch(tok, "} else {")) { if (initif || possibleInitIf) { ++number_of_if; if (number_of_if >= 2) return true; } } else { // goto the { tok = tok->tokAt(2); bool possibleInitElse((!alwaysFalse && number_of_if > 0) || suppressErrors); bool noreturnElse = false; const bool initelse = !alwaysTrue && checkScopeForVariable(tok->next(), var, &possibleInitElse, &noreturnElse, alloc, membervar, variableValue); std::map varValueElse; if (!alwaysTrue && !initelse && !noreturnElse) { for (const Token *tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}.] %var% = - %name% ;")) varValueElse[tok2->next()->varId()] = !VariableValue(0); else if (Token::Match(tok2, "[;{}.] %var% = %num% ;")) varValueElse[tok2->next()->varId()] = VariableValue(MathLib::toLongNumber(tok2->strAt(3))); } } if (initelse && condVarId > 0U && !noreturnIf && !noreturnElse) variableValue[condVarId] = condVarValue; // goto the } tok = tok->link(); if ((alwaysFalse || initif || noreturnIf) && (alwaysTrue || initelse || noreturnElse)) return true; if (initif || initelse || possibleInitElse) ++number_of_if; if (!initif && !noreturnIf) variableValue.insert(varValueIf.begin(), varValueIf.end()); if (!initelse && !noreturnElse) variableValue.insert(varValueElse.begin(), varValueElse.end()); } } } // = { .. } else if (Token::simpleMatch(tok, "= {")) { // end token const Token *end = tok->next()->link(); // If address of variable is taken in the block then bail out if (var.isPointer() || var.isArray()) { if (Token::findmatch(tok->tokAt(2), "%varid%", end, var.declarationId())) return true; } else if (Token::findmatch(tok->tokAt(2), "& %varid%", end, var.declarationId())) { return true; } const Token *errorToken = nullptr; visitAstNodes(tok->next(), [&](const Token *child) { if (child->isUnaryOp("&")) return ChildrenToVisit::none; if (child->str() == "," || child->str() == "{" || child->isConstOp()) return ChildrenToVisit::op1_and_op2; if (child->str() == "." && Token::Match(child->astOperand1(), "%varid%", var.declarationId()) && child->astOperand2() && child->astOperand2()->str() == membervar) { errorToken = child; return ChildrenToVisit::done; } return ChildrenToVisit::none; }); if (errorToken) { uninitStructMemberError(errorToken->astOperand2(), errorToken->astOperand1()->str() + "." + membervar); return true; } // Skip block tok = end; continue; } // skip sizeof / offsetof if (Token::Match(tok, "sizeof|typeof|offsetof|decltype (")) tok = tok->next()->link(); // for/while.. else if (Token::Match(tok, "for|while (") || Token::simpleMatch(tok, "do {")) { const bool forwhile = Token::Match(tok, "for|while ("); // is variable initialized in for-head? if (forwhile && checkIfForWhileHead(tok->next(), var, tok->str() == "for", false, *alloc, membervar)) return true; // goto the { const Token *tok2 = forwhile ? tok->next()->link()->next() : tok->next(); if (tok2 && tok2->str() == "{") { const bool init = checkLoopBody(tok2, var, *alloc, membervar, (number_of_if > 0) || suppressErrors); // variable is initialized in the loop.. if (init) return true; // is variable used in for-head? bool initcond = false; if (!suppressErrors) { const Token *startCond = forwhile ? tok->next() : tok->next()->link()->tokAt(2); initcond = checkIfForWhileHead(startCond, var, false, bool(number_of_if == 0), *alloc, membervar); } // goto "}" tok = tok2->link(); // do-while => goto ")" if (!forwhile) { // Assert that the tokens are '} while (' if (!Token::simpleMatch(tok, "} while (")) { if (printDebug) reportError(tok,Severity::debug,"","assertion failed '} while ('"); break; } // Goto ')' tok = tok->linkAt(2); if (!tok) // bailout : invalid code / bad tokenizer break; if (initcond) // variable is initialized in while-condition return true; } } } // Unknown or unhandled inner scope else if (Token::simpleMatch(tok, ") {") || (Token::Match(tok, "%name% {") && tok->str() != "try")) { if (tok->str() == "struct" || tok->str() == "union") { tok = tok->linkAt(1); continue; } return true; } // bailout if there is ({ if (Token::simpleMatch(tok, "( {")) { return true; } // bailout if there is assembler code or setjmp if (Token::Match(tok, "asm|setjmp (")) { return true; } // bailout if there is a goto label if (Token::Match(tok, "[;{}] %name% :")) { return true; } if (tok->str() == "?") { if (!tok->astOperand2()) return true; const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); if (err) uninitvarError(tok, var.nameToken()->str(), *alloc); // Todo: skip expression if there is no error return true; } if (Token::Match(tok, "return|break|continue|throw|goto")) { if (noreturn) *noreturn = true; tok = tok->next(); while (tok && tok->str() != ";") { // variable is seen.. if (tok->varId() == var.declarationId()) { if (!membervar.empty()) { if (!suppressErrors && Token::Match(tok, "%name% . %name% ;|%cop%") && tok->strAt(2) == membervar) uninitStructMemberError(tok, tok->str() + "." + membervar); } // Use variable else if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) uninitvarError(tok, tok->str(), *alloc); return true; } else if (Token::Match(tok, "sizeof|typeof|offsetof|decltype (")) tok = tok->linkAt(1); else if (tok->str() == "?") { if (!tok->astOperand2()) return true; const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); if (err) uninitvarError(tok, var.nameToken()->str(), *alloc); return true; } tok = tok->next(); } return (noreturn == nullptr); } // variable is seen.. if (tok->varId() == var.declarationId()) { // calling function that returns uninit data through pointer.. if (var.isPointer() && Token::simpleMatch(tok->next(), "=")) { const Token *rhs = tok->next()->astOperand2(); while (rhs && rhs->isCast()) rhs = rhs->astOperand1(); if (rhs && Token::Match(rhs->previous(), "%name% (") && mSettings->library.returnuninitdata.count(rhs->previous()->str()) > 0U) { *alloc = NO_CTOR_CALL; continue; } } if (mTokenizer->isCPP() && var.isPointer() && (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True)) && Token::simpleMatch(tok->next(), "= new")) { *alloc = CTOR_CALL; // type has constructor(s) if (var.typeScope() && var.typeScope()->numConstructors > 0) return true; // standard or enum type: check if new initializes the allocated memory if (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType()) { // scalar new with initialization if (Token::Match(tok->next(), "= new %type% (")) return true; // array new if (Token::Match(tok->next(), "= new %type% [") && Token::simpleMatch(tok->linkAt(4), "] (")) return true; } continue; } if (!membervar.empty()) { if (isMemberVariableAssignment(tok, membervar)) { checkRhs(tok, var, *alloc, number_of_if, membervar); return true; } if (isMemberVariableUsage(tok, var.isPointer(), *alloc, membervar)) { uninitStructMemberError(tok, tok->str() + "." + membervar); return true; } if (Token::Match(tok->previous(), "[(,] %name% [,)]")) return true; if (Token::Match(tok->previous(), "= %var% . %var% ;") && membervar == tok->strAt(2)) return true; } else { // Use variable if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) { uninitvarError(tok, tok->str(), *alloc); return true; } else { if (tok->strAt(1) == "=") checkRhs(tok, var, *alloc, number_of_if, emptyString); // assume that variable is assigned return true; } } } } return false; } bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar) { const Token * const endpar = startparentheses->link(); if (Token::Match(startparentheses, "( ! %name% %oror%") && startparentheses->tokAt(2)->getValue(0)) suppressErrors = true; for (const Token *tok = startparentheses->next(); tok && tok != endpar; tok = tok->next()) { if (tok->varId() == var.declarationId()) { if (Token::Match(tok, "%name% . %name%")) { if (membervar.empty()) return true; if (tok->strAt(2) == membervar) { if (isMemberVariableAssignment(tok, membervar)) return true; if (!suppressErrors && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) uninitStructMemberError(tok, tok->str() + "." + membervar); } continue; } if (isVariableUsage(tok, var.isPointer(), alloc)) { if (suppressErrors) continue; uninitvarError(tok, tok->str(), alloc); } return true; } if (Token::Match(tok, "sizeof|decltype|offsetof (")) tok = tok->next()->link(); if ((!isuninit || !membervar.empty()) && tok->str() == "&&") suppressErrors = true; } return false; } bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors) { const Token *usetok = nullptr; assert(tok->str() == "{"); for (const Token * const end = tok->link(); tok != end; tok = tok->next()) { if (Token::Match(tok, "sizeof|typeof (")) { tok = tok->next()->link(); continue; } if (Token::Match(tok, "asm ( %str% ) ;")) return true; if (tok->varId() != var.declarationId()) continue; if (!membervar.empty()) { if (isMemberVariableAssignment(tok, membervar)) { bool assign = true; bool rhs = false; // Used for tracking if an ")" is inner or outer const Token *rpar = nullptr; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "=") rhs = true; // Look at inner expressions but not outer expressions if (!rpar && tok2->str() == "(") rpar = tok2->link(); else if (tok2->str() == ")") { // No rpar => this is an outer right parenthesis if (!rpar) break; if (rpar == tok2) rpar = nullptr; } if (tok2->str() == ";" || (!rpar && tok2->str() == ",")) break; if (rhs && tok2->varId() == var.declarationId() && isMemberVariableUsage(tok2, var.isPointer(), alloc, membervar)) { assign = false; break; } } if (assign) return true; } if (isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) usetok = tok; else if (Token::Match(tok->previous(), "[(,] %name% [,)]")) return true; } else { if (isVariableUsage(tok, var.isPointer(), alloc)) usetok = tok; else if (tok->strAt(1) == "=") { // Is var used in rhs? bool rhs = false; std::stack tokens; tokens.push(tok->next()->astOperand2()); while (!tokens.empty()) { const Token *t = tokens.top(); tokens.pop(); if (!t) continue; if (t->varId() == var.declarationId()) { // var is used in rhs rhs = true; break; } if (Token::simpleMatch(t->previous(),"sizeof (")) continue; tokens.push(t->astOperand1()); tokens.push(t->astOperand2()); } if (!rhs) return true; } else { return true; } } } if (!suppressErrors && usetok) { if (membervar.empty()) uninitvarError(usetok, usetok->str(), alloc); else uninitStructMemberError(usetok, usetok->str() + "." + membervar); return true; } return false; } void CheckUninitVar::checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar) { bool rhs = false; int indent = 0; while (nullptr != (tok = tok->next())) { if (tok->str() == "=") rhs = true; else if (rhs && tok->varId() == var.declarationId()) { if (membervar.empty() && isVariableUsage(tok, var.isPointer(), alloc)) uninitvarError(tok, tok->str(), alloc); else if (!membervar.empty() && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) uninitStructMemberError(tok, tok->str() + "." + membervar); else if (Token::Match(tok, "%var% =")) break; } else if (tok->str() == ";" || (indent==0 && tok->str() == ",")) break; else if (tok->str() == "(") ++indent; else if (tok->str() == ")") { if (indent == 0) break; --indent; } else if (tok->str() == "?" && tok->astOperand2()) { const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); if (err) uninitvarError(tok, var.nameToken()->str(), alloc); break; } else if (Token::simpleMatch(tok, "sizeof (")) tok = tok->next()->link(); } } bool CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const { if (!pointer && Token::Match(vartok, "%name% (")) return false; if (alloc == NO_ALLOC && (Token::Match(vartok->previous(), "return|delete %var% !!=") || (vartok->strAt(-1) == "]" && vartok->linkAt(-1)->strAt(-1) == "delete"))) return true; // Passing variable to typeof/__alignof__ if (Token::Match(vartok->tokAt(-3), "typeof|__alignof__ ( * %name%")) return false; // Accessing Rvalue member using "." or "->" if (Token::Match(vartok->previous(), "!!& %var% .")) { // Is struct member passed to function? if (!pointer) return false; if (alloc != CTOR_CALL && Token::Match(vartok, "%name% . %name% (")) return true; bool assignment = false; const Token* parent = vartok->astParent(); while (parent) { if (parent->str() == "=") { assignment = true; break; } if (alloc != NO_ALLOC && parent->str() == "(") { if (!mSettings->library.isFunctionConst(parent->strAt(-1), true)) { assignment = true; break; } } parent = parent->astParent(); } if (!assignment) return true; } // Passing variable to function.. { bool unknown = false; const Token *possibleParent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown); if (possibleParent && possibleParent->isUnaryOp("*")) { while (possibleParent && possibleParent->isUnaryOp("*")) possibleParent = getAstParentSkipPossibleCastAndAddressOf(possibleParent, &unknown); if (possibleParent && Token::Match(possibleParent->previous(), "decltype|sizeof (")) return false; } if (Token::Match(possibleParent, "[(,]")) { if (unknown) return false; // TODO: output some info message? const int use = isFunctionParUsage(vartok, pointer, alloc, indirect); if (use >= 0) return (use>0); } else if (!pointer && Token::simpleMatch(possibleParent, "=") && vartok->astParent()->str() == "&") { return false; } } { const Token *parent = vartok->astParent(); while (parent && parent->isCast()) parent = parent->astParent(); while (parent && parent->str() == ",") parent = parent->astParent(); if (Token::simpleMatch(parent, "{")) return true; } if (Token::Match(vartok->previous(), "++|--|%cop%")) { if (mTokenizer->isCPP() && alloc == ARRAY && Token::Match(vartok->tokAt(-4), "& %var% =|( *")) return false; if (isLikelyStreamRead(mTokenizer->isCPP(), vartok->previous())) return false; if (mTokenizer->isCPP() && Token::simpleMatch(vartok->previous(), "<<")) { const Token* tok2 = vartok->previous(); // Looks like stream operator, but could also initialize the variable. Check lhs. do { tok2 = tok2->astOperand1(); } while (Token::simpleMatch(tok2, "<<")); if (tok2 && tok2->strAt(-1) == "::") tok2 = tok2->previous(); if (tok2 && (Token::simpleMatch(tok2->previous(), "std ::") || (tok2->variable() && tok2->variable()->isStlType()) || tok2->isStandardType() || tok2->isEnumType())) return true; const Variable *var = vartok->tokAt(-2)->variable(); return (var && (var->typeStartToken()->isStandardType() || var->typeStartToken()->isEnumType())); } // is there something like: ; "*((&var ..expr.. =" => the variable is assigned if (vartok->previous()->str() == "&" && !vartok->previous()->astOperand2()) return false; // bailout to avoid fp for 'int x = 2 + x();' where 'x()' is a unseen preprocessor macro (seen in linux) if (!pointer && vartok->next() && vartok->next()->str() == "(") return false; if (vartok->previous()->str() != "&" || !Token::Match(vartok->tokAt(-2), "[(,=?:]")) { if (alloc != NO_ALLOC && vartok->previous()->str() == "*") { // TestUninitVar::isVariableUsageDeref() const Token *parent = vartok->previous()->astParent(); if (parent && parent->str() == "=" && parent->astOperand1() == vartok->previous()) return false; if (vartok->variable() && vartok->variable()->dimensions().size() >= 2) return false; return true; } return alloc == NO_ALLOC; } } if (alloc == NO_ALLOC && Token::Match(vartok->previous(), "%assign% %name% %cop%|;|)")) { // taking reference? const Token *prev = vartok->tokAt(-2); while (Token::Match(prev, "%name%|*")) prev = prev->previous(); if (!Token::simpleMatch(prev, "&")) return true; } bool unknown = false; if (pointer && alloc == NO_ALLOC && CheckNullPointer::isPointerDeRef(vartok, unknown, mSettings)) { // function parameter? bool functionParameter = false; if (Token::Match(vartok->tokAt(-2), "%name% (") || vartok->previous()->str() == ",") functionParameter = true; // if this is not a function parameter report this dereference as variable usage if (!functionParameter) return true; } else if (alloc != NO_ALLOC && Token::Match(vartok, "%var% [")) { const Token *parent = vartok->next()->astParent(); while (Token::Match(parent, "[|.")) parent = parent->astParent(); if (Token::simpleMatch(parent, "&") && !parent->astOperand2()) return false; if (parent && Token::Match(parent->previous(), "if|while|switch (")) return true; if (Token::Match(parent, "[=,(]")) return false; return true; } if (mTokenizer->isCPP() && Token::simpleMatch(vartok->next(), "<<")) { return false; } if (alloc == NO_ALLOC && vartok->next() && vartok->next()->isOp() && !vartok->next()->isAssignmentOp()) return true; if (alloc == NO_ALLOC && vartok->next() && vartok->next()->isAssignmentOp() && vartok->next()->str() != "=") return true; if (vartok->strAt(1) == "]") return true; return false; } /*** * Is function parameter "used" so a "usage of uninitialized variable" can * be written? If parameter is passed "by value" then it is "used". If it * is passed "by reference" then it is not necessarily "used". * @return -1 => unknown 0 => not used 1 => used */ int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const { bool unknown = false; const Token *parent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown); if (unknown || !Token::Match(parent, "[(,]")) return -1; // locate start parentheses in function call.. int argumentNumber = 0; const Token *start = vartok; while (start && !Token::Match(start, "[;{}(]")) { if (start->str() == ")") start = start->link(); else if (start->str() == ",") ++argumentNumber; start = start->previous(); } if (!start) return -1; if (Token::simpleMatch(start->link(), ") {") && Token::Match(start->previous(), "if|for|while|switch")) return (!pointer || alloc == NO_ALLOC); // is this a function call? if (Token::Match(start->previous(), "%name% (")) { const bool address(vartok->previous()->str() == "&"); const bool array(vartok->variable() && vartok->variable()->isArray()); // check how function handle uninitialized data arguments.. const Function *func = start->previous()->function(); if (func) { const Variable *arg = func->getArgumentVar(argumentNumber); if (arg) { const Token *argStart = arg->typeStartToken(); if (!address && !array && Token::Match(argStart, "%type% %name%| [,)]")) return 1; if (pointer && !address && alloc == NO_ALLOC && Token::Match(argStart, "%type% * %name% [,)]")) return 1; while (argStart->previous() && argStart->previous()->isName()) argStart = argStart->previous(); if (Token::Match(argStart, "const %type% & %name% [,)]")) { // If it's a record it's ok to pass a partially uninitialized struct. if (vartok->variable() && vartok->variable()->valueType() && vartok->variable()->valueType()->type == ValueType::Type::RECORD) return -1; return 1; } if ((pointer || address) && alloc == NO_ALLOC && Token::Match(argStart, "const struct| %type% * %name% [,)]")) return 1; if ((pointer || address) && Token::Match(argStart, "const %type% %name% [") && Token::Match(argStart->linkAt(3), "] [,)]")) return 1; } } else if (Token::Match(start->previous(), "if|while|for")) { // control-flow statement reading the variable "by value" return alloc == NO_ALLOC; } else { const bool isnullbad = mSettings->library.isnullargbad(start->previous(), argumentNumber + 1); if (indirect == 0 && pointer && !address && isnullbad && alloc == NO_ALLOC) return 1; const bool isuninitbad = mSettings->library.isuninitargbad(start->previous(), argumentNumber + 1, indirect); if (alloc != NO_ALLOC) return isnullbad && isuninitbad; return isuninitbad && (!address || isnullbad); } } // unknown return -1; } bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const { if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar) { if (Token::Match(tok->tokAt(3), "[=.[]")) return true; else if (Token::Match(tok->tokAt(-2), "[(,=] &")) return true; else if (isLikelyStreamRead(mTokenizer->isCPP(), tok->previous())) return true; else if ((tok->previous() && tok->previous()->isConstOp()) || Token::Match(tok->previous(), "[|=")) ; // member variable usage else if (tok->tokAt(3)->isConstOp()) ; // member variable usage else if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") && 1 == isFunctionParUsage(tok, false, NO_ALLOC)) { return false; } else return true; } else if (tok->strAt(1) == "=") return true; else if (Token::Match(tok, "%var% . %name% (")) { const Token *ftok = tok->tokAt(2); if (!ftok->function() || !ftok->function()->isConst()) // TODO: Try to determine if membervar is assigned in method return true; } else if (tok->strAt(-1) == "&") { if (Token::Match(tok->tokAt(-2), "[(,] & %name%")) { // locate start parentheses in function call.. int argumentNumber = 0; const Token *ftok = tok; while (ftok && !Token::Match(ftok, "[;{}(]")) { if (ftok->str() == ")") ftok = ftok->link(); else if (ftok->str() == ",") ++argumentNumber; ftok = ftok->previous(); } // is this a function call? ftok = ftok ? ftok->previous() : nullptr; if (Token::Match(ftok, "%name% (")) { // check how function handle uninitialized data arguments.. const Function *function = ftok->function(); if (!function && mSettings) { // Function definition not seen, check if direction is specified in the library configuration const Library::ArgumentChecks::Direction argDirection = mSettings->library.getArgDirection(ftok, 1 + argumentNumber); if (argDirection == Library::ArgumentChecks::Direction::DIR_IN) return false; else if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT) return true; } const Variable *arg = function ? function->getArgumentVar(argumentNumber) : nullptr; const Token *argStart = arg ? arg->typeStartToken() : nullptr; while (argStart && argStart->previous() && argStart->previous()->isName()) argStart = argStart->previous(); if (Token::Match(argStart, "const struct| %type% * const| %name% [,)]")) return false; } else if (ftok && Token::simpleMatch(ftok->previous(), "= * (")) return false; } return true; } return false; } bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const { if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") && tok->strAt(2) == membervar) { const int use = isFunctionParUsage(tok, isPointer, alloc); if (use == 1) return true; } if (isMemberVariableAssignment(tok, membervar)) return false; if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference())) { const Token *parent = tok->next()->astParent(); if (parent && parent->isUnaryOp("&")) return false; return true; } else if (!isPointer && Token::Match(tok->previous(), "[(,] %name% [,)]") && isVariableUsage(tok, isPointer, alloc)) return true; else if (!isPointer && Token::Match(tok->previous(), "= %name% ;")) return true; // = *(&var); else if (!isPointer && Token::simpleMatch(tok->astParent(),"&") && Token::simpleMatch(tok->astParent()->astParent(),"*") && Token::Match(tok->astParent()->astParent()->astParent(), "= * (| &") && tok->astParent()->astParent()->astParent()->astOperand2() == tok->astParent()->astParent()) return true; else if (mSettings->experimental && !isPointer && Token::Match(tok->tokAt(-2), "[(,] & %name% [,)]") && isVariableUsage(tok, isPointer, alloc)) return true; return false; } void CheckUninitVar::uninitstringError(const Token *tok, const std::string &varname, bool strncpy_) { reportError(tok, Severity::error, "uninitstring", "$symbol:" + varname + "\nDangerous usage of '$symbol'" + (strncpy_ ? " (strncpy doesn't always null-terminate it)." : " (not null-terminated)."), CWE676, false); } void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "uninitdata", "$symbol:" + varname + "\nMemory is allocated but not initialized: $symbol", CWE908, false); } void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath) { errorPath.emplace_back(tok, ""); reportError(errorPath, Severity::error, "uninitvar", "$symbol:" + varname + "\nUninitialized variable: $symbol", CWE908, false); // reportError(tok, Severity::error, "uninitvar", "$symbol:" + varname + "\nUninitialized variable: $symbol", CWE908, false); } void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername) { reportError(tok, Severity::error, "uninitStructMember", "$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE908, false); } static bool isLeafDot(const Token* tok) { if (!tok) return false; const Token * parent = tok->astParent(); if (!Token::simpleMatch(parent, ".")) return false; if (parent->astOperand2() == tok) return true; return isLeafDot(parent); } void CheckUninitVar::valueFlowUninit() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // check every executable scope for (const Scope &scope : symbolDatabase->scopeList) { if (!scope.isExecutable()) continue; for (const Token* tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof (")) { tok = tok->linkAt(1); continue; } if (!tok->variable() && !tok->isUnaryOp("*")) continue; auto v = std::find_if(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isUninitValue)); if (v == tok->values().end()) continue; if (v->isInconclusive()) continue; if (v->indirect > 1 || v->indirect < 0) continue; bool uninitderef = false; if (tok->variable()) { if (!isVariableUsage(tok, tok->variable()->isPointer(), tok->variable()->isArray() ? ARRAY : NO_ALLOC, v->indirect)) continue; bool unknown; const bool deref = CheckNullPointer::isPointerDeRef(tok, unknown, mSettings); if (v->indirect == 1 && !deref) continue; uninitderef = deref && v->indirect == 0; const bool isleaf = isLeafDot(tok) || uninitderef; if (Token::Match(tok->astParent(), ". %var%") && !isleaf) continue; } if (!Token::Match(tok->astParent(), ". %name% (") && !uninitderef && isVariableChanged(tok, v->indirect, mSettings, mTokenizer->isCPP())) continue; uninitvarError(tok, tok->expressionString(), v->errorPath); const Token * nextTok = tok; while (Token::simpleMatch(nextTok->astParent(), ".")) nextTok = nextTok->astParent(); nextTok = nextAfterAstRightmostLeaf(nextTok); if (nextTok == scope.bodyEnd) break; tok = nextTok ? nextTok : tok; } } } std::string CheckUninitVar::MyFileInfo::toString() const { return CTU::toString(unsafeUsage); } Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { const CheckUninitVar checker(tokenizer, settings, nullptr); return checker.getFileInfo(); } static bool isVariableUsage(const Check *check, const Token *vartok, MathLib::bigint *value) { (void)value; const CheckUninitVar *c = dynamic_cast(check); return c && c->isVariableUsage(vartok, true, CheckUninitVar::Alloc::ARRAY); } Check::FileInfo *CheckUninitVar::getFileInfo() const { const std::list &unsafeUsage = CTU::getUnsafeUsage(mTokenizer, mSettings, this, ::isVariableUsage); if (unsafeUsage.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeUsage = unsafeUsage ; return fileInfo; } Check::FileInfo * CheckUninitVar::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { const std::list &unsafeUsage = CTU::loadUnsafeUsageListFromXml(xmlElement); if (unsafeUsage.empty()) return nullptr; MyFileInfo *fileInfo = new MyFileInfo; fileInfo->unsafeUsage = unsafeUsage; return fileInfo; } bool CheckUninitVar::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { if (!ctu) return false; bool foundErrors = false; (void)settings; // This argument is unused const std::map> callsMap = ctu->getCallsMap(); for (Check::FileInfo *fi1 : fileInfo) { const MyFileInfo *fi = dynamic_cast(fi1); if (!fi) continue; for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeUsage) { const CTU::FileInfo::FunctionCall *functionCall = nullptr; const std::list &locationList = ctu->getErrorPath(CTU::FileInfo::InvalidValueType::uninit, unsafeUsage, callsMap, "Using argument ARG", &functionCall, false); if (locationList.empty()) continue; const ErrorLogger::ErrorMessage errmsg(locationList, emptyString, Severity::error, "Using argument " + unsafeUsage.myArgumentName + " that points at uninitialized variable " + functionCall->callArgumentExpression, "ctuuninitvar", CWE908, false); errorLogger.reportErr(errmsg); foundErrors = true; } } return foundErrors; } cppcheck-1.90/lib/checkuninitvar.h000066400000000000000000000136101357737443600172030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkuninitvarH #define checkuninitvarH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include "ctu.h" #include #include class ErrorLogger; class Scope; class Settings; class Token; class Tokenizer; class Variable; struct VariableValue { explicit VariableValue(MathLib::bigint val = 0) : value(val), notEqual(false) {} MathLib::bigint value; bool notEqual; }; /// @addtogroup Checks /// @{ /** @brief Checking for uninitialized variables */ class CPPCHECKLIB CheckUninitVar : public Check { public: /** @brief This constructor is used when registering the CheckUninitVar */ CheckUninitVar() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckUninitVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger); checkUninitVar.check(); checkUninitVar.valueFlowUninit(); } /** Check for uninitialized variables */ void check(); void checkScope(const Scope* scope, const std::set &arrayTypeDefs); void checkStruct(const Token *tok, const Variable &structvar); enum Alloc { NO_ALLOC, NO_CTOR_CALL, CTOR_CALL, ARRAY }; bool checkScopeForVariable(const Token *tok, const Variable& var, bool* const possibleInit, bool* const noreturn, Alloc* const alloc, const std::string &membervar, std::map variableValue); bool checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar); bool checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors); void checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar); bool isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const; int isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const; bool isMemberVariableAssignment(const Token *tok, const std::string &membervar) const; bool isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const; /** ValueFlow-based checking for uninitialized variables */ void valueFlowUninit(); /* data for multifile checking */ class MyFileInfo : public Check::FileInfo { public: /** function arguments that data are unconditionally read */ std::list unsafeUsage; /** Convert MyFileInfo data into xml string */ std::string toString() const OVERRIDE; }; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; void uninitstringError(const Token *tok, const std::string &varname, bool strncpy_); void uninitdataError(const Token *tok, const std::string &varname); void uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath); void uninitvarError(const Token *tok, const std::string &varname) { ErrorPath errorPath; uninitvarError(tok, varname, errorPath); } void uninitvarError(const Token *tok, const std::string &varname, Alloc alloc) { if (alloc == NO_CTOR_CALL || alloc == CTOR_CALL) uninitdataError(tok, varname); else uninitvarError(tok, varname); } void uninitStructMemberError(const Token *tok, const std::string &membername); private: Check::FileInfo *getFileInfo() const; bool isUnsafeFunction(const Scope *scope, int argnr, const Token **tok) const; void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckUninitVar c(nullptr, settings, errorLogger); // error c.uninitstringError(nullptr, "varname", true); c.uninitdataError(nullptr, "varname"); c.uninitvarError(nullptr, "varname"); c.uninitStructMemberError(nullptr, "a.b"); } static std::string myName() { return "Uninitialized variables"; } std::string classInfo() const OVERRIDE { return "Uninitialized variables\n" "- using uninitialized local variables\n" "- using allocated data before it has been initialized\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkuninitvarH cppcheck-1.90/lib/checkunusedfunctions.cpp000066400000000000000000000426511357737443600207620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkunusedfunctions.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class CheckUnusedFunctions CheckUnusedFunctions::instance; static const struct CWE CWE561(561U); // Dead Code //--------------------------------------------------------------------------- // FUNCTION USAGE - Check for unused functions etc //--------------------------------------------------------------------------- void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings) { const bool doMarkup = settings->library.markupFile(FileName); const SymbolDatabase* symbolDatabase = tokenizer.getSymbolDatabase(); // Function declarations.. for (const Scope* scope : symbolDatabase->functionScopes) { const Function* func = scope->function; if (!func || !func->token || scope->bodyStart->fileIndex() != 0) continue; // Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor)) if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction || func->isOperator()) continue; // Don't care about templates if (tokenizer.isCPP()) { const Token *retDef = func->retDef; while (retDef && retDef->isName()) retDef = retDef->previous(); if (retDef && retDef->str() == ">") continue; } mFunctionDecl.emplace_back(func); FunctionUsage &usage = mFunctions[func->name()]; if (!usage.lineNumber) usage.lineNumber = func->token->linenr(); // No filename set yet.. if (usage.filename.empty()) { usage.filename = tokenizer.list.getSourceFilePath(); } // Multiple files => filename = "+" else if (usage.filename != tokenizer.list.getSourceFilePath()) { //func.filename = "+"; usage.usedOtherFile |= usage.usedSameFile; } } // Function usage.. const Token *lambdaEndToken = nullptr; for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok == lambdaEndToken) lambdaEndToken = nullptr; else if (!lambdaEndToken && tok->str() == "[") lambdaEndToken = findLambdaEndToken(tok); // parsing of library code to find called functions if (settings->library.isexecutableblock(FileName, tok->str())) { const Token * markupVarToken = tok->tokAt(settings->library.blockstartoffset(FileName)); // not found if (!markupVarToken) continue; int scope = 0; bool start = true; // find all function calls in library code (starts with '(', not if or while etc) while ((scope || start) && markupVarToken) { if (markupVarToken->str() == settings->library.blockstart(FileName)) { scope++; if (start) { start = false; } } else if (markupVarToken->str() == settings->library.blockend(FileName)) scope--; else if (!settings->library.iskeyword(FileName, markupVarToken->str())) { mFunctionCalls.insert(markupVarToken->str()); if (mFunctions.find(markupVarToken->str()) != mFunctions.end()) mFunctions[markupVarToken->str()].usedOtherFile = true; else if (markupVarToken->next()->str() == "(") { FunctionUsage &func = mFunctions[markupVarToken->str()]; func.filename = tokenizer.list.getSourceFilePath(); if (func.filename.empty() || func.filename == "+") func.usedOtherFile = true; else func.usedSameFile = true; } } markupVarToken = markupVarToken->next(); } } if (!doMarkup // only check source files && settings->library.isexporter(tok->str()) && tok->next() != nullptr) { const Token * propToken = tok->next(); while (propToken && propToken->str() != ")") { if (settings->library.isexportedprefix(tok->str(), propToken->str())) { const Token* nextPropToken = propToken->next(); const std::string& value = nextPropToken->str(); if (mFunctions.find(value) != mFunctions.end()) { mFunctions[value].usedOtherFile = true; } mFunctionCalls.insert(value); } if (settings->library.isexportedsuffix(tok->str(), propToken->str())) { const Token* prevPropToken = propToken->previous(); const std::string& value = prevPropToken->str(); if (value != ")" && mFunctions.find(value) != mFunctions.end()) { mFunctions[value].usedOtherFile = true; } mFunctionCalls.insert(value); } propToken = propToken->next(); } } if (doMarkup && settings->library.isimporter(FileName, tok->str()) && tok->next()) { const Token * propToken = tok->next(); if (propToken->next()) { propToken = propToken->next(); while (propToken && propToken->str() != ")") { const std::string& value = propToken->str(); if (!value.empty()) { mFunctions[value].usedOtherFile = true; mFunctionCalls.insert(value); break; } propToken = propToken->next(); } } } if (settings->library.isreflection(tok->str())) { const int argIndex = settings->library.reflectionArgument(tok->str()); if (argIndex >= 0) { const Token * funcToken = tok->next(); int index = 0; std::string value; while (funcToken) { if (funcToken->str()==",") { if (++index == argIndex) break; value.clear(); } else value += funcToken->str(); funcToken = funcToken->next(); } if (index == argIndex) { value = value.substr(1, value.length() - 2); mFunctions[value].usedOtherFile = true; mFunctionCalls.insert(value); } } } const Token *funcname = nullptr; if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% (")) { funcname = tok; } else if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% <") && Token::simpleMatch(tok->linkAt(1), "> (")) { funcname = tok; } else if (Token::Match(tok, "[;{}.,()[=+-/|!?:]")) { funcname = tok->next(); if (funcname && funcname->str() == "&") funcname = funcname->next(); if (funcname && funcname->str() == "::") funcname = funcname->next(); while (Token::Match(funcname, "%name% :: %name%")) funcname = funcname->tokAt(2); if (!Token::Match(funcname, "%name% [(),;]:}>]")) continue; } if (!funcname) continue; // funcname ( => Assert that the end parentheses isn't followed by { if (Token::Match(funcname, "%name% (|<")) { const Token *ftok = funcname->next(); if (ftok->str() == "<") ftok = ftok->link(); if (Token::Match(ftok->linkAt(1), ") const|throw|{")) funcname = nullptr; } if (funcname) { FunctionUsage &func = mFunctions[ funcname->str()]; const std::string& called_from_file = tokenizer.list.getSourceFilePath(); if (func.filename.empty() || func.filename == "+" || func.filename != called_from_file) func.usedOtherFile = true; else func.usedSameFile = true; mFunctionCalls.insert(funcname->str()); } } } static bool isOperatorFunction(const std::string & funcName) { /* Operator functions are invalid function names for C, so no need to check * this in here. As result the returned error function might be incorrect. * * List of valid operators can be found at: * http://en.cppreference.com/w/cpp/language/operators * * Conversion functions must be a member function (at least for gcc), so no * need to cover them for unused functions. * * To speed up the comparison, not the whole list of operators is used. * Instead only the character after the operator prefix is checked to be a * none alpa numeric value, but the '_', to cover function names like * "operator_unused". In addition the following valid operators are checked: * - new * - new[] * - delete * - delete[] */ const std::string operatorPrefix = "operator"; if (funcName.compare(0, operatorPrefix.length(), operatorPrefix) != 0) { return false; } // Taking care of funcName == "operator", which is no valid operator if (funcName.length() == operatorPrefix.length()) { return false; } const char firstOperatorChar = funcName[operatorPrefix.length()]; if (firstOperatorChar == '_') { return false; } if (!std::isalnum(firstOperatorChar)) { return true; } const std::vector additionalOperators = { "new", "new[]", "delete", "delete[]" }; return std::find(additionalOperators.begin(), additionalOperators.end(), funcName.substr(operatorPrefix.length())) != additionalOperators.end();; } bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings& settings) { bool errors = false; for (std::map::const_iterator it = mFunctions.begin(); it != mFunctions.end(); ++it) { const FunctionUsage &func = it->second; if (func.usedOtherFile || func.filename.empty()) continue; if (it->first == "main" || (settings.isWindowsPlatform() && (it->first == "WinMain" || it->first == "_tmain")) || it->first == "if") continue; if (!func.usedSameFile) { if (isOperatorFunction(it->first)) continue; std::string filename; if (func.filename != "+") filename = func.filename; unusedFunctionError(errorLogger, filename, func.lineNumber, it->first); errors = true; } else if (! func.usedOtherFile) { /** @todo add error message "function is only used in it can be static" */ /* std::ostringstream errmsg; errmsg << "The function '" << it->first << "' is only used in the file it was declared in so it should have local linkage."; mErrorLogger->reportErr( errmsg.str() ); errors = true; */ } } return errors; } void CheckUnusedFunctions::unusedFunctionError(ErrorLogger * const errorLogger, const std::string &filename, unsigned int lineNumber, const std::string &funcname) { std::list locationList; if (!filename.empty()) { ErrorLogger::ErrorMessage::FileLocation fileLoc; fileLoc.setfile(filename); fileLoc.line = lineNumber; locationList.push_back(fileLoc); } const ErrorLogger::ErrorMessage errmsg(locationList, emptyString, Severity::style, "$symbol:" + funcname + "\nThe function '$symbol' is never used.", "unusedFunction", CWE561, false); if (errorLogger) errorLogger->reportErr(errmsg); else reportError(errmsg); } Check::FileInfo *CheckUnusedFunctions::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { if (!settings->isEnabled(Settings::UNUSED_FUNCTION)) return nullptr; if (settings->jobs == 1 && settings->buildDir.empty()) instance.parseTokens(*tokenizer, tokenizer->list.getFiles().front().c_str(), settings); return nullptr; } bool CheckUnusedFunctions::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) { (void)ctu; (void)fileInfo; return check(&errorLogger, settings); } CheckUnusedFunctions::FunctionDecl::FunctionDecl(const Function *f) : functionName(f->name()), lineNumber(f->token->linenr()) { } std::string CheckUnusedFunctions::analyzerInfo() const { std::ostringstream ret; for (const FunctionDecl &functionDecl : mFunctionDecl) { ret << " \n"; } for (const std::string &fc : mFunctionCalls) { ret << " \n"; } return ret.str(); } namespace { struct Location { Location() : lineNumber(0) {} Location(const std::string &f, const int l) : fileName(f), lineNumber(l) {} std::string fileName; int lineNumber; }; } void CheckUnusedFunctions::analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir) { std::map decls; std::set calls; const std::string filesTxt(buildDir + "/files.txt"); std::ifstream fin(filesTxt.c_str()); std::string filesTxtLine; while (std::getline(fin, filesTxtLine)) { const std::string::size_type firstColon = filesTxtLine.find(':'); if (firstColon == std::string::npos) continue; const std::string::size_type secondColon = filesTxtLine.find(':', firstColon+1); if (secondColon == std::string::npos) continue; const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon); const std::string sourcefile = filesTxtLine.substr(secondColon+1); tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); if (error != tinyxml2::XML_SUCCESS) continue; const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); if (rootNode == nullptr) continue; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "FileInfo") != 0) continue; const char *checkattr = e->Attribute("check"); if (checkattr == nullptr || std::strcmp(checkattr,"CheckUnusedFunctions") != 0) continue; for (const tinyxml2::XMLElement *e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) { const char* functionName = e2->Attribute("functionName"); if (functionName == nullptr) continue; if (std::strcmp(e2->Name(),"functioncall") == 0) { calls.insert(functionName); continue; } else if (std::strcmp(e2->Name(),"functiondecl") == 0) { const char* lineNumber = e2->Attribute("lineNumber"); if (lineNumber) decls[functionName] = Location(sourcefile, std::atoi(lineNumber)); } } } } for (std::map::const_iterator decl = decls.begin(); decl != decls.end(); ++decl) { const std::string &functionName = decl->first; if (functionName == "main" || functionName == "WinMain" || functionName == "_tmain" || functionName == "if") continue; if (calls.find(functionName) == calls.end() && !isOperatorFunction(functionName)) { const Location &loc = decl->second; unusedFunctionError(errorLogger, loc.fileName, loc.lineNumber, functionName); } } } cppcheck-1.90/lib/checkunusedfunctions.h000066400000000000000000000102721357737443600204210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkunusedfunctionsH #define checkunusedfunctionsH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include #include #include #include class ErrorLogger; class Function; class Settings; class Tokenizer; /// @addtogroup Checks /** @brief Check for functions never called */ /// @{ class CPPCHECKLIB CheckUnusedFunctions : public Check { public: /** @brief This constructor is used when registering the CheckUnusedFunctions */ CheckUnusedFunctions() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckUnusedFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } static void clear() { instance.mFunctions.clear(); instance.mFunctionCalls.clear(); } // Parse current tokens and determine.. // * Check what functions are used // * What functions are declared void parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings); // Return true if an error is reported. bool check(ErrorLogger * const errorLogger, const Settings& settings); /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const OVERRIDE; /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) OVERRIDE; static CheckUnusedFunctions instance; std::string analyzerInfo() const; /** @brief Combine and analyze all analyzerInfos for all TUs */ static void analyseWholeProgram(ErrorLogger * const errorLogger, const std::string &buildDir); private: void getErrorMessages(ErrorLogger *errorLogger, const Settings * /*settings*/) const OVERRIDE { CheckUnusedFunctions::unusedFunctionError(errorLogger, emptyString, 0, "funcName"); } void runChecks(const Tokenizer * /*tokenizer*/, const Settings * /*settings*/, ErrorLogger * /*errorLogger*/) OVERRIDE {} /** * Dummy implementation, just to provide error for --errorlist */ static void unusedFunctionError(ErrorLogger * const errorLogger, const std::string &filename, unsigned int lineNumber, const std::string &funcname); static std::string myName() { return "Unused functions"; } std::string classInfo() const OVERRIDE { return "Check for functions that are never called\n"; } class CPPCHECKLIB FunctionUsage { public: FunctionUsage() : lineNumber(0), usedSameFile(false), usedOtherFile(false) { } std::string filename; unsigned int lineNumber; bool usedSameFile; bool usedOtherFile; }; std::map mFunctions; class CPPCHECKLIB FunctionDecl { public: explicit FunctionDecl(const Function *f); std::string functionName; unsigned int lineNumber; }; std::list mFunctionDecl; std::set mFunctionCalls; }; /// @} //--------------------------------------------------------------------------- #endif // checkunusedfunctionsH cppcheck-1.90/lib/checkunusedvar.cpp000066400000000000000000001621451357737443600175430ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "checkunusedvar.h" #include "astutils.h" #include "errorlogger.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include "valueflow.h" #include #include #include #include #include #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckUnusedVar instance; } static const struct CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') static const struct CWE CWE665(665U); // Improper Initialization /** * @brief This class is used create a list of variables within a function. */ class Variables { public: enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer, none }; /** Store information about variable usage */ class VariableUsage { public: explicit VariableUsage(const Variable *var = nullptr, VariableType type = standard, bool read = false, bool write = false, bool modified = false, bool allocateMemory = false) : _var(var), _lastAccess(var ? var->nameToken() : nullptr), mType(type), _read(read), _write(write), _modified(modified), _allocateMemory(allocateMemory) { } /** variable is used.. set both read+write */ void use() { _read = true; _write = true; } /** is variable unused? */ bool unused() const { return (!_read && !_write); } std::set _aliases; std::set _assignments; const Variable* _var; const Token* _lastAccess; VariableType mType; bool _read; bool _write; bool _modified; // read/modify/write bool _allocateMemory; }; void clear() { mVarUsage.clear(); } const std::map &varUsage() const { return mVarUsage; } void addVar(const Variable *var, VariableType type, bool write_); void allocateMemory(unsigned int varid, const Token* tok); void read(unsigned int varid, const Token* tok); void readAliases(unsigned int varid, const Token* tok); void readAll(unsigned int varid, const Token* tok); void write(unsigned int varid, const Token* tok); void writeAliases(unsigned int varid, const Token* tok); void writeAll(unsigned int varid, const Token* tok); void use(unsigned int varid, const Token* tok); void modified(unsigned int varid, const Token* tok); VariableUsage *find(unsigned int varid); void alias(unsigned int varid1, unsigned int varid2, bool replace); void erase(unsigned int varid) { mVarUsage.erase(varid); } void eraseAliases(unsigned int varid); void eraseAll(unsigned int varid); void clearAliases(unsigned int varid); private: std::map mVarUsage; }; /** * Alias the 2 given variables. Either replace the existing aliases if * they exist or merge them. You would replace an existing alias when this * assignment is in the same scope as the previous assignment. You might * merge the aliases when this assignment is in a different scope from the * previous assignment depending on the relationship of the 2 scopes. */ void Variables::alias(unsigned int varid1, unsigned int varid2, bool replace) { VariableUsage *var1 = find(varid1); VariableUsage *var2 = find(varid2); if (!var1 || !var2) return; // alias to self if (varid1 == varid2) { var1->use(); return; } if (replace) { // remove var1 from all aliases for (std::set::const_iterator i = var1->_aliases.begin(); i != var1->_aliases.end(); ++i) { VariableUsage *temp = find(*i); if (temp) temp->_aliases.erase(var1->_var->declarationId()); } // remove all aliases from var1 var1->_aliases.clear(); } // var1 gets all var2s aliases for (std::set::const_iterator i = var2->_aliases.begin(); i != var2->_aliases.end(); ++i) { if (*i != varid1) var1->_aliases.insert(*i); } // var2 is an alias of var1 var2->_aliases.insert(varid1); var1->_aliases.insert(varid2); if (var2->mType == Variables::pointer) { var2->_read = true; } } void Variables::clearAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { // remove usage from all aliases std::set::const_iterator i; for (i = usage->_aliases.begin(); i != usage->_aliases.end(); ++i) { VariableUsage *temp = find(*i); if (temp) temp->_aliases.erase(usage->_var->declarationId()); } // remove all aliases from usage usage->_aliases.clear(); } } void Variables::eraseAliases(unsigned int varid) { VariableUsage *usage = find(varid); if (usage) { for (std::set::const_iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) erase(*aliases); } } void Variables::eraseAll(unsigned int varid) { eraseAliases(varid); erase(varid); } void Variables::addVar(const Variable *var, VariableType type, bool write_) { if (var->declarationId() > 0) { mVarUsage.insert(std::make_pair(var->declarationId(), VariableUsage(var, type, false, write_, false))); } } void Variables::allocateMemory(unsigned int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { usage->_allocateMemory = true; usage->_lastAccess = tok; } } void Variables::read(unsigned int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { usage->_read = true; if (tok) usage->_lastAccess = tok; } } void Variables::readAliases(unsigned int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { for (std::set::iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) { aliased->_read = true; aliased->_lastAccess = tok; } } } } void Variables::readAll(unsigned int varid, const Token* tok) { read(varid, tok); readAliases(varid, tok); } void Variables::write(unsigned int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { usage->_write = true; if (!usage->_var->isStatic() && !Token::simpleMatch(tok->next(), "= 0 ;")) usage->_read = false; usage->_lastAccess = tok; } } void Variables::writeAliases(unsigned int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { for (std::set::const_iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) { aliased->_write = true; aliased->_lastAccess = tok; } } } } void Variables::writeAll(unsigned int varid, const Token* tok) { write(varid, tok); writeAliases(varid, tok); } void Variables::use(unsigned int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { usage->use(); usage->_lastAccess = tok; for (std::set::const_iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) { aliased->use(); aliased->_lastAccess = tok; } } } } void Variables::modified(unsigned int varid, const Token* tok) { VariableUsage *usage = find(varid); if (usage) { if (!usage->_var->isStatic()) usage->_read = false; usage->_modified = true; usage->_lastAccess = tok; for (std::set::const_iterator aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) { VariableUsage *aliased = find(*aliases); if (aliased) { aliased->_modified = true; aliased->_lastAccess = tok; } } } } Variables::VariableUsage *Variables::find(unsigned int varid) { if (varid) { std::map::iterator i = mVarUsage.find(varid); if (i != mVarUsage.end()) return &i->second; } return nullptr; } static const Token* doAssignment(Variables &variables, const Token *tok, bool dereference, const Scope *scope) { // a = a + b; if (Token::Match(tok, "%var% = %var% !!;")) { const Token* rhsVarTok = tok->tokAt(2); if (tok->varId() == rhsVarTok->varId()) { return rhsVarTok; } } if (Token::Match(tok, "%var% %assign%") && tok->strAt(1) != "=") return tok->next(); const Token* const tokOld = tok; // check for aliased variable const unsigned int varid1 = tok->varId(); Variables::VariableUsage *var1 = variables.find(varid1); if (var1) { // jump behind '=' tok = tok->next(); while (!tok->isAssignmentOp()) { if (tok->varId()) variables.read(tok->varId(), tok); tok = tok->next(); } tok = tok->next(); if (Token::Match(tok, "( const| struct|union| %type% * ) ( (")) tok = tok->link()->next(); if (Token::Match(tok, "( [(<] const| struct|union| %type% *| [>)]")) tok = tok->next(); if (Token::Match(tok, "(| &| %name%") || (Token::Match(tok->next(), "< const| struct|union| %type% *| > ( &| %name%"))) { bool addressOf = false; if (Token::Match(tok, "%var% .")) variables.use(tok->varId(), tok); // use = read + write // check for C style cast if (tok->str() == "(") { tok = tok->next(); if (tok->str() == "const") tok = tok->next(); if (Token::Match(tok, "struct|union")) tok = tok->next(); while ((tok->isName() && tok->varId() == 0) || (tok->str() == "*") || (tok->str() == ")")) tok = tok->next(); if (tok->str() == "&") { addressOf = true; tok = tok->next(); } else if (tok->str() == "(") { tok = tok->next(); if (tok->str() == "&") { addressOf = true; tok = tok->next(); } } else if (Token::Match(tok, "%cop% %var%")) { variables.read(tok->next()->varId(), tok); } } // check for C++ style cast else if (tok->str().find("cast") != std::string::npos && tok->strAt(1) == "<") { tok = tok->tokAt(2); if (tok->str() == "const") tok = tok->next(); if (Token::Match(tok, "struct|union")) tok = tok->next(); tok = tok->next(); if (tok->str() == "*") tok = tok->next(); tok = tok->tokAt(2); if (!tok) return tokOld; if (tok->str() == "&") { addressOf = true; tok = tok->next(); } } // no cast, no ? else if (!Token::Match(tok, "%name% ?")) { if (tok->str() == "&") { addressOf = true; tok = tok->next(); } else if (tok->str() == "new") return tokOld; } // check if variable is local const unsigned int varid2 = tok->varId(); const Variables::VariableUsage* var2 = variables.find(varid2); if (var2) { // local variable (alias or read it) if (var1->mType == Variables::pointer || var1->mType == Variables::pointerArray) { if (dereference) variables.read(varid2, tok); else { if (addressOf || var2->mType == Variables::array || var2->mType == Variables::pointer) { bool replace = true; // pointerArray => don't replace if (var1->mType == Variables::pointerArray) replace = false; // check if variable declared in same scope else if (scope == var1->_var->scope()) replace = true; // not in same scope as declaration else { // no other assignment in this scope if (var1->_assignments.find(scope) == var1->_assignments.end() || scope->type == Scope::eSwitch) { // nothing to replace if (var1->_assignments.empty()) replace = false; // this variable has previous assignments else { /** * @todo determine if existing aliases should be replaced or merged */ replace = false; } } // assignment in this scope else { // replace when only one other assignment, merge them otherwise replace = (var1->_assignments.size() == 1); } } variables.alias(varid1, varid2, replace); } else if (tok->strAt(1) == "?") { if (var2->mType == Variables::reference) variables.readAliases(varid2, tok); else variables.read(varid2, tok); } else { variables.readAll(varid2, tok); } } } else if (var1->mType == Variables::reference) { variables.alias(varid1, varid2, true); } else if (var1->mType == Variables::standard && addressOf) { variables.alias(varid1, varid2, true); } else { if ((var2->mType == Variables::pointer || var2->mType == Variables::pointerArray) && tok->strAt(1) == "[") variables.readAliases(varid2, tok); variables.read(varid2, tok); } } else { // not a local variable (or an unsupported local variable) if (var1->mType == Variables::pointer && !dereference) { // check if variable declaration is in this scope if (var1->_var->scope() == scope) { // If variable is used in RHS then "use" variable for (const Token *rhs = tok; rhs && rhs->str() != ";"; rhs = rhs->next()) { if (rhs->varId() == varid1) { variables.use(varid1, tok); break; } } variables.clearAliases(varid1); } else { // no other assignment in this scope if (var1->_assignments.find(scope) == var1->_assignments.end()) { /** * @todo determine if existing aliases should be discarded */ } // this assignment replaces the last assignment in this scope else { // aliased variables in a larger scope are not supported // remove all aliases variables.clearAliases(varid1); } } } } } else tok = tokOld; var1->_assignments.insert(scope); } // check for alias to struct member // char c[10]; a.b = c; else if (Token::Match(tok->tokAt(-2), "%name% .")) { const Token *rhsVarTok = tok->tokAt(2); if (rhsVarTok && rhsVarTok->varId()) { const unsigned int varid2 = rhsVarTok->varId(); const Variables::VariableUsage *var2 = variables.find(varid2); // struct member aliased to local variable if (var2 && (var2->mType == Variables::array || var2->mType == Variables::pointer)) { // erase aliased variable and all variables that alias it // to prevent false positives variables.eraseAll(varid2); } } } // Possible pointer alias else if (Token::Match(tok, "%name% = %name% ;")) { const unsigned int varid2 = tok->tokAt(2)->varId(); const Variables::VariableUsage *var2 = variables.find(varid2); if (var2 && (var2->mType == Variables::array || var2->mType == Variables::pointer)) { variables.use(varid2,tok); } } return tok; } static bool isPartOfClassStructUnion(const Token* tok) { for (; tok; tok = tok->previous()) { if (tok->str() == "}" || tok->str() == ")") tok = tok->link(); else if (tok->str() == "(") return (false); else if (tok->str() == "{") { return (tok->strAt(-1) == "struct" || tok->strAt(-2) == "struct" || tok->strAt(-1) == "class" || tok->strAt(-2) == "class" || tok->strAt(-1) == "union" || tok->strAt(-2) == "union"); } } return false; } // Skip [ .. ] static const Token * skipBrackets(const Token *tok) { while (tok && tok->str() == "[") tok = tok->link()->next(); return tok; } // Skip [ .. ] . x static const Token * skipBracketsAndMembers(const Token *tok) { while (tok) { if (tok->str() == "[") tok = tok->link()->next(); else if (Token::Match(tok, ". %name%")) tok = tok->tokAt(2); else break; } return tok; } static void useFunctionArgs(const Token *tok, Variables& variables) { // TODO: Match function args to see if they are const or not. Assume that const data is not written. if (!tok) return; if (tok->str() == ",") { useFunctionArgs(tok->astOperand1(), variables); useFunctionArgs(tok->astOperand2(), variables); } else if (Token::Match(tok, "[+:]") && (!tok->valueType() || tok->valueType()->pointer)) { useFunctionArgs(tok->astOperand1(), variables); useFunctionArgs(tok->astOperand2(), variables); } else if (tok->variable() && tok->variable()->isArray()) { variables.use(tok->varId(), tok); } } //--------------------------------------------------------------------------- // Usage of function variables //--------------------------------------------------------------------------- void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables) { // Find declarations if the scope is executable.. if (scope->isExecutable()) { // Find declarations for (std::list::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) { if (i->isThrow() || i->isExtern()) continue; Variables::VariableType type = Variables::none; if (i->isArray() && (i->nameToken()->previous()->str() == "*" || i->nameToken()->strAt(-2) == "*")) type = Variables::pointerArray; else if (i->isArray() && i->nameToken()->previous()->str() == "&") type = Variables::referenceArray; else if (i->isArray()) type = (i->dimensions().size() == 1U) ? Variables::array : Variables::pointerArray; else if (i->isReference()) type = Variables::reference; else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*") type = Variables::pointerPointer; else if (i->isPointerToArray()) type = Variables::pointerPointer; else if (i->isPointer()) type = Variables::pointer; else if (mTokenizer->isC() || i->typeEndToken()->isStandardType() || isRecordTypeWithoutSideEffects(i->type()) || (i->isStlType() && !Token::Match(i->typeStartToken()->tokAt(2), "lock_guard|unique_lock|shared_ptr|unique_ptr|auto_ptr|shared_lock"))) type = Variables::standard; if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken())) continue; const Token* defValTok = i->nameToken()->next(); if (Token::Match(i->nameToken()->previous(), "* %var% ) (")) // function pointer. Jump behind parameter list. defValTok = defValTok->linkAt(1)->next(); for (; defValTok; defValTok = defValTok->next()) { if (defValTok->str() == "[") defValTok = defValTok->link(); else if (defValTok->str() == "(" || defValTok->str() == "{" || defValTok->str() == "=" || defValTok->str() == ":") { variables.addVar(&*i, type, true); break; } else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") { variables.addVar(&*i, type, i->isStatic()); break; } } if (i->isArray() && i->isClass()) // Array of class/struct members. Initialized by ctor. variables.write(i->declarationId(), i->nameToken()); if (i->isArray() && Token::Match(i->nameToken(), "%name% [ %var% ]")) // Array index variable read. variables.read(i->nameToken()->tokAt(2)->varId(), i->nameToken()); if (defValTok && defValTok->next()) { // simple assignment "var = 123" if (defValTok->str() == "=" && defValTok->next()->str() != "{") { doAssignment(variables, i->nameToken(), false, scope); } else { // could be "var = {...}" OR "var{...}" (since C++11) const Token* tokBraceStart = nullptr; if (Token::simpleMatch(defValTok, "= {")) { // "var = {...}" tokBraceStart = defValTok->next(); } else if (defValTok->str() == "{") { // "var{...}" tokBraceStart = defValTok; } if (tokBraceStart) { for (const Token* tok = tokBraceStart->next(); tok && tok != tokBraceStart->link(); tok = tok->next()) { if (tok->varId()) { // Variables used to initialize the array read. variables.read(tok->varId(), i->nameToken()); } } } } } } } // Check variable usage const Token *tok; if (scope->type == Scope::eFunction) tok = scope->bodyStart->next(); else tok = scope->classDef->next(); for (; tok && tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "{" && tok != scope->bodyStart && !tok->previous()->varId()) { for (const Scope *i : scope->nestedList) { if (i->bodyStart == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(tok->scope(), variables); // Scan child scope tok = tok->link(); break; } } if (!tok) break; } if (Token::Match(tok, "asm ( %str% )")) { variables.clear(); break; } // templates if (tok->isName() && endsWith(tok->str(), '>')) { // TODO: This is a quick fix to handle when constants are used // as template parameters. Try to handle this better, perhaps // only remove constants. variables.clear(); } else if (Token::Match(tok->previous(), "[;{}]")) { for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId()) { // Is this a variable declaration? const Variable *var = tok2->variable(); if (!var || var->nameToken() != tok2) continue; // Mark template parameters used in declaration as use.. if (tok2->strAt(-1) == ">") { for (const Token *tok3 = tok; tok3 != tok2; tok3 = tok3->next()) { if (tok3->varId() > 0U) variables.use(tok3->varId(), tok3); } } // Skip variable declaration.. tok = tok2->next(); if (Token::Match(tok, "( %name% )")) // Simple initialization through copy ctor tok = tok->next(); else if (Token::Match(tok, "= %var% ;")) { // Simple initialization tok = tok->next(); if (!var->isReference()) variables.read(tok->varId(), tok); } else if (tok->str() == "[" && Token::simpleMatch(skipBrackets(tok),"= {")) { const Token * const rhs1 = skipBrackets(tok)->next(); for (const Token *rhs = rhs1->link(); rhs != rhs1; rhs = rhs->previous()) { if (rhs->varId()) variables.readAll(rhs->varId(), rhs); } } else if (var->typeEndToken()->str() == ">") // Be careful with types like std::vector tok = tok->previous(); break; } else if (Token::Match(tok2, "[;({=]")) break; } } // Freeing memory (not considered "using" the pointer if it was also allocated in this function) if (Token::Match(tok, "free|g_free|kfree|vfree ( %var% )") || (mTokenizer->isCPP() && (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")))) { unsigned int varid = 0; if (tok->str() != "delete") { const Token *varTok = tok->tokAt(2); varid = varTok->varId(); tok = varTok->next(); } else if (tok->strAt(1) == "[") { const Token *varTok = tok->tokAt(3); varid = varTok->varId(); tok = varTok; } else { varid = tok->next()->varId(); tok = tok->next(); } const Variables::VariableUsage *const var = variables.find(varid); if (var) { if (!var->_aliases.empty()) variables.use(varid, tok); else if (!var->_allocateMemory) variables.readAll(varid, tok); } } else if (Token::Match(tok, "return|throw")) { for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId()) variables.readAll(tok2->varId(), tok); else if (tok2->str() == ";") break; } } // assignment else if (Token::Match(tok, "*| ++|--| %name% ++|--| %assign%") || Token::Match(tok, "*| ( const| %type% *| ) %name% %assign%")) { bool dereference = false; bool pre = false; bool post = false; if (tok->str() == "*") { dereference = true; tok = tok->next(); } if (Token::Match(tok, "( const| %type% *| ) %name% %assign%")) tok = tok->link()->next(); else if (tok->str() == "(") tok = tok->next(); if (tok->tokType() == Token::eIncDecOp) { pre = true; tok = tok->next(); } if (tok->next()->tokType() == Token::eIncDecOp) post = true; const unsigned int varid1 = tok->varId(); const Token * const start = tok; // assignment in while head.. bool inwhile = false; { const Token *parent = tok->astParent(); while (parent) { if (Token::simpleMatch(parent->previous(), "while (")) { inwhile = true; break; } parent = parent->astParent(); } } tok = doAssignment(variables, tok, dereference, scope); if (tok && tok->isAssignmentOp() && tok->str() != "=") { variables.use(varid1, tok); if (Token::Match(tok, "%assign% %name%")) { tok = tok->next(); variables.read(tok->varId(), tok); } } if (pre || post) variables.use(varid1, tok); if (dereference) { const Variables::VariableUsage *const var = variables.find(varid1); if (var && var->mType == Variables::array) variables.write(varid1, tok); variables.writeAliases(varid1, tok); variables.read(varid1, tok); } else { const Variables::VariableUsage *const var = variables.find(varid1); if (var && (inwhile || start->strAt(-1) == ",")) { variables.use(varid1, tok); } else if (var && var->mType == Variables::reference) { variables.writeAliases(varid1, tok); variables.read(varid1, tok); } // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable else if (var && var->mType == Variables::pointer && Token::Match(start, "%name% = new|malloc|calloc|kmalloc|kzalloc|kcalloc|strdup|strndup|vmalloc|g_new0|g_try_new|g_new|g_malloc|g_malloc0|g_try_malloc|g_try_malloc0|g_strdup|g_strndup|g_strdup_printf")) { bool allocate = true; if (start->strAt(2) == "new") { const Token *type = start->tokAt(3); // skip nothrow if (mTokenizer->isCPP() && (Token::simpleMatch(type, "( nothrow )") || Token::simpleMatch(type, "( std :: nothrow )"))) type = type->link()->next(); // is it a user defined type? if (!type->isStandardType()) { const Variable *variable = start->variable(); if (!variable || !isRecordTypeWithoutSideEffects(variable->type())) allocate = false; } } if (allocate) variables.allocateMemory(varid1, tok); else variables.write(varid1, tok); } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) { variables.read(varid1, tok); variables.write(varid1, start); } else if (var && var->mType == Variables::pointer && Token::Match(tok, "%name% ;") && tok->varId() == 0 && tok->hasKnownIntValue() && tok->values().front().intvalue == 0) { variables.use(varid1, tok); } else { variables.write(varid1, tok); } } const Variables::VariableUsage * const var2 = variables.find(tok->varId()); if (var2) { if (var2->mType == Variables::reference) { variables.writeAliases(tok->varId(), tok); variables.read(tok->varId(), tok); } else if (tok->varId() != varid1 && Token::Match(tok, "%name% .|[")) variables.read(tok->varId(), tok); else if (tok->varId() != varid1 && var2->mType == Variables::standard && tok->strAt(-1) != "&") variables.use(tok->varId(), tok); } const Token * const equal = skipBracketsAndMembers(tok->next()); // checked for chained assignments if (tok != start && equal && equal->str() == "=") { const unsigned int varId = tok->varId(); const Variables::VariableUsage * const var = variables.find(varId); if (var && var->mType != Variables::reference) { variables.read(varId,tok); } tok = tok->previous(); } } // assignment else if ((Token::Match(tok, "%name% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) || (Token::simpleMatch(tok, "* (") && Token::simpleMatch(tok->next()->link(), ") ="))) { const Token *eq = tok; while (eq && !eq->isAssignmentOp()) eq = eq->astParent(); const bool deref = eq && eq->astOperand1() && eq->astOperand1()->valueType() && eq->astOperand1()->valueType()->pointer == 0U; if (tok->str() == "*") { tok = tok->tokAt(2); if (tok->str() == "(") tok = tok->link()->next(); } const unsigned int varid = tok->varId(); const Variables::VariableUsage *var = variables.find(varid); if (var) { // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable if (var->mType == Variables::pointer && Token::Match(skipBrackets(tok->next()), "= new|malloc|calloc|kmalloc|kzalloc|kcalloc|strdup|strndup|vmalloc|g_new0|g_try_new|g_new|g_malloc|g_malloc0|g_try_malloc|g_try_malloc0|g_strdup|g_strndup|g_strdup_printf")) { variables.allocateMemory(varid, tok); } else if (var->mType == Variables::pointer || var->mType == Variables::reference) { variables.read(varid, tok); variables.writeAliases(varid, tok); } else if (var->mType == Variables::pointerArray) { tok = doAssignment(variables, tok, deref, scope); } else variables.writeAll(varid, tok); } } else if (mTokenizer->isCPP() && Token::Match(tok, "[;{}] %var% <<")) { variables.erase(tok->next()->varId()); } else if (Token::Match(tok, "& %var%")) { if (tok->astOperand2()) { // bitop variables.read(tok->next()->varId(), tok); } else // addressof variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, ">>|>>= %name%")) { if (isLikelyStreamRead(mTokenizer->isCPP(), tok)) variables.use(tok->next()->varId(), tok); // use = read + write else variables.read(tok->next()->varId(), tok); } else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) { variables.read(tok->varId(), tok); } else if (isLikelyStreamRead(mTokenizer->isCPP(),tok->previous())) { variables.use(tok->varId(), tok); } // function parameter else if (Token::Match(tok, "[(,] %var% [")) { variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") { variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "[(,] & %var% [,)]")) { variables.eraseAll(tok->tokAt(2)->varId()); } else if (Token::Match(tok, "[(,] (") && Token::Match(tok->next()->link(), ") %var% [,)]")) { variables.use(tok->next()->link()->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "[(,] *| %var% =")) { tok = tok->next(); if (tok->str() == "*") tok = tok->next(); variables.use(tok->varId(), tok); } // function else if (Token::Match(tok, "%name% (")) { variables.read(tok->varId(), tok); useFunctionArgs(tok->next()->astOperand2(), variables); } else if (Token::Match(tok, "std :: ref ( %var% )")) { variables.eraseAll(tok->tokAt(4)->varId()); } else if (Token::Match(tok->previous(), "[{,] %var% [,}]")) { variables.read(tok->varId(), tok); } else if (tok->varId() && Token::Match(tok, "%var% .")) { variables.use(tok->varId(), tok); // use = read + write } else if (tok->str() == ":" && (!tok->valueType() || tok->valueType()->pointer)) { if (tok->astOperand1()) variables.use(tok->astOperand1()->varId(), tok->astOperand1()); if (tok->astOperand2()) variables.use(tok->astOperand2()->varId(), tok->astOperand2()); } else if (tok->isExtendedOp() && tok->next() && tok->next()->varId() && tok->strAt(2) != "=") { variables.readAll(tok->next()->varId(), tok); } else if (tok->varId() && tok->next() && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) { if (Token::Match(tok->tokAt(-2), "%name% ( %var% [,)]") && !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference())) variables.use(tok->varId(), tok); else variables.readAll(tok->varId(), tok); } else if (Token::Match(tok, "%var% ;") && Token::Match(tok->previous(), "[;{}:]")) { variables.readAll(tok->varId(), tok); } // ++|-- else if (tok->next() && tok->next()->tokType() == Token::eIncDecOp && tok->next()->astOperand1() && tok->next()->astOperand1()->varId()) { if (tok->next()->astParent()) variables.use(tok->next()->astOperand1()->varId(), tok); else variables.modified(tok->next()->astOperand1()->varId(), tok); } else if (tok->isAssignmentOp()) { for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId()) { if (tok2->strAt(1) == "=") variables.write(tok2->varId(), tok); else if (tok2->next() && tok2->next()->isAssignmentOp()) variables.use(tok2->varId(), tok); else variables.read(tok2->varId(), tok); } } } } } void CheckUnusedVar::checkFunctionVariableUsage() { if (!mSettings->isEnabled(Settings::STYLE)) return; // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // only check functions for (const Scope * scope : symbolDatabase->functionScopes) { // Bailout when there are lambdas or inline functions // TODO: Handle lambdas and inline functions properly if (scope->hasInlineOrLambdaFunction()) continue; for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (findLambdaEndToken(tok)) // todo: handle lambdas break; if (Token::simpleMatch(tok, "try {")) // todo: check try blocks tok = tok->linkAt(1); const Token *varDecl = nullptr; if (tok->variable() && tok->variable()->nameToken() == tok) { const Token * eq = tok->next(); while (Token::simpleMatch(eq, "[")) eq = eq->link()->next(); if (Token::simpleMatch(eq, "=")) { varDecl = tok; tok = eq; } } // not assignment/initialization/increment => continue const bool isAssignment = tok->isAssignmentOp() && tok->astOperand1(); const bool isInitialization = (Token::Match(tok, "%var% (") && tok->variable() && tok->variable()->nameToken() == tok); const bool isIncrementOrDecrement = (tok->tokType() == Token::Type::eIncDecOp); if (!isAssignment && !isInitialization && !isIncrementOrDecrement) continue; if (tok->isName()) { if (mTokenizer->isCPP()) { // do not check RAII/scope_lock objects if (!tok->valueType()) continue; bool check = false; switch (tok->valueType()->type) { case ValueType::Type::UNKNOWN_TYPE: case ValueType::Type::NONSTD: case ValueType::Type::RECORD: check = tok->valueType()->typeScope && !tok->valueType()->typeScope->getDestructor(); break; case ValueType::Type::CONTAINER: case ValueType::Type::ITERATOR: case ValueType::Type::VOID: case ValueType::Type::BOOL: case ValueType::Type::CHAR: case ValueType::Type::SHORT: case ValueType::Type::WCHAR_T: case ValueType::Type::INT: case ValueType::Type::LONG: case ValueType::Type::LONGLONG: case ValueType::Type::UNKNOWN_INT: case ValueType::Type::FLOAT: case ValueType::Type::DOUBLE: case ValueType::Type::LONGDOUBLE: check = true; break; }; if (!check) continue; } tok = tok->next(); } if (tok->astParent() && tok->str() != "(") { const Token *parent = tok->astParent(); while (Token::Match(parent, "%oror%|%comp%|!|&&")) parent = parent->astParent(); if (!parent) continue; if (!Token::simpleMatch(parent->previous(), "if (")) continue; } // Do not warn about assignment with NULL if (FwdAnalysis::isNullOperand(tok->astOperand2())) continue; if (!tok->astOperand1()) continue; const Token *iteratorToken = tok->astOperand1(); while (Token::Match(iteratorToken, "[.*]")) iteratorToken = iteratorToken->astOperand1(); if (iteratorToken && iteratorToken->variable() && iteratorToken->variable()->typeEndToken()->str().find("iterator") != std::string::npos) continue; const Token *op1tok = tok->astOperand1(); while (Token::Match(op1tok, ".|[|*")) op1tok = op1tok->astOperand1(); const Variable *op1Var = op1tok ? op1tok->variable() : nullptr; std::string bailoutTypeName; if (op1Var) { if (op1Var->isReference() && op1Var->nameToken() != tok->astOperand1()) // todo: check references continue; if (op1Var->isStatic()) // todo: check static variables continue; if (op1Var->nameToken()->isAttributeUnused()) continue; // Bailout for unknown template classes, we have no idea what side effects such assignments have if (mTokenizer->isCPP() && op1Var->isClass() && (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) { // Check in the library if we should bailout or not.. const std::string typeName = op1Var->getTypeName(); switch (mSettings->library.getTypeCheck("unusedvar", typeName)) { case Library::TypeCheck::def: bailoutTypeName = typeName; break; case Library::TypeCheck::check: break; case Library::TypeCheck::suppress: continue; }; } } // Is there a redundant assignment? const Token *start = tok->findExpressionStartEndTokens().second->next(); const Token *expr = varDecl ? varDecl : tok->astOperand1(); // Is variable in lhs a union member? if (tok->previous() && tok->previous()->variable() && tok->previous()->variable()->nameToken()->scope()->type == Scope::eUnion) continue; FwdAnalysis fwdAnalysis(mTokenizer->isCPP(), mSettings->library); if (fwdAnalysis.unusedValue(expr, start, scope->bodyEnd)) { if (!bailoutTypeName.empty() && bailoutTypeName != "auto") { if (mSettings->checkLibrary && mSettings->isEnabled(Settings::INFORMATION)) { reportError(tok, Severity::information, "checkLibraryCheckType", "--check-library: Provide configuration for " + bailoutTypeName); continue; } } // warn unreadVariableError(tok, expr->expressionString(), false); } } // varId, usage {read, write, modified} Variables variables; checkFunctionVariableUsage_iterateScopes(scope, variables); // Check usage of all variables in the current scope.. for (std::map::const_iterator it = variables.varUsage().begin(); it != variables.varUsage().end(); ++it) { const Variables::VariableUsage &usage = it->second; // variable has been marked as unused so ignore it if (usage._var->nameToken()->isAttributeUnused() || usage._var->nameToken()->isAttributeUsed()) continue; // skip things that are only partially implemented to prevent false positives if (usage.mType == Variables::pointerPointer || usage.mType == Variables::pointerArray || usage.mType == Variables::referenceArray) continue; const std::string &varname = usage._var->name(); const Variable* var = symbolDatabase->getVariableFromVarId(it->first); // variable has had memory allocated for it, but hasn't done // anything with that memory other than, perhaps, freeing it if (usage.unused() && !usage._modified && usage._allocateMemory) allocatedButUnusedVariableError(usage._lastAccess, varname); // variable has not been written, read, or modified else if (usage.unused() && !usage._modified) unusedVariableError(usage._var->nameToken(), varname); // variable has not been written but has been modified else if (usage._modified && !usage._write && !usage._allocateMemory && var && !var->isStlType()) unassignedVariableError(usage._var->nameToken(), varname); // variable has been read but not written else if (!usage._write && !usage._allocateMemory && var && !var->isStlType() && !isEmptyType(var->type())) unassignedVariableError(usage._var->nameToken(), varname); } } } void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedVariable", "$symbol:" + varname + "\nUnused variable: $symbol", CWE563, false); } void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unusedAllocatedMemory", "$symbol:" + varname + "\nVariable '$symbol' is allocated memory that is never used.", CWE563, false); } void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname, bool modified) { if (modified) reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is modified but its new value is never used.", CWE563, false); else reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is assigned a value that is never used.", CWE563, false); } void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "unassignedVariable", "$symbol:" + varname + "\nVariable '$symbol' is not assigned a value.", CWE665, false); } //--------------------------------------------------------------------------- // Check that all struct members are used //--------------------------------------------------------------------------- void CheckUnusedVar::checkStructMemberUsage() { if (!mSettings->isEnabled(Settings::STYLE)) return; const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (scope.type != Scope::eStruct && scope.type != Scope::eUnion) continue; if (scope.bodyStart->fileIndex() != 0 || scope.className.empty()) continue; // Packed struct => possibly used by lowlevel code. Struct members might be required by hardware. if (scope.bodyEnd->isAttributePacked()) continue; // Bail out if struct/union contains any functions if (!scope.functionList.empty()) continue; // Bail out for template struct, members might be used in non-matching instantiations if (scope.className.find("<") != std::string::npos) continue; // bail out if struct is inherited bool bailout = false; for (const Scope &derivedScope : symbolDatabase->scopeList) { if (derivedScope.definedType) { for (const Type::BaseInfo &derivedFrom : derivedScope.definedType->derivedFrom) { if (derivedFrom.type == scope.definedType) { bailout = true; break; } } } } if (bailout) continue; // bail out for extern/global struct for (const Variable* var : symbolDatabase->variableList()) { if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope.className) { bailout = true; break; } } if (bailout) continue; // Bail out if some data is casted to struct.. const std::string castPattern("( struct| " + scope.className + " * ) & %name% ["); if (Token::findmatch(scope.bodyEnd, castPattern.c_str())) continue; // (struct S){..} const std::string initPattern("( struct| " + scope.className + " ) {"); if (Token::findmatch(scope.bodyEnd, initPattern.c_str())) continue; // Bail out if struct is used in sizeof.. for (const Token *tok = scope.bodyEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) { tok = tok->tokAt(2); if (Token::Match(tok, ("struct| " + scope.className).c_str())) { bailout = true; break; } } if (bailout) continue; // Try to prevent false positives when struct members are not used directly. if (Token::findmatch(scope.bodyEnd, (scope.className + " %type%| *").c_str())) continue; for (const Variable &var : scope.varlist) { // declaring a POD member variable? if (!var.typeStartToken()->isStandardType() && !var.isPointer()) continue; // Check if the struct member variable is used anywhere in the file if (Token::findsimplematch(mTokenizer->tokens(), (". " + var.name()).c_str())) continue; unusedStructMemberError(var.nameToken(), scope.className, var.name(), scope.type == Scope::eUnion); } } } void CheckUnusedVar::unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, bool isUnion) { const std::string prefix = isUnion ? "union member " : "struct member "; reportError(tok, Severity::style, "unusedStructMember", "$symbol:" + structname + "::" + varname + '\n' + prefix + "'$symbol' is never used.", CWE563, false); } bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type) { // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check constructors for side effects */ const std::pair::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert( std::pair(type,false)); //Initialize with side effects for possible recursions bool & withoutSideEffects=found.first->second; if (!found.second) return withoutSideEffects; if (type && type->classScope && type->classScope->numConstructors == 0 && (type->classScope->varlist.empty() || type->needInitialization == Type::NeedInitialization::True)) { for (std::vector::const_iterator i = type->derivedFrom.begin(); i != type->derivedFrom.end(); ++i) { if (!isRecordTypeWithoutSideEffects(i->type)) { withoutSideEffects=false; return withoutSideEffects; } } withoutSideEffects=true; return withoutSideEffects; } withoutSideEffects=false; // unknown types are assumed to have side effects return withoutSideEffects; } bool CheckUnusedVar::isEmptyType(const Type* type) { // a type that has no variables and no constructor const std::pair::iterator,bool> found=mIsEmptyTypeMap.insert( std::pair(type,false)); bool & emptyType=found.first->second; if (!found.second) return emptyType; if (type && type->classScope && type->classScope->numConstructors == 0 && (type->classScope->varlist.empty())) { for (std::vector::const_iterator i = type->derivedFrom.begin(); i != type->derivedFrom.end(); ++i) { if (!isEmptyType(i->type)) { emptyType=false; return emptyType; } } emptyType=true; return emptyType; } emptyType=false; // unknown types are assumed to be nonempty return emptyType; } cppcheck-1.90/lib/checkunusedvar.h000066400000000000000000000076361357737443600172130ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkunusedvarH #define checkunusedvarH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include #include class ErrorLogger; class Scope; class Settings; class Token; class Tokenizer; class Type; class Variables; /// @addtogroup Checks /// @{ /** @brief Various small checks */ class CPPCHECKLIB CheckUnusedVar : public Check { public: /** @brief This constructor is used when registering the CheckClass */ CheckUnusedVar() : Check(myName()) { } /** @brief This constructor is used when running checks. */ CheckUnusedVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } /** @brief Run checks against the normal token list */ void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckUnusedVar checkUnusedVar(tokenizer, settings, errorLogger); // Coding style checks checkUnusedVar.checkStructMemberUsage(); checkUnusedVar.checkFunctionVariableUsage(); } /** @brief %Check for unused function variables */ void checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables); void checkFunctionVariableUsage(); /** @brief %Check that all struct members are used */ void checkStructMemberUsage(); private: bool isRecordTypeWithoutSideEffects(const Type* type); bool isEmptyType(const Type* type); // Error messages.. void unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, bool isUnion = false); void unusedVariableError(const Token *tok, const std::string &varname); void allocatedButUnusedVariableError(const Token *tok, const std::string &varname); void unreadVariableError(const Token *tok, const std::string &varname, bool modified); void unassignedVariableError(const Token *tok, const std::string &varname); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckUnusedVar c(nullptr, settings, errorLogger); // style/warning c.unusedVariableError(nullptr, "varname"); c.allocatedButUnusedVariableError(nullptr, "varname"); c.unreadVariableError(nullptr, "varname", false); c.unassignedVariableError(nullptr, "varname"); c.unusedStructMemberError(nullptr, "structname", "variable"); } static std::string myName() { return "UnusedVar"; } std::string classInfo() const OVERRIDE { return "UnusedVar checks\n" // style "- unused variable\n" "- allocated but unused variable\n" "- unred variable\n" "- unassigned variable\n" "- unused struct member\n"; } std::map mIsRecordTypeWithoutSideEffectsMap; std::map mIsEmptyTypeMap; }; /// @} //--------------------------------------------------------------------------- #endif // checkunusedvarH cppcheck-1.90/lib/checkvaarg.cpp000066400000000000000000000171321357737443600166220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkvaarg.h" #include "astutils.h" #include "errorlogger.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" #include #include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) namespace { CheckVaarg instance; } //--------------------------------------------------------------------------- // Ensure that correct parameter is passed to va_start() //--------------------------------------------------------------------------- // CWE ids used: static const struct CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime static const struct CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument static const struct CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior void CheckVaarg::va_start_argument() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); const bool printWarnings = mSettings->isEnabled(Settings::WARNING); for (std::size_t i = 0; i < functions; ++i) { const Scope* scope = symbolDatabase->functionScopes[i]; const Function* function = scope->function; if (!function) continue; for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (!tok->scope()->isExecutable()) tok = tok->scope()->bodyEnd; else if (Token::simpleMatch(tok, "va_start (")) { const Token* param2 = tok->tokAt(2)->nextArgument(); if (!param2) continue; const Variable* var = param2->variable(); if (var && var->isReference()) referenceAs_va_start_error(param2, var->name()); if (var && var->index() + 2 < function->argCount() && printWarnings) { std::list::const_reverse_iterator it = function->argumentList.rbegin(); ++it; wrongParameterTo_va_start_error(tok, var->name(), it->name()); } tok = tok->linkAt(1); } } } } void CheckVaarg::wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName) { reportError(tok, Severity::warning, "va_start_wrongParameter", "'" + paramIsName + "' given to va_start() is not last named argument of the function. Did you intend to pass '" + paramShouldName + "'?", CWE688, false); } void CheckVaarg::referenceAs_va_start_error(const Token *tok, const std::string& paramName) { reportError(tok, Severity::error, "va_start_referencePassed", "Using reference '" + paramName + "' as parameter for va_start() results in undefined behaviour.", CWE758, false); } //--------------------------------------------------------------------------- // Detect missing va_end() if va_start() was used // Detect va_list usage after va_end() //--------------------------------------------------------------------------- void CheckVaarg::va_list_usage() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable* var : symbolDatabase->variableList()) { if (!var || var->isPointer() || var->isReference() || var->isArray() || !var->scope() || var->typeStartToken()->str() != "va_list") continue; if (!var->isLocal() && !var->isArgument()) // Check only local variables and arguments continue; bool open = var->isArgument(); // va_list passed as argument are opened bool exitOnEndOfStatement = false; const Token* tok = var->nameToken()->next(); for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) { // Skip lambdas const Token* tok2 = findLambdaEndToken(tok); if (tok2) tok = tok2; if (Token::Match(tok, "va_start ( %varid%", var->declarationId())) { if (open) va_start_subsequentCallsError(tok, var->name()); open = true; tok = tok->linkAt(1); } else if (Token::Match(tok, "va_end ( %varid%", var->declarationId())) { if (!open) va_list_usedBeforeStartedError(tok, var->name()); open = false; tok = tok->linkAt(1); } else if (Token::simpleMatch(tok, "va_copy (")) { bool nopen = open; if (tok->linkAt(1)->previous()->varId() == var->declarationId()) { // Source if (!open) va_list_usedBeforeStartedError(tok, var->name()); } if (tok->tokAt(2)->varId() == var->declarationId()) { // Destination if (open) va_start_subsequentCallsError(tok, var->name()); nopen = true; } open = nopen; tok = tok->linkAt(1); } else if (Token::Match(tok, "throw|return")) exitOnEndOfStatement = true; else if (tok->str() == "break") { const Scope* scope = tok->scope(); while (scope->nestedIn && scope->type != Scope::eFor && scope->type != Scope::eWhile && scope->type != Scope::eDo && scope->type != Scope::eSwitch) scope = scope->nestedIn; tok = scope->bodyEnd; if (!tok) return; } else if (tok->str() == "goto" || (mTokenizer->isCPP() && tok->str() == "try")) { open = false; break; } else if (!open && tok->varId() == var->declarationId()) va_list_usedBeforeStartedError(tok, var->name()); else if (exitOnEndOfStatement && tok->str() == ";") break; } if (open && !var->isArgument()) va_end_missingError(tok, var->name()); } } void CheckVaarg::va_end_missingError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_end_missing", "va_list '" + varname + "' was opened but not closed by va_end().", CWE664, false); } void CheckVaarg::va_list_usedBeforeStartedError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_list_usedBeforeStarted", "va_list '" + varname + "' used before va_start() was called.", CWE664, false); } void CheckVaarg::va_start_subsequentCallsError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_start_subsequentCalls", "va_start() or va_copy() called subsequently on '" + varname + "' without va_end() in between.", CWE664, false); } cppcheck-1.90/lib/checkvaarg.h000066400000000000000000000062061357737443600162670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef checkvaargtH #define checkvaargtH //--------------------------------------------------------------------------- #include "check.h" #include "config.h" #include class ErrorLogger; class Settings; class Token; class Tokenizer; /// @addtogroup Checks /// @{ /** * @brief Checking for misusage of variable argument lists */ class CPPCHECKLIB CheckVaarg : public Check { public: CheckVaarg() : Check(myName()) { } CheckVaarg(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : Check(myName(), tokenizer, settings, errorLogger) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { CheckVaarg check(tokenizer, settings, errorLogger); check.va_start_argument(); check.va_list_usage(); } void va_start_argument(); void va_list_usage(); private: void wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName); void referenceAs_va_start_error(const Token *tok, const std::string& paramName); void va_end_missingError(const Token *tok, const std::string& varname); void va_list_usedBeforeStartedError(const Token *tok, const std::string& varname); void va_start_subsequentCallsError(const Token *tok, const std::string& varname); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE { CheckVaarg c(nullptr, settings, errorLogger); c.wrongParameterTo_va_start_error(nullptr, "arg1", "arg2"); c.referenceAs_va_start_error(nullptr, "arg1"); c.va_end_missingError(nullptr, "vl"); c.va_list_usedBeforeStartedError(nullptr, "vl"); c.va_start_subsequentCallsError(nullptr, "vl"); } static std::string myName() { return "Vaarg"; } std::string classInfo() const OVERRIDE { return "Check for misusage of variable argument lists:\n" "- Wrong parameter passed to va_start()\n" "- Reference passed to va_start()\n" "- Missing va_end()\n" "- Using va_list before it is opened\n" "- Subsequent calls to va_start/va_copy()\n"; } }; /// @} //--------------------------------------------------------------------------- #endif // checkvaargtH cppcheck-1.90/lib/config.h000066400000000000000000000012311357737443600154270ustar00rootroot00000000000000#ifndef configH #define configH #ifdef _WIN32 # ifdef CPPCHECKLIB_EXPORT # define CPPCHECKLIB __declspec(dllexport) # elif defined(CPPCHECKLIB_IMPORT) # define CPPCHECKLIB __declspec(dllimport) # else # define CPPCHECKLIB # endif #else # define CPPCHECKLIB #endif // MS Visual C++ memory leak debug tracing #if defined(_MSC_VER) && defined(_DEBUG) # define _CRTDBG_MAP_ALLOC # include #endif // C++11 override #if defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__CPPCHECK__) # define OVERRIDE override #else # define OVERRIDE #endif #include static const std::string emptyString; #endif // configH cppcheck-1.90/lib/cppcheck.cpp000066400000000000000000001532311357737443600163050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppcheck.h" #include "check.h" #include "checkunusedfunctions.h" #include "ctu.h" #include "library.h" #include "mathlib.h" #include "path.h" #include "platform.h" #include "preprocessor.h" // Preprocessor #include "suppressions.h" #include "timer.h" #include "token.h" #include "tokenize.h" // Tokenizer #include "tokenlist.h" #include "version.h" #include "exprengine.h" #define PICOJSON_USE_INT64 #include #include #include #include #include #include #include #include #include #include #include // <- TEMPORARY #include #ifdef HAVE_RULES #define PCRE_STATIC #include #endif static const char Version[] = CPPCHECK_VERSION_STRING; static const char ExtraVersion[] = ""; static TimerResults s_timerResults; // CWE ids used static const CWE CWE398(398U); // Indicator of Poor Code Quality namespace { struct AddonInfo { std::string name; std::string scriptFile; std::string args; static std::string getFullPath(const std::string &fileName, const std::string &exename) { if (Path::fileExists(fileName)) return fileName; const std::string exepath = Path::getPathFromFilename(exename); if (Path::fileExists(exepath + fileName)) return exepath + fileName; if (Path::fileExists(exepath + "addons/" + fileName)) return exepath + "addons/" + fileName; #ifdef FILESDIR if (Path::fileExists(FILESDIR + ("/" + fileName))) return FILESDIR + ("/" + fileName); if (Path::fileExists(FILESDIR + ("/addons/" + fileName))) return FILESDIR + ("/addons/" + fileName); #endif return ""; } std::string getAddonInfo(const std::string &fileName, const std::string &exename) { if (fileName.find(".") == std::string::npos) return getAddonInfo(fileName + ".py", exename); if (endsWith(fileName, ".py", 3)) { scriptFile = getFullPath(fileName, exename); if (scriptFile.empty()) return "Did not find addon " + fileName; std::string::size_type pos1 = scriptFile.rfind("/"); if (pos1 == std::string::npos) pos1 = 0; else pos1++; std::string::size_type pos2 = scriptFile.rfind("."); if (pos2 < pos1) pos2 = std::string::npos; name = scriptFile.substr(pos1, pos2 - pos1); return ""; } if (!endsWith(fileName, ".json", 5)) return "Failed to open addon " + fileName; std::ifstream fin(fileName); if (!fin.is_open()) return "Failed to open " + fileName; picojson::value json; fin >> json; std::string json_error = picojson::get_last_error(); if (!json_error.empty()) { return "Loading " + fileName + " failed. " + json_error; } if (!json.is()) return "Loading " + fileName + " failed. Bad json."; picojson::object obj = json.get(); if (obj.count("args")) { if (!obj["args"].is()) return "Loading " + fileName + " failed. args must be array."; for (const picojson::value &v : obj["args"].get()) args += " " + v.get(); } return getAddonInfo(obj["script"].get(), exename); } }; } static std::string executeAddon(const AddonInfo &addonInfo, const std::string &dumpFile) { // Can python be executed? { const std::string cmd = "python --version 2>&1"; #ifdef _WIN32 std::unique_ptr pipe(_popen(cmd.c_str(), "r"), _pclose); #else std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose); #endif if (!pipe) throw InternalError(nullptr, "popen failed (command: '" + cmd + "')"); char buffer[1024]; std::string result; while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) result += buffer; if (result.compare(0, 7, "Python ", 0, 7) != 0 || result.size() > 50) throw InternalError(nullptr, "Failed to execute '" + cmd + "' (" + result + ")"); } const std::string cmd = "python \"" + addonInfo.scriptFile + "\" --cli" + addonInfo.args + " \"" + dumpFile + "\" 2>&1"; #ifdef _WIN32 std::unique_ptr pipe(_popen(cmd.c_str(), "r"), _pclose); #else std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose); #endif if (!pipe) throw InternalError(nullptr, "popen failed (command: '" + cmd + "')"); char buffer[1024]; std::string result; while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) { result += buffer; } // Validate output.. std::istringstream istr(result); std::string line; while (std::getline(istr, line)) { if (line.compare(0,9,"Checking ", 0, 9) != 0 && !line.empty() && line[0] != '{') throw InternalError(nullptr, "Failed to execute '" + cmd + "'. " + result); } // Valid results return result; } static std::vector split(const std::string &str, const std::string &sep) { std::vector ret; for (std::string::size_type defineStartPos = 0U; defineStartPos < str.size();) { const std::string::size_type defineEndPos = str.find(sep, defineStartPos); ret.push_back((defineEndPos == std::string::npos) ? str.substr(defineStartPos) : str.substr(defineStartPos, defineEndPos - defineStartPos)); if (defineEndPos == std::string::npos) break; defineStartPos = defineEndPos + 1U; } return ret; } CppCheck::CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions) : mErrorLogger(errorLogger), mExitCode(0), mSuppressInternalErrorFound(false), mUseGlobalSuppressions(useGlobalSuppressions), mTooManyConfigs(false), mSimplify(true) { } CppCheck::~CppCheck() { while (!mFileInfo.empty()) { delete mFileInfo.back(); mFileInfo.pop_back(); } s_timerResults.showResults(mSettings.showtime); } const char * CppCheck::version() { return Version; } const char * CppCheck::extraVersion() { return ExtraVersion; } unsigned int CppCheck::check(const std::string &path) { std::ifstream fin(path); return checkFile(Path::simplifyPath(path), emptyString, fin); } unsigned int CppCheck::check(const std::string &path, const std::string &content) { std::istringstream iss(content); return checkFile(Path::simplifyPath(path), emptyString, iss); } unsigned int CppCheck::check(const ImportProject::FileSettings &fs) { CppCheck temp(mErrorLogger, mUseGlobalSuppressions); temp.mSettings = mSettings; if (!temp.mSettings.userDefines.empty()) temp.mSettings.userDefines += ';'; temp.mSettings.userDefines += fs.cppcheckDefines(); temp.mSettings.includePaths = fs.includePaths; temp.mSettings.userUndefs = fs.undefs; if (fs.platformType != Settings::Unspecified) { temp.mSettings.platform(fs.platformType); } std::ifstream fin(fs.filename); return temp.checkFile(Path::simplifyPath(fs.filename), fs.cfg, fin); } unsigned int CppCheck::checkFile(const std::string& filename, const std::string &cfgname, std::istream& fileStream) { mExitCode = 0; mSuppressInternalErrorFound = false; // only show debug warnings for accepted C/C++ source files if (!Path::acceptFile(filename)) mSettings.debugwarnings = false; if (Settings::terminated()) return mExitCode; if (!mSettings.quiet) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("...")); if (mSettings.verbose) { mErrorLogger.reportOut("Defines:" + mSettings.userDefines); std::string undefs; for (const std::string& U : mSettings.userUndefs) { if (!undefs.empty()) undefs += ';'; undefs += ' ' + U; } mErrorLogger.reportOut("Undefines:" + undefs); std::string includePaths; for (const std::string &I : mSettings.includePaths) includePaths += " -I" + I; mErrorLogger.reportOut("Includes:" + includePaths); mErrorLogger.reportOut(std::string("Platform:") + mSettings.platformString()); } } if (plistFile.is_open()) { plistFile << ErrorLogger::plistFooter(); plistFile.close(); } CheckUnusedFunctions checkUnusedFunctions(nullptr, nullptr, nullptr); try { Preprocessor preprocessor(mSettings, this); std::set configurations; simplecpp::OutputList outputList; std::vector files; simplecpp::TokenList tokens1(fileStream, files, filename, &outputList); // If there is a syntax error, report it and stop for (const simplecpp::Output &output : outputList) { bool err; switch (output.type) { case simplecpp::Output::ERROR: case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: err = true; break; case simplecpp::Output::WARNING: case simplecpp::Output::MISSING_HEADER: case simplecpp::Output::PORTABILITY_BACKSLASH: err = false; break; } if (err) { const ErrorLogger::ErrorMessage::FileLocation loc1(output.location.file(), output.location.line, output.location.col); std::list callstack(1, loc1); ErrorLogger::ErrorMessage errmsg(callstack, "", Severity::error, output.msg, "syntaxError", false); reportErr(errmsg); return mExitCode; } } if (!preprocessor.loadFiles(tokens1, files)) return mExitCode; if (!mSettings.plistOutput.empty()) { std::string filename2; if (filename.find('/') != std::string::npos) filename2 = filename.substr(filename.rfind('/') + 1); else filename2 = filename; filename2 = mSettings.plistOutput + filename2.substr(0, filename2.find('.')) + ".plist"; plistFile.open(filename2); plistFile << ErrorLogger::plistHeader(version(), files); } // write dump file xml prolog std::ofstream fdump; std::string dumpFile; if (mSettings.dump || !mSettings.addons.empty()) { if (!mSettings.dumpFile.empty()) dumpFile = mSettings.dumpFile; else if (!mSettings.dump && !mSettings.buildDir.empty()) dumpFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, filename, "") + ".dump"; else dumpFile = filename + ".dump"; fdump.open(dumpFile); if (fdump.is_open()) { fdump << "" << std::endl; fdump << "" << std::endl; fdump << " \n"; fdump << " " << std::endl; for (unsigned int i = 0; i < files.size(); ++i) fdump << " " << std::endl; for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) { fdump << " location.fileIndex << "\" " << "linenr=\"" << tok->location.line << "\" " << "column=\"" << tok->location.col << "\" " << "str=\"" << ErrorLogger::toxml(tok->str()) << "\"" << "/>" << std::endl; } fdump << " " << std::endl; } } // Parse comments and then remove them preprocessor.inlineSuppressions(tokens1); if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { mSettings.nomsg.dump(fdump); } tokens1.removeComments(); preprocessor.removeComments(); if (!mSettings.buildDir.empty()) { // Get toolinfo std::ostringstream toolinfo; toolinfo << CPPCHECK_VERSION_STRING; toolinfo << (mSettings.isEnabled(Settings::WARNING) ? 'w' : ' '); toolinfo << (mSettings.isEnabled(Settings::STYLE) ? 's' : ' '); toolinfo << (mSettings.isEnabled(Settings::PERFORMANCE) ? 'p' : ' '); toolinfo << (mSettings.isEnabled(Settings::PORTABILITY) ? 'p' : ' '); toolinfo << (mSettings.isEnabled(Settings::INFORMATION) ? 'i' : ' '); toolinfo << mSettings.userDefines; mSettings.nomsg.dump(toolinfo); // Calculate checksum so it can be compared with old checksum / future checksums const unsigned int checksum = preprocessor.calculateChecksum(tokens1, toolinfo.str()); std::list errors; if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, checksum, &errors)) { while (!errors.empty()) { reportErr(errors.front()); errors.pop_front(); } return mExitCode; // known results => no need to reanalyze file } } // Get directives preprocessor.setDirectives(tokens1); preprocessor.simplifyPragmaAsm(&tokens1); preprocessor.setPlatformInfo(&tokens1); // Get configurations.. if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty()) || mSettings.force) { Timer t("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults); configurations = preprocessor.getConfigs(tokens1); } else { configurations.insert(mSettings.userDefines); } if (mSettings.checkConfiguration) { for (const std::string &config : configurations) (void)preprocessor.getcode(tokens1, config, files, true); return 0; } // Run define rules on raw code for (const Settings::Rule &rule : mSettings.rules) { if (rule.tokenlist != "define") continue; std::string code; const std::list &directives = preprocessor.getDirectives(); for (const Directive &dir : directives) { if (dir.str.compare(0,8,"#define ") == 0) code += "#line " + MathLib::toString(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n'; } Tokenizer tokenizer2(&mSettings, this); std::istringstream istr2(code); tokenizer2.list.createTokens(istr2); executeRules("define", tokenizer2); break; } if (!mSettings.force && configurations.size() > mSettings.maxConfigs) { if (mSettings.isEnabled(Settings::INFORMATION)) { tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size()); } else { mTooManyConfigs = true; } } std::set checksums; unsigned int checkCount = 0; bool hasValidConfig = false; std::list configurationError; for (const std::string &currCfg : configurations) { // bail out if terminated if (Settings::terminated()) break; // Check only a few configurations (default 12), after that bail out, unless --force // was used. if (!mSettings.force && ++checkCount > mSettings.maxConfigs) break; if (!mSettings.userDefines.empty()) { mCurrentConfig = mSettings.userDefines; const std::vector v1(split(mSettings.userDefines, ";")); for (const std::string &cfg: split(currCfg, ";")) { if (std::find(v1.begin(), v1.end(), cfg) == v1.end()) { mCurrentConfig += ";" + cfg; } } } else { mCurrentConfig = currCfg; } if (mSettings.preprocessOnly) { Timer t("Preprocessor::getcode", mSettings.showtime, &s_timerResults); std::string codeWithoutCfg = preprocessor.getcode(tokens1, mCurrentConfig, files, true); t.stop(); if (codeWithoutCfg.compare(0,5,"#file") == 0) codeWithoutCfg.insert(0U, "//"); std::string::size_type pos = 0; while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos) codeWithoutCfg.insert(pos+1U, "//"); pos = 0; while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos) codeWithoutCfg.insert(pos+1U, "//"); pos = 0; while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos) codeWithoutCfg[pos] = ' '; reportOut(codeWithoutCfg); continue; } Tokenizer mTokenizer(&mSettings, this); if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE) mTokenizer.setTimerResults(&s_timerResults); try { bool result; // Create tokens, skip rest of iteration if failed Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults); const simplecpp::TokenList &tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true); mTokenizer.createTokens(&tokensP); timer.stop(); hasValidConfig = true; // If only errors are printed, print filename after the check if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) { std::string fixedpath = Path::simplifyPath(filename); fixedpath = Path::toNativeSeparators(fixedpath); mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "..."); } if (tokensP.empty()) continue; // skip rest of iteration if just checking configuration if (mSettings.checkConfiguration) continue; // Check raw tokens checkRawTokens(mTokenizer); // Simplify tokens into normal form, skip rest of iteration if failed Timer timer2("Tokenizer::simplifyTokens1", mSettings.showtime, &s_timerResults); result = mTokenizer.simplifyTokens1(mCurrentConfig); timer2.stop(); if (!result) continue; // dump xml if --dump if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { fdump << "" << std::endl; fdump << " " << std::endl; fdump << " " << std::endl; fdump << " " << std::endl; fdump << " " << std::endl; preprocessor.dump(fdump); mTokenizer.dump(fdump); fdump << "" << std::endl; } // Skip if we already met the same simplified token list if (mSettings.force || mSettings.maxConfigs > 1) { const unsigned long long checksum = mTokenizer.list.calculateChecksum(); if (checksums.find(checksum) != checksums.end()) { if (mSettings.debugwarnings) purgedConfigurationMessage(filename, mCurrentConfig); continue; } checksums.insert(checksum); } // Check normal tokens checkNormalTokens(mTokenizer); // Analyze info.. if (!mSettings.buildDir.empty()) checkUnusedFunctions.parseTokens(mTokenizer, filename.c_str(), &mSettings); // simplify more if required, skip rest of iteration if failed if (mSimplify && hasRule("simple")) { // if further simplification fails then skip rest of iteration Timer timer3("Tokenizer::simplifyTokenList2", mSettings.showtime, &s_timerResults); result = mTokenizer.simplifyTokenList2(); timer3.stop(); if (!result) continue; if (!Settings::terminated()) executeRules("simple", mTokenizer); } } catch (const simplecpp::Output &o) { // #error etc during preprocessing configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + MathLib::toString(o.location.line) + "] " + o.msg); --checkCount; // don't count invalid configurations continue; } catch (const InternalError &e) { std::list locationList; if (e.token) { ErrorLogger::ErrorMessage::FileLocation loc(e.token, &mTokenizer.list); locationList.push_back(loc); } else { ErrorLogger::ErrorMessage::FileLocation loc(mTokenizer.list.getSourceFilePath(), 0, 0); ErrorLogger::ErrorMessage::FileLocation loc2(filename, 0, 0); locationList.push_back(loc2); locationList.push_back(loc); } ErrorLogger::ErrorMessage errmsg(locationList, mTokenizer.list.getSourceFilePath(), Severity::error, e.errorMessage, e.id, false); if (errmsg.severity == Severity::error || mSettings.isEnabled(errmsg.severity)) reportErr(errmsg); } } if (!hasValidConfig && configurations.size() > 1 && mSettings.isEnabled(Settings::INFORMATION)) { std::string msg; msg = "This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details."; msg += "\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:"; for (const std::string &s : configurationError) msg += '\n' + s; std::list locationList; ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(Path::toNativeSeparators(filename)); locationList.push_back(loc); ErrorLogger::ErrorMessage errmsg(locationList, loc.getfile(), Severity::information, msg, "noValidConfiguration", false); reportErr(errmsg); } // dumped all configs, close root element now if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) fdump << "" << std::endl; if (!mSettings.addons.empty()) { fdump.close(); for (const std::string &addon : mSettings.addons) { struct AddonInfo addonInfo; const std::string &failedToGetAddonInfo = addonInfo.getAddonInfo(addon, mSettings.exename); if (!failedToGetAddonInfo.empty()) { reportOut(failedToGetAddonInfo); mExitCode = 1; continue; } const std::string results = executeAddon(addonInfo, dumpFile); std::istringstream istr(results); std::string line; while (std::getline(istr, line)) { if (line.compare(0,1,"{") != 0) continue; picojson::value res; std::istringstream istr2(line); istr2 >> res; if (!res.is()) continue; picojson::object obj = res.get(); const std::string fileName = obj["file"].get(); const int64_t lineNumber = obj["linenr"].get(); const int64_t column = obj["column"].get(); ErrorLogger::ErrorMessage errmsg; errmsg.callStack.emplace_back(ErrorLogger::ErrorMessage::FileLocation(fileName, lineNumber, column)); errmsg.id = obj["addon"].get() + "-" + obj["errorId"].get(); const std::string text = obj["message"].get(); errmsg.setmsg(text); const std::string severity = obj["severity"].get(); errmsg.severity = Severity::fromString(severity); if (errmsg.severity == Severity::SeverityType::none) continue; errmsg.file0 = fileName; reportErr(errmsg); } } std::remove(dumpFile.c_str()); } } catch (const std::runtime_error &e) { internalError(filename, e.what()); } catch (const std::bad_alloc &e) { internalError(filename, e.what()); } catch (const InternalError &e) { internalError(filename, e.errorMessage); mExitCode=1; // e.g. reflect a syntax error } mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo()); mAnalyzerInformation.close(); // In jointSuppressionReport mode, unmatched suppressions are // collected after all files are processed if (!mSettings.jointSuppressionReport && (mSettings.isEnabled(Settings::INFORMATION) || mSettings.checkConfiguration)) { reportUnmatchedSuppressions(mSettings.nomsg.getUnmatchedLocalSuppressions(filename, isUnusedFunctionCheckEnabled())); } mErrorList.clear(); return mExitCode; } void CppCheck::internalError(const std::string &filename, const std::string &msg) { const std::string fixedpath = Path::toNativeSeparators(filename); const std::string fullmsg("Bailing out from checking " + fixedpath + " since there was an internal error: " + msg); if (mSettings.isEnabled(Settings::INFORMATION)) { const ErrorLogger::ErrorMessage::FileLocation loc1(filename, 0, 0); std::list callstack(1, loc1); ErrorLogger::ErrorMessage errmsg(callstack, emptyString, Severity::information, fullmsg, "internalError", false); mErrorLogger.reportErr(errmsg); } else { // Report on stdout mErrorLogger.reportOut(fullmsg); } } //--------------------------------------------------------------------------- // CppCheck - A function that checks a raw token list //--------------------------------------------------------------------------- void CppCheck::checkRawTokens(const Tokenizer &tokenizer) { // Execute rules for "raw" code executeRules("raw", tokenizer); } //--------------------------------------------------------------------------- // CppCheck - A function that checks a normal token list //--------------------------------------------------------------------------- void CppCheck::checkNormalTokens(const Tokenizer &tokenizer) { // call all "runChecks" in all registered Check classes for (Check *check : Check::instances()) { if (Settings::terminated()) return; if (Tokenizer::isMaxTime()) return; Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults); check->runChecks(&tokenizer, &mSettings, this); } // Verification using ExprEngine.. if (mSettings.verification) { ExprEngine::runChecks(this, &tokenizer, &mSettings); } // Analyse the tokens.. CTU::FileInfo *fi1 = CTU::getFileInfo(&tokenizer); if (fi1) { mFileInfo.push_back(fi1); mAnalyzerInformation.setFileInfo("ctu", fi1->toString()); } for (const Check *check : Check::instances()) { Check::FileInfo *fi = check->getFileInfo(&tokenizer, &mSettings); if (fi != nullptr) { mFileInfo.push_back(fi); mAnalyzerInformation.setFileInfo(check->name(), fi->toString()); } } executeRules("normal", tokenizer); } //--------------------------------------------------------------------------- bool CppCheck::hasRule(const std::string &tokenlist) const { #ifdef HAVE_RULES for (const Settings::Rule &rule : mSettings.rules) { if (rule.tokenlist == tokenlist) return true; } #else (void)tokenlist; #endif return false; } #ifdef HAVE_RULES static const char * pcreErrorCodeToString(const int pcreExecRet) { switch (pcreExecRet) { case PCRE_ERROR_NULL: return "Either code or subject was passed as NULL, or ovector was NULL " "and ovecsize was not zero (PCRE_ERROR_NULL)"; case PCRE_ERROR_BADOPTION: return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)"; case PCRE_ERROR_BADMAGIC: return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, " "to catch the case when it is passed a junk pointer and to detect when a " "pattern that was compiled in an environment of one endianness is run in " "an environment with the other endianness. This is the error that PCRE " "gives when the magic number is not present (PCRE_ERROR_BADMAGIC)"; case PCRE_ERROR_UNKNOWN_NODE: return "While running the pattern match, an unknown item was encountered in the " "compiled pattern. This error could be caused by a bug in PCRE or by " "overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)"; case PCRE_ERROR_NOMEMORY: return "If a pattern contains back references, but the ovector that is passed " "to pcre_exec() is not big enough to remember the referenced substrings, " "PCRE gets a block of memory at the start of matching to use for this purpose. " "If the call via pcre_malloc() fails, this error is given. The memory is " "automatically freed at the end of matching. This error is also given if " "pcre_stack_malloc() fails in pcre_exec(). " "This can happen only when PCRE has been compiled with " "--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)"; case PCRE_ERROR_NOSUBSTRING: return "This error is used by the pcre_copy_substring(), pcre_get_substring(), " "and pcre_get_substring_list() functions (see below). " "It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)"; case PCRE_ERROR_MATCHLIMIT: return "The backtracking limit, as specified by the match_limit field in a pcre_extra " "structure (or defaulted) was reached. " "See the description above (PCRE_ERROR_MATCHLIMIT)"; case PCRE_ERROR_CALLOUT: return "This error is never generated by pcre_exec() itself. " "It is provided for use by callout functions that want to yield a distinctive " "error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)"; case PCRE_ERROR_BADUTF8: return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, " "and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector " "(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 " "character is placed in the first element, and a reason code is placed in the " "second element. The reason codes are listed in the following section. For " "backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated " "UTF-8 character at the end of the subject (reason codes 1 to 5), " "PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8"; case PCRE_ERROR_BADUTF8_OFFSET: return "The UTF-8 byte sequence that was passed as a subject was checked and found to " "be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of " "startoffset did not point to the beginning of a UTF-8 character or the end of " "the subject (PCRE_ERROR_BADUTF8_OFFSET)"; case PCRE_ERROR_PARTIAL: return "The subject string did not match, but it did match partially. See the " "pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)"; case PCRE_ERROR_BADPARTIAL: return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL " "option was used with a compiled pattern containing items that were not supported " "for partial matching. From release 8.00 onwards, there are no restrictions on " "partial matching (PCRE_ERROR_BADPARTIAL)"; case PCRE_ERROR_INTERNAL: return "An unexpected internal error has occurred. This error could be caused by a bug " "in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)"; case PCRE_ERROR_BADCOUNT: return"This error is given if the value of the ovecsize argument is negative " "(PCRE_ERROR_BADCOUNT)"; case PCRE_ERROR_RECURSIONLIMIT : return "The internal recursion limit, as specified by the match_limit_recursion " "field in a pcre_extra structure (or defaulted) was reached. " "See the description above (PCRE_ERROR_RECURSIONLIMIT)"; case PCRE_ERROR_DFA_UITEM: return "PCRE_ERROR_DFA_UITEM"; case PCRE_ERROR_DFA_UCOND: return "PCRE_ERROR_DFA_UCOND"; case PCRE_ERROR_DFA_WSSIZE: return "PCRE_ERROR_DFA_WSSIZE"; case PCRE_ERROR_DFA_RECURSE: return "PCRE_ERROR_DFA_RECURSE"; case PCRE_ERROR_NULLWSLIMIT: return "PCRE_ERROR_NULLWSLIMIT"; case PCRE_ERROR_BADNEWLINE: return "An invalid combination of PCRE_NEWLINE_xxx options was " "given (PCRE_ERROR_BADNEWLINE)"; case PCRE_ERROR_BADOFFSET: return "The value of startoffset was negative or greater than the length " "of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)"; case PCRE_ERROR_SHORTUTF8: return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject " "string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. " "Information about the failure is returned as for PCRE_ERROR_BADUTF8. " "It is in fact sufficient to detect this case, but this special error code for " "PCRE_PARTIAL_HARD precedes the implementation of returned information; " "it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)"; case PCRE_ERROR_RECURSELOOP: return "This error is returned when pcre_exec() detects a recursion loop " "within the pattern. Specifically, it means that either the whole pattern " "or a subpattern has been called recursively for the second time at the same " "position in the subject string. Some simple patterns that might do this " "are detected and faulted at compile time, but more complicated cases, " "in particular mutual recursions between two different subpatterns, " "cannot be detected until run time (PCRE_ERROR_RECURSELOOP)"; case PCRE_ERROR_JIT_STACKLIMIT: return "This error is returned when a pattern that was successfully studied " "using a JIT compile option is being matched, but the memory available " "for the just-in-time processing stack is not large enough. See the pcrejit " "documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)"; case PCRE_ERROR_BADMODE: return "This error is given if a pattern that was compiled by the 8-bit library " "is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)"; case PCRE_ERROR_BADENDIANNESS: return "This error is given if a pattern that was compiled and saved is reloaded on a " "host with different endianness. The utility function pcre_pattern_to_host_byte_order() " "can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)"; case PCRE_ERROR_DFA_BADRESTART: return "PCRE_ERROR_DFA_BADRESTART"; #if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32 case PCRE_ERROR_BADLENGTH: return "This error is given if pcre_exec() is called with a negative value for the length argument (PCRE_ERROR_BADLENGTH)"; case PCRE_ERROR_JIT_BADOPTION: return "This error is returned when a pattern that was successfully studied using a JIT compile " "option is being matched, but the matching mode (partial or complete match) does not correspond " "to any JIT compilation mode. When the JIT fast path function is used, this error may be " "also given for invalid options. See the pcrejit documentation for more details (PCRE_ERROR_JIT_BADOPTION)"; #endif } return ""; } #endif // HAVE_RULES void CppCheck::executeRules(const std::string &tokenlist, const Tokenizer &tokenizer) { (void)tokenlist; (void)tokenizer; #ifdef HAVE_RULES // There is no rule to execute if (!hasRule(tokenlist)) return; // Write all tokens in a string that can be parsed by pcre std::ostringstream ostr; for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) ostr << " " << tok->str(); const std::string str(ostr.str()); for (const Settings::Rule &rule : mSettings.rules) { if (rule.pattern.empty() || rule.id.empty() || rule.severity == Severity::none || rule.tokenlist != tokenlist) continue; const char *pcreCompileErrorStr = nullptr; int erroffset = 0; pcre * const re = pcre_compile(rule.pattern.c_str(),0,&pcreCompileErrorStr,&erroffset,nullptr); if (!re) { if (pcreCompileErrorStr) { const std::string msg = "pcre_compile failed: " + std::string(pcreCompileErrorStr); const ErrorLogger::ErrorMessage errmsg(std::list(), emptyString, Severity::error, msg, "pcre_compile", false); reportErr(errmsg); } continue; } // Optimize the regex, but only if PCRE_CONFIG_JIT is available #ifdef PCRE_CONFIG_JIT const char *pcreStudyErrorStr = nullptr; pcre_extra * const pcreExtra = pcre_study(re, PCRE_STUDY_JIT_COMPILE, &pcreStudyErrorStr); // pcre_study() returns NULL for both errors and when it can not optimize the regex. // The last argument is how one checks for errors. // It is NULL if everything works, and points to an error string otherwise. if (pcreStudyErrorStr) { const std::string msg = "pcre_study failed: " + std::string(pcreStudyErrorStr); const ErrorLogger::ErrorMessage errmsg(std::list(), emptyString, Severity::error, msg, "pcre_study", false); reportErr(errmsg); // pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile(). pcre_free(re); continue; } #else const pcre_extra * const pcreExtra = nullptr; #endif int pos = 0; int ovector[30]= {0}; while (pos < (int)str.size()) { const int pcreExecRet = pcre_exec(re, pcreExtra, str.c_str(), (int)str.size(), pos, 0, ovector, 30); if (pcreExecRet < 0) { const std::string errorMessage = pcreErrorCodeToString(pcreExecRet); if (!errorMessage.empty()) { const ErrorLogger::ErrorMessage errmsg(std::list(), emptyString, Severity::error, std::string("pcre_exec failed: ") + errorMessage, "pcre_exec", false); reportErr(errmsg); } break; } const unsigned int pos1 = (unsigned int)ovector[0]; const unsigned int pos2 = (unsigned int)ovector[1]; // jump to the end of the match for the next pcre_exec pos = (int)pos2; // determine location.. ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(tokenizer.list.getSourceFilePath()); loc.line = 0; std::size_t len = 0; for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { len = len + 1U + tok->str().size(); if (len > pos1) { loc.setfile(tokenizer.list.getFiles().at(tok->fileIndex())); loc.line = tok->linenr(); break; } } const std::list callStack(1, loc); // Create error message std::string summary; if (rule.summary.empty()) summary = "found '" + str.substr(pos1, pos2 - pos1) + "'"; else summary = rule.summary; const ErrorLogger::ErrorMessage errmsg(callStack, tokenizer.list.getSourceFilePath(), rule.severity, summary, rule.id, false); // Report error reportErr(errmsg); } pcre_free(re); #ifdef PCRE_CONFIG_JIT // Free up the EXTRA PCRE value (may be NULL at this point) if (pcreExtra) { pcre_free_study(pcreExtra); } #endif } #endif } Settings &CppCheck::settings() { return mSettings; } void CppCheck::tooManyConfigsError(const std::string &file, const std::size_t numberOfConfigurations) { if (!mSettings.isEnabled(Settings::INFORMATION) && !mTooManyConfigs) return; mTooManyConfigs = false; if (mSettings.isEnabled(Settings::INFORMATION) && file.empty()) return; std::list loclist; if (!file.empty()) { ErrorLogger::ErrorMessage::FileLocation location; location.setfile(file); loclist.push_back(location); } std::ostringstream msg; msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs; if (numberOfConfigurations > mSettings.maxConfigs) msg << " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.\n"; if (file.empty()) msg << " configurations. Use --force to check all configurations. For more details, use --enable=information.\n"; msg << "The checking of the file will be interrupted because there are too many " "#ifdef configurations. Checking of all #ifdef configurations can be forced " "by --force command line option or from GUI preferences. However that may " "increase the checking time."; if (file.empty()) msg << " For more details, use --enable=information."; ErrorLogger::ErrorMessage errmsg(loclist, emptyString, Severity::information, msg.str(), "toomanyconfigs", CWE398, false); reportErr(errmsg); } void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration) { mTooManyConfigs = false; if (mSettings.isEnabled(Settings::INFORMATION) && file.empty()) return; std::list loclist; if (!file.empty()) { ErrorLogger::ErrorMessage::FileLocation location; location.setfile(file); loclist.push_back(location); } ErrorLogger::ErrorMessage errmsg(loclist, emptyString, Severity::information, "The configuration '" + configuration + "' was not checked because its code equals another one.", "purgedConfiguration", false); reportErr(errmsg); } //--------------------------------------------------------------------------- void CppCheck::reportErr(const ErrorLogger::ErrorMessage &msg) { mSuppressInternalErrorFound = false; if (!mSettings.library.reportErrors(msg.file0)) return; const std::string errmsg = msg.toString(mSettings.verbose); if (errmsg.empty()) return; // Alert only about unique errors if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) != mErrorList.end()) return; const Suppressions::ErrorMessage errorMessage = msg.toSuppressionsErrorMessage(); if (mUseGlobalSuppressions) { if (mSettings.nomsg.isSuppressed(errorMessage)) { mSuppressInternalErrorFound = true; return; } } else { if (mSettings.nomsg.isSuppressedLocal(errorMessage)) { mSuppressInternalErrorFound = true; return; } } if (!mSettings.nofail.isSuppressed(errorMessage) && !mSettings.nomsg.isSuppressed(errorMessage)) { mExitCode = 1; } mErrorList.push_back(errmsg); mErrorLogger.reportErr(msg); mAnalyzerInformation.reportErr(msg, mSettings.verbose); if (!mSettings.plistOutput.empty() && plistFile.is_open()) { plistFile << ErrorLogger::plistData(msg); } } void CppCheck::reportOut(const std::string &outmsg) { mErrorLogger.reportOut(outmsg); } void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value) { mErrorLogger.reportProgress(filename, stage, value); } void CppCheck::reportInfo(const ErrorLogger::ErrorMessage &msg) { const Suppressions::ErrorMessage &errorMessage = msg.toSuppressionsErrorMessage(); if (!mSettings.nomsg.isSuppressed(errorMessage)) mErrorLogger.reportInfo(msg); } void CppCheck::reportStatus(unsigned int /*fileindex*/, unsigned int /*filecount*/, std::size_t /*sizedone*/, std::size_t /*sizetotal*/) { } void CppCheck::getErrorMessages() { Settings s(mSettings); s.addEnabled("warning"); s.addEnabled("style"); s.addEnabled("portability"); s.addEnabled("performance"); s.addEnabled("information"); purgedConfigurationMessage("",""); mTooManyConfigs = true; tooManyConfigsError("",0U); // call all "getErrorMessages" in all registered Check classes for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) (*it)->getErrorMessages(this, &s); Preprocessor::getErrorMessages(this, &s); } bool CppCheck::analyseWholeProgram() { bool errors = false; // Init CTU CTU::maxCtuDepth = mSettings.maxCtuDepth; // Analyse the tokens CTU::FileInfo ctu; for (const Check::FileInfo *fi : mFileInfo) { const CTU::FileInfo *fi2 = dynamic_cast(fi); if (fi2) { ctu.functionCalls.insert(ctu.functionCalls.end(), fi2->functionCalls.begin(), fi2->functionCalls.end()); ctu.nestedCalls.insert(ctu.nestedCalls.end(), fi2->nestedCalls.begin(), fi2->nestedCalls.end()); } } for (Check *check : Check::instances()) errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, *this); // TODO: ctu return errors && (mExitCode > 0); } void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map &files) { (void)files; if (buildDir.empty()) return; if (mSettings.isEnabled(Settings::UNUSED_FUNCTION)) CheckUnusedFunctions::analyseWholeProgram(this, buildDir); std::list fileInfoList; CTU::FileInfo ctuFileInfo; // Load all analyzer info data.. const std::string filesTxt(buildDir + "/files.txt"); std::ifstream fin(filesTxt); std::string filesTxtLine; while (std::getline(fin, filesTxtLine)) { const std::string::size_type firstColon = filesTxtLine.find(':'); if (firstColon == std::string::npos) continue; const std::string::size_type lastColon = filesTxtLine.rfind(':'); if (firstColon == lastColon) continue; const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon); //const std::string sourcefile = filesTxtLine.substr(lastColon+1); tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); if (error != tinyxml2::XML_SUCCESS) continue; const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); if (rootNode == nullptr) continue; for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "FileInfo") != 0) continue; const char *checkClassAttr = e->Attribute("check"); if (!checkClassAttr) continue; if (std::strcmp(checkClassAttr, "ctu") == 0) { ctuFileInfo.loadFromXml(e); continue; } for (Check *check : Check::instances()) { if (checkClassAttr == check->name()) fileInfoList.push_back(check->loadFileInfoFromXml(e)); } } } // Set CTU max depth CTU::maxCtuDepth = mSettings.maxCtuDepth; // Analyse the tokens for (Check *check : Check::instances()) check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this); for (Check::FileInfo *fi : fileInfoList) delete fi; } bool CppCheck::isUnusedFunctionCheckEnabled() const { return (mSettings.jobs == 1 && mSettings.isEnabled(Settings::UNUSED_FUNCTION)); } cppcheck-1.90/lib/cppcheck.h000066400000000000000000000161141357737443600157500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef cppcheckH #define cppcheckH //--------------------------------------------------------------------------- #include "analyzerinfo.h" #include "check.h" #include "config.h" #include "errorlogger.h" #include "importproject.h" #include "settings.h" #include #include #include #include #include class Tokenizer; /// @addtogroup Core /// @{ /** * @brief This is the base class which will use other classes to do * static code analysis for C and C++ code to find possible * errors or places that could be improved. * Usage: See check() for more info. */ class CPPCHECKLIB CppCheck : ErrorLogger { public: /** * @brief Constructor. */ CppCheck(ErrorLogger &errorLogger, bool useGlobalSuppressions); /** * @brief Destructor. */ ~CppCheck() OVERRIDE; /** * @brief This starts the actual checking. Note that you must call * parseFromArgs() or settings() and addFile() before calling this. * @return amount of errors found or 0 if none were found. */ /** * @brief Check the file. * This function checks one given file for errors. * @param path Path to the file to check. * @return amount of errors found or 0 if none were found. * @note You must set settings before calling this function (by calling * settings()). */ unsigned int check(const std::string &path); unsigned int check(const ImportProject::FileSettings &fs); /** * @brief Check the file. * This function checks one "virtual" file. The file is not read from * the disk but the content is given in @p content. In errors the @p path * is used as a filename. * @param path Path to the file to check. * @param content File content as a string. * @return amount of errors found or 0 if none were found. * @note You must set settings before calling this function (by calling * settings()). */ unsigned int check(const std::string &path, const std::string &content); /** * @brief Get reference to current settings. * @return a reference to current settings */ Settings &settings(); /** * @brief Returns current version number as a string. * @return version, e.g. "1.38" */ static const char * version(); /** * @brief Returns extra version info as a string. * This is for returning extra version info, like Git commit id, build * time/date etc. * @return extra version info, e.g. "04d42151" (Git commit id). */ static const char * extraVersion(); virtual void reportStatus(unsigned int fileindex, unsigned int filecount, std::size_t sizedone, std::size_t sizetotal); /** * @brief Terminate checking. The checking will be terminated as soon as possible. */ void terminate() { Settings::terminate(); } /** * @brief Call all "getErrorMessages" in all registered Check classes. * Also print out XML header and footer. */ void getErrorMessages(); void tooManyConfigsError(const std::string &file, const std::size_t numberOfConfigurations); void purgedConfigurationMessage(const std::string &file, const std::string& configuration); void dontSimplify() { mSimplify = false; } /** Analyse whole program, run this after all TUs has been scanned. * This is deprecated and the plan is to remove this when * .analyzeinfo is good enough. * Return true if an error is reported. */ bool analyseWholeProgram(); /** analyse whole program use .analyzeinfo files */ void analyseWholeProgram(const std::string &buildDir, const std::map &files); /** Check if the user wants to check for unused functions * and if it's possible at all */ bool isUnusedFunctionCheckEnabled() const; private: /** Are there "simple" rules */ bool hasRule(const std::string &tokenlist) const; /** @brief There has been an internal error => Report information message */ void internalError(const std::string &filename, const std::string &msg); /** * @brief Check a file using stream * @param filename file name * @param cfgname cfg name * @param fileStream stream the file content can be read from * @return number of errors found */ unsigned int checkFile(const std::string& filename, const std::string &cfgname, std::istream& fileStream); /** * @brief Check raw tokens * @param tokenizer tokenizer instance */ void checkRawTokens(const Tokenizer &tokenizer); /** * @brief Check normal tokens * @param tokenizer tokenizer instance */ void checkNormalTokens(const Tokenizer &tokenizer); /** * @brief Execute rules, if any * @param tokenlist token list to use (normal / simple) * @param tokenizer tokenizer */ void executeRules(const std::string &tokenlist, const Tokenizer &tokenizer); /** * @brief Errors and warnings are directed here. * * @param msg Errors messages are normally in format * "[filepath:line number] Message", e.g. * "[main.cpp:4] Uninitialized member variable" */ void reportErr(const ErrorLogger::ErrorMessage &msg) OVERRIDE; /** * @brief Information about progress is directed here. * * @param outmsg Message to show, e.g. "Checking main.cpp..." */ void reportOut(const std::string &outmsg) OVERRIDE; std::list mErrorList; Settings mSettings; void reportProgress(const std::string &filename, const char stage[], const std::size_t value) OVERRIDE; /** * Output information messages. */ void reportInfo(const ErrorLogger::ErrorMessage &msg) OVERRIDE; ErrorLogger &mErrorLogger; /** @brief Current preprocessor configuration */ std::string mCurrentConfig; unsigned int mExitCode; bool mSuppressInternalErrorFound; bool mUseGlobalSuppressions; /** Are there too many configs? */ bool mTooManyConfigs; /** Simplify code? true by default */ bool mSimplify; /** File info used for whole program analysis */ std::list mFileInfo; AnalyzerInformation mAnalyzerInformation; }; /// @} //--------------------------------------------------------------------------- #endif // cppcheckH cppcheck-1.90/lib/cppcheck.natvis000066400000000000000000000043451357737443600170300ustar00rootroot00000000000000 {mStr} {front->mStr} - {back->mStr} {mTokensFrontBack.front->mStr} - {mTokensFrontBack.back->mStr} mFiles pCurr pCurr = pCurr->mNext {mNameToken->mStr} {tokenDef->mStr} {num} ? {type}: {className} {platformType} {files[fileIndex]} : {line} [col={col}] {intvalue} (INT) {tokvalue} (TOK) {floatValue} (FLOAT) [UNKNOWN] {name} = {value} (value_known=true) {name} = ? cppcheck-1.90/lib/cppcheck.vcxproj000066400000000000000000001011211357737443600172050ustar00rootroot00000000000000 Debug-PCRE Win32 Debug-PCRE x64 Debug Win32 Debug x64 Release-PCRE Win32 Release-PCRE x64 Release Win32 Release x64 Create Create Create Create Create Create Create Create {C183DB5B-AD6C-423D-80CA-1F9549555A1A} cppcheck_lib 10.0 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 DynamicLibrary Unicode false v142 ..\bin\debug\ ..\bin\debug\ ..\bin\debug\ ..\bin\debug\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ cppcheck-core cppcheck-core cppcheck-core cppcheck-core false false false false ..\bin\ ..\bin\ ..\bin\ ..\bin\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ cppcheck-core cppcheck-core cppcheck-core cppcheck-core false false false false true true true true ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4018;4244;4251;4389;4482;4512;4701 MultiThreadedDebugDLL Use check.h check.h true stdcpplatest ../externals;%(AdditionalLibraryDirectories) true true false true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4018;4244;4251;4389;4482;4512;4701 MultiThreadedDebugDLL Use check.h check.h true stdcpplatest pcre.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4018;4244;4251;4389;4482;4512;4701 MultiThreadedDebugDLL Use check.h check.h true stdcpplatest ../externals;%(AdditionalLibraryDirectories) true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y ProgramDatabase true Disabled CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) Level4 ..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4018;4244;4251;4389;4482;4512;4701 MultiThreadedDebugDLL Use check.h check.h true stdcpplatest pcre64.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4018;4244;4251;4389;4482;4512;4701 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDLL true false Use check.h check.h /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest ../externals;%(AdditionalLibraryDirectories) false true true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4018;4244;4251;4389;4482;4512;4701 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;TIXML_USE_STL;%(PreprocessorDefinitions) MultiThreadedDLL true false Use check.h check.h /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest pcre.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false true true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4018;4244;4251;4389;4482;4512;4701 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true false Use check.h check.h /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest ../externals;%(AdditionalLibraryDirectories) true true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y MaxSpeed Level4 AnySuitable true Speed true true true ..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) 4018;4244;4251;4389;4482;4512;4701 CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true false Use check.h check.h /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest pcre64.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false true true true 8000000 8000000 xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y cppcheck-1.90/lib/cppcheck.vcxproj.filters000066400000000000000000000253051357737443600206650ustar00rootroot00000000000000 {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files cppcheck-1.90/lib/ctu.cpp000066400000000000000000000577251357737443600153330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "ctu.h" #include "astutils.h" #include "symboldatabase.h" #include #include // back_inserter //--------------------------------------------------------------------------- static const char ATTR_CALL_ID[] = "call-id"; static const char ATTR_CALL_FUNCNAME[] = "call-funcname"; static const char ATTR_CALL_ARGNR[] = "call-argnr"; static const char ATTR_CALL_ARGEXPR[] = "call-argexpr"; static const char ATTR_CALL_ARGVALUETYPE[] = "call-argvaluetype"; static const char ATTR_CALL_ARGVALUE[] = "call-argvalue"; static const char ATTR_WARNING[] = "warning"; static const char ATTR_LOC_FILENAME[] = "file"; static const char ATTR_LOC_LINENR[] = "line"; static const char ATTR_LOC_COLUMN[] = "col"; static const char ATTR_INFO[] = "info"; static const char ATTR_MY_ID[] = "my-id"; static const char ATTR_MY_ARGNR[] = "my-argnr"; static const char ATTR_MY_ARGNAME[] = "my-argname"; static const char ATTR_VALUE[] = "value"; int CTU::maxCtuDepth = 2; std::string CTU::getFunctionId(const Tokenizer *tokenizer, const Function *function) { return tokenizer->list.file(function->tokenDef) + ':' + MathLib::toString(function->tokenDef->linenr()) + ':' + MathLib::toString(function->tokenDef->column()); } CTU::FileInfo::Location::Location(const Tokenizer *tokenizer, const Token *tok) : fileName(tokenizer->list.file(tok)) , lineNumber(tok->linenr()) , column(tok->column()) { } std::string CTU::FileInfo::toString() const { std::ostringstream out; // Function calls.. for (const CTU::FileInfo::FunctionCall &functionCall : functionCalls) { out << functionCall.toXmlString(); } // Nested calls.. for (const CTU::FileInfo::NestedCall &nestedCall : nestedCalls) { out << nestedCall.toXmlString() << "\n"; } return out.str(); } std::string CTU::FileInfo::CallBase::toBaseXmlString() const { std::ostringstream out; out << " " << ATTR_CALL_ID << "=\"" << callId << "\"" << " " << ATTR_CALL_FUNCNAME << "=\"" << callFunctionName << "\"" << " " << ATTR_CALL_ARGNR << "=\"" << callArgNr << "\"" << " " << ATTR_LOC_FILENAME << "=\"" << location.fileName << "\"" << " " << ATTR_LOC_LINENR << "=\"" << location.lineNumber << "\"" << " " << ATTR_LOC_COLUMN << "=\"" << location.column << "\""; return out.str(); } std::string CTU::FileInfo::FunctionCall::toXmlString() const { std::ostringstream out; out << ""; else { out << ">\n"; for (const ErrorLogger::ErrorMessage::FileLocation &loc : callValuePath) out << " \n"; out << ""; } return out.str(); } std::string CTU::FileInfo::NestedCall::toXmlString() const { std::ostringstream out; out << ""; return out.str(); } std::string CTU::FileInfo::UnsafeUsage::toString() const { std::ostringstream out; out << " \n"; return out.str(); } std::string CTU::toString(const std::list &unsafeUsage) { std::ostringstream ret; for (const CTU::FileInfo::UnsafeUsage &u : unsafeUsage) ret << u.toString(); return ret.str(); } CTU::FileInfo::CallBase::CallBase(const Tokenizer *tokenizer, const Token *callToken) : callId(getFunctionId(tokenizer, callToken->function())) , callArgNr(0) , callFunctionName(callToken->next()->astOperand1()->expressionString()) , location(CTU::FileInfo::Location(tokenizer, callToken)) { } CTU::FileInfo::NestedCall::NestedCall(const Tokenizer *tokenizer, const Function *myFunction, const Token *callToken) : CallBase(tokenizer, callToken) , myId(getFunctionId(tokenizer, myFunction)) , myArgNr(0) { } static std::string readAttrString(const tinyxml2::XMLElement *e, const char *attr, bool *error) { const char *value = e->Attribute(attr); if (!value && error) *error = true; return value ? value : ""; } static long long readAttrInt(const tinyxml2::XMLElement *e, const char *attr, bool *error) { const char *value = e->Attribute(attr); if (!value && error) *error = true; return value ? std::atoi(value) : 0; } bool CTU::FileInfo::CallBase::loadBaseFromXml(const tinyxml2::XMLElement *xmlElement) { bool error = false; callId = readAttrString(xmlElement, ATTR_CALL_ID, &error); callFunctionName = readAttrString(xmlElement, ATTR_CALL_FUNCNAME, &error); callArgNr = readAttrInt(xmlElement, ATTR_CALL_ARGNR, &error); location.fileName = readAttrString(xmlElement, ATTR_LOC_FILENAME, &error); location.lineNumber = readAttrInt(xmlElement, ATTR_LOC_LINENR, &error); location.column = readAttrInt(xmlElement, ATTR_LOC_COLUMN, &error); return !error; } bool CTU::FileInfo::FunctionCall::loadFromXml(const tinyxml2::XMLElement *xmlElement) { if (!loadBaseFromXml(xmlElement)) return false; bool error=false; callArgumentExpression = readAttrString(xmlElement, ATTR_CALL_ARGEXPR, &error); callValueType = (ValueFlow::Value::ValueType)readAttrInt(xmlElement, ATTR_CALL_ARGVALUETYPE, &error); callArgValue = readAttrInt(xmlElement, ATTR_CALL_ARGVALUE, &error); const char *w = xmlElement->Attribute(ATTR_WARNING); warning = w && std::strcmp(w, "true") == 0; for (const tinyxml2::XMLElement *e2 = xmlElement->FirstChildElement(); !error && e2; e2 = e2->NextSiblingElement()) { if (std::strcmp(e2->Name(), "path") != 0) continue; ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(readAttrString(e2, ATTR_LOC_FILENAME, &error)); loc.line = readAttrInt(e2, ATTR_LOC_LINENR, &error); loc.column = readAttrInt(e2, ATTR_LOC_COLUMN, &error); loc.setinfo(readAttrString(e2, ATTR_INFO, &error)); } return !error; } bool CTU::FileInfo::NestedCall::loadFromXml(const tinyxml2::XMLElement *xmlElement) { if (!loadBaseFromXml(xmlElement)) return false; bool error = false; myId = readAttrString(xmlElement, ATTR_MY_ID, &error); myArgNr = readAttrInt(xmlElement, ATTR_MY_ARGNR, &error); return !error; } void CTU::FileInfo::loadFromXml(const tinyxml2::XMLElement *xmlElement) { for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "function-call") == 0) { FunctionCall functionCall; if (functionCall.loadFromXml(e)) functionCalls.push_back(functionCall); } else if (std::strcmp(e->Name(), "nested-call") == 0) { NestedCall nestedCall; if (nestedCall.loadFromXml(e)) nestedCalls.push_back(nestedCall); } } } std::map> CTU::FileInfo::getCallsMap() const { std::map> ret; for (const CTU::FileInfo::NestedCall &nc : nestedCalls) ret[nc.callId].push_back(&nc); for (const CTU::FileInfo::FunctionCall &fc : functionCalls) ret[fc.callId].push_back(&fc); return ret; } std::list CTU::loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement) { std::list ret; for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "unsafe-usage") != 0) continue; bool error = false; FileInfo::UnsafeUsage unsafeUsage; unsafeUsage.myId = readAttrString(e, ATTR_MY_ID, &error); unsafeUsage.myArgNr = readAttrInt(e, ATTR_MY_ARGNR, &error); unsafeUsage.myArgumentName = readAttrString(e, ATTR_MY_ARGNAME, &error); unsafeUsage.location.fileName = readAttrString(e, ATTR_LOC_FILENAME, &error); unsafeUsage.location.lineNumber = readAttrInt(e, ATTR_LOC_LINENR, &error); unsafeUsage.location.column = readAttrInt(e, ATTR_LOC_COLUMN, &error); unsafeUsage.value = readAttrInt(e, ATTR_VALUE, &error); if (!error) ret.push_back(unsafeUsage); } return ret; } static int isCallFunction(const Scope *scope, int argnr, const Token **tok) { const Variable * const argvar = scope->function->getArgumentVar(argnr); if (!argvar->isPointer()) return -1; for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { if (tok2->variable() != argvar) continue; if (!Token::Match(tok2->previous(), "[(,] %var% [,)]")) break; int argnr2 = 1; const Token *prev = tok2; while (prev && prev->str() != "(") { if (Token::Match(prev,"]|)")) prev = prev->link(); else if (prev->str() == ",") ++argnr2; prev = prev->previous(); } if (!prev || !Token::Match(prev->previous(), "%name% (")) break; if (!prev->astOperand1() || !prev->astOperand1()->function()) break; *tok = prev->previous(); return argnr2; } return -1; } CTU::FileInfo *CTU::getFileInfo(const Tokenizer *tokenizer) { const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); FileInfo *fileInfo = new FileInfo; // Parse all functions in TU for (const Scope &scope : symbolDatabase->scopeList) { if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function) continue; const Function *const function = scope.function; // source function calls for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand2()) continue; if (!tok->astOperand1()->function()) continue; const std::vector args(getArguments(tok->previous())); for (int argnr = 0; argnr < args.size(); ++argnr) { const Token *argtok = args[argnr]; if (!argtok) continue; for (const ValueFlow::Value &value : argtok->values()) { if ((!value.isIntValue() || value.intvalue != 0 || value.isInconclusive()) && !value.isBufferSizeValue()) continue; // Skip impossible values since they cannot be represented if (value.isImpossible()) continue; FileInfo::FunctionCall functionCall; functionCall.callValueType = value.valueType; functionCall.callId = getFunctionId(tokenizer, tok->astOperand1()->function()); functionCall.callFunctionName = tok->astOperand1()->expressionString(); functionCall.location = FileInfo::Location(tokenizer,tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); functionCall.callArgValue = value.intvalue; functionCall.warning = !value.errorSeverity(); for (const ErrorPathItem &i : value.errorPath) { ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile(tokenizer->list.file(i.first)); loc.line = i.first->linenr(); loc.column = i.first->column(); loc.setinfo(i.second); functionCall.callValuePath.push_back(loc); } fileInfo->functionCalls.push_back(functionCall); } // array if (argtok->variable() && argtok->variable()->isArray() && argtok->variable()->dimensions().size()==1 && argtok->variable()->dimension(0)>1) { FileInfo::FunctionCall functionCall; functionCall.callValueType = ValueFlow::Value::ValueType::BUFFER_SIZE; functionCall.callId = getFunctionId(tokenizer, tok->astOperand1()->function()); functionCall.callFunctionName = tok->astOperand1()->expressionString(); functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); functionCall.callArgValue = argtok->variable()->dimension(0) * argtok->valueType()->typeSize(*tokenizer->getSettings()); functionCall.warning = false; fileInfo->functionCalls.push_back(functionCall); } // &var => buffer if (argtok->isUnaryOp("&") && argtok->astOperand1()->variable() && argtok->astOperand1()->valueType() && !argtok->astOperand1()->variable()->isArray()) { FileInfo::FunctionCall functionCall; functionCall.callValueType = ValueFlow::Value::ValueType::BUFFER_SIZE; functionCall.callId = getFunctionId(tokenizer, tok->astOperand1()->function()); functionCall.callFunctionName = tok->astOperand1()->expressionString(); functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgumentExpression = argtok->expressionString(); functionCall.callArgValue = argtok->astOperand1()->valueType()->typeSize(*tokenizer->getSettings()); functionCall.warning = false; fileInfo->functionCalls.push_back(functionCall); } // pointer to uninitialized data.. if (!argtok->isUnaryOp("&")) continue; argtok = argtok->astOperand1(); if (!argtok || !argtok->valueType() || argtok->valueType()->pointer != 0) continue; if (argtok->values().size() != 1U) continue; const ValueFlow::Value &v = argtok->values().front(); if (v.valueType == ValueFlow::Value::ValueType::UNINIT && !v.isInconclusive()) { FileInfo::FunctionCall functionCall; functionCall.callValueType = ValueFlow::Value::ValueType::UNINIT; functionCall.callId = getFunctionId(tokenizer, tok->astOperand1()->function()); functionCall.callFunctionName = tok->astOperand1()->expressionString(); functionCall.location = FileInfo::Location(tokenizer, tok); functionCall.callArgNr = argnr + 1; functionCall.callArgValue = 0; functionCall.callArgumentExpression = argtok->expressionString(); functionCall.warning = false; fileInfo->functionCalls.push_back(functionCall); continue; } } } // Nested function calls for (int argnr = 0; argnr < function->argCount(); ++argnr) { const Token *tok; const int argnr2 = isCallFunction(&scope, argnr, &tok); if (argnr2 > 0) { FileInfo::NestedCall nestedCall(tokenizer, function, tok); nestedCall.myArgNr = argnr + 1; nestedCall.callArgNr = argnr2; fileInfo->nestedCalls.push_back(nestedCall); } } } return fileInfo; } static std::list> getUnsafeFunction(const Tokenizer *tokenizer, const Settings *settings, const Scope *scope, int argnr, const Check *check, bool (*isUnsafeUsage)(const Check *check, const Token *argtok, MathLib::bigint *value)) { std::list> ret; const Variable * const argvar = scope->function->getArgumentVar(argnr); if (!argvar->isPointer()) return ret; for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { if (Token::Match(tok2, ")|else {")) { tok2 = tok2->linkAt(1); if (Token::findmatch(tok2->link(), "return|throw", tok2)) return ret; if (isVariableChanged(tok2->link(), tok2, argvar->declarationId(), false, settings, tokenizer->isCPP())) return ret; } if (Token::Match(tok2, "%oror%|&&|?")) { tok2 = tok2->findExpressionStartEndTokens().second; continue; } if (tok2->variable() != argvar) continue; MathLib::bigint value = 0; if (!isUnsafeUsage(check, tok2, &value)) return ret; // TODO: Is this a read? then continue.. ret.emplace_back(tok2, value); return ret; } return ret; } std::list CTU::getUnsafeUsage(const Tokenizer *tokenizer, const Settings *settings, const Check *check, bool (*isUnsafeUsage)(const Check *check, const Token *argtok, MathLib::bigint *_value)) { std::list unsafeUsage; // Parse all functions in TU const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); for (const Scope &scope : symbolDatabase->scopeList) { if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function) continue; const Function *const function = scope.function; // "Unsafe" functions unconditionally reads data before it is written.. for (int argnr = 0; argnr < function->argCount(); ++argnr) { for (const std::pair &v : getUnsafeFunction(tokenizer, settings, &scope, argnr, check, isUnsafeUsage)) { const Token *tok = v.first; MathLib::bigint value = v.second; unsafeUsage.emplace_back(CTU::getFunctionId(tokenizer, function), argnr+1, tok->str(), CTU::FileInfo::Location(tokenizer,tok), value); } } } return unsafeUsage; } static bool findPath(const std::string &callId, nonneg int callArgNr, MathLib::bigint unsafeValue, CTU::FileInfo::InvalidValueType invalidValue, const std::map> &callsMap, const CTU::FileInfo::CallBase *path[10], int index, bool warning) { if (index >= CTU::maxCtuDepth || index >= 10) return false; const std::map>::const_iterator it = callsMap.find(callId); if (it == callsMap.end()) return false; for (const CTU::FileInfo::CallBase *c : it->second) { if (c->callArgNr != callArgNr) continue; const CTU::FileInfo::FunctionCall *functionCall = dynamic_cast(c); if (functionCall) { if (!warning && functionCall->warning) continue; switch (invalidValue) { case CTU::FileInfo::InvalidValueType::null: if (functionCall->callValueType != ValueFlow::Value::ValueType::INT || functionCall->callArgValue != 0) continue; break; case CTU::FileInfo::InvalidValueType::uninit: if (functionCall->callValueType != ValueFlow::Value::ValueType::UNINIT) continue; break; case CTU::FileInfo::InvalidValueType::bufferOverflow: if (functionCall->callValueType != ValueFlow::Value::ValueType::BUFFER_SIZE) continue; if (unsafeValue < 0 || unsafeValue >= functionCall->callArgValue) break; continue; }; path[index] = functionCall; return true; } const CTU::FileInfo::NestedCall *nestedCall = dynamic_cast(c); if (!nestedCall) continue; if (findPath(nestedCall->myId, nestedCall->myArgNr, unsafeValue, invalidValue, callsMap, path, index + 1, warning)) { path[index] = nestedCall; return true; } } return false; } std::list CTU::FileInfo::getErrorPath(InvalidValueType invalidValue, const CTU::FileInfo::UnsafeUsage &unsafeUsage, const std::map> &callsMap, const char info[], const FunctionCall * * const functionCallPtr, bool warning) const { std::list locationList; const CTU::FileInfo::CallBase *path[10] = {nullptr}; if (!findPath(unsafeUsage.myId, unsafeUsage.myArgNr, unsafeUsage.value, invalidValue, callsMap, path, 0, warning)) return locationList; const std::string value1 = (invalidValue == InvalidValueType::null) ? "null" : "uninitialized"; for (int index = 9; index >= 0; index--) { if (!path[index]) continue; const CTU::FileInfo::FunctionCall *functionCall = dynamic_cast(path[index]); if (functionCall) { if (functionCallPtr) *functionCallPtr = functionCall; std::copy(functionCall->callValuePath.cbegin(), functionCall->callValuePath.cend(), std::back_inserter(locationList)); } ErrorLogger::ErrorMessage::FileLocation fileLoc(path[index]->location.fileName, path[index]->location.lineNumber, path[index]->location.column); fileLoc.setinfo("Calling function " + path[index]->callFunctionName + ", " + MathLib::toString(path[index]->callArgNr) + getOrdinalText(path[index]->callArgNr) + " argument is " + value1); locationList.push_back(fileLoc); } ErrorLogger::ErrorMessage::FileLocation fileLoc2(unsafeUsage.location.fileName, unsafeUsage.location.lineNumber, unsafeUsage.location.column); fileLoc2.setinfo(replaceStr(info, "ARG", unsafeUsage.myArgumentName)); locationList.push_back(fileLoc2); return locationList; } cppcheck-1.90/lib/ctu.h000066400000000000000000000126031357737443600147620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef ctuH #define ctuH //--------------------------------------------------------------------------- #include "check.h" #include "valueflow.h" /// @addtogroup Core /// @{ /** @brief Whole program analysis (ctu=Cross Translation Unit) */ namespace CTU { class CPPCHECKLIB FileInfo : public Check::FileInfo { public: enum class InvalidValueType { null, uninit, bufferOverflow }; std::string toString() const OVERRIDE; struct Location { Location() = default; Location(const Tokenizer *tokenizer, const Token *tok); Location(const std::string &fileName, nonneg int lineNumber, nonneg int column) : fileName(fileName), lineNumber(lineNumber), column(column) {} std::string fileName; nonneg int lineNumber; nonneg int column; }; struct UnsafeUsage { UnsafeUsage() = default; UnsafeUsage(const std::string &myId, nonneg int myArgNr, const std::string &myArgumentName, const Location &location, MathLib::bigint value) : myId(myId), myArgNr(myArgNr), myArgumentName(myArgumentName), location(location), value(value) {} std::string myId; nonneg int myArgNr; std::string myArgumentName; Location location; MathLib::bigint value; std::string toString() const; }; class CallBase { public: CallBase() = default; CallBase(const std::string &callId, int callArgNr, const std::string &callFunctionName, const Location &loc) : callId(callId), callArgNr(callArgNr), callFunctionName(callFunctionName), location(loc) {} CallBase(const Tokenizer *tokenizer, const Token *callToken); virtual ~CallBase() {} std::string callId; int callArgNr; std::string callFunctionName; Location location; protected: std::string toBaseXmlString() const; bool loadBaseFromXml(const tinyxml2::XMLElement *xmlElement); }; class FunctionCall : public CallBase { public: std::string callArgumentExpression; MathLib::bigint callArgValue; ValueFlow::Value::ValueType callValueType; std::vector callValuePath; bool warning; std::string toXmlString() const; bool loadFromXml(const tinyxml2::XMLElement *xmlElement); }; class NestedCall : public CallBase { public: NestedCall() = default; NestedCall(const std::string &myId, nonneg int myArgNr, const std::string &callId, nonneg int callArgnr, const std::string &callFunctionName, const Location &location) : CallBase(callId, callArgnr, callFunctionName, location), myId(myId), myArgNr(myArgNr) { } NestedCall(const Tokenizer *tokenizer, const Function *myFunction, const Token *callToken); std::string toXmlString() const; bool loadFromXml(const tinyxml2::XMLElement *xmlElement); std::string myId; nonneg int myArgNr; }; std::list functionCalls; std::list nestedCalls; void loadFromXml(const tinyxml2::XMLElement *xmlElement); std::map> getCallsMap() const; std::list getErrorPath(InvalidValueType invalidValue, const UnsafeUsage &unsafeUsage, const std::map> &callsMap, const char info[], const FunctionCall * * const functionCallPtr, bool warning) const; }; extern int maxCtuDepth; CPPCHECKLIB std::string toString(const std::list &unsafeUsage); CPPCHECKLIB std::string getFunctionId(const Tokenizer *tokenizer, const Function *function); /** @brief Parse current TU and extract file info */ CPPCHECKLIB FileInfo *getFileInfo(const Tokenizer *tokenizer); CPPCHECKLIB std::list getUnsafeUsage(const Tokenizer *tokenizer, const Settings *settings, const Check *check, bool (*isUnsafeUsage)(const Check *check, const Token *argtok, MathLib::bigint *value)); CPPCHECKLIB std::list loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement); } /// @} //--------------------------------------------------------------------------- #endif // ctuH cppcheck-1.90/lib/errorlogger.cpp000066400000000000000000000721051357737443600170560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "errorlogger.h" #include "cppcheck.h" #include "mathlib.h" #include "path.h" #include "token.h" #include "tokenlist.h" #include "utils.h" #include #include #include #include #include #include #include InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type type) : token(tok), errorMessage(errorMsg), type(type) { switch (type) { case AST: id = "internalAstError"; break; case SYNTAX: id = "syntaxError"; break; case UNKNOWN_MACRO: id = "unknownMacro"; break; case INTERNAL: id = "cppcheckError"; break; case LIMIT: id = "cppcheckLimit"; break; case INSTANTIATION: id = "instantiationError"; break; } } ErrorLogger::ErrorMessage::ErrorMessage() : severity(Severity::none), cwe(0U), inconclusive(false) { } ErrorLogger::ErrorMessage::ErrorMessage(const std::list &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, bool inconclusive) : callStack(callStack), // locations for this error message id(id), // set the message id file0(file1), severity(severity), // severity for this error message cwe(0U), inconclusive(inconclusive) { // set the summary and verbose messages setmsg(msg); } ErrorLogger::ErrorMessage::ErrorMessage(const std::list &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, const CWE &cwe, bool inconclusive) : callStack(callStack), // locations for this error message id(id), // set the message id file0(file1), severity(severity), // severity for this error message cwe(cwe.id), inconclusive(inconclusive) { // set the summary and verbose messages setmsg(msg); } ErrorLogger::ErrorMessage::ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive) : id(id), severity(severity), cwe(0U), inconclusive(inconclusive) { // Format callstack for (std::list::const_iterator it = callstack.begin(); it != callstack.end(); ++it) { // --errorlist can provide null values here if (!(*it)) continue; callStack.emplace_back(*it, list); } if (list && !list->getFiles().empty()) file0 = list->getFiles()[0]; setmsg(msg); } ErrorLogger::ErrorMessage::ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, const CWE &cwe, bool inconclusive) : id(id), severity(severity), cwe(cwe.id), inconclusive(inconclusive) { // Format callstack for (std::list::const_iterator it = callstack.begin(); it != callstack.end(); ++it) { // --errorlist can provide null values here if (!(*it)) continue; callStack.emplace_back(*it, list); } if (list && !list->getFiles().empty()) file0 = list->getFiles()[0]; setmsg(msg); } ErrorLogger::ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, bool inconclusive) : id(id), severity(severity), cwe(cwe.id), inconclusive(inconclusive) { // Format callstack for (ErrorPath::const_iterator it = errorPath.begin(); it != errorPath.end(); ++it) { const Token *tok = it->first; const std::string &info = it->second; // --errorlist can provide null values here if (tok) callStack.emplace_back(tok, info, tokenList); } if (tokenList && !tokenList->getFiles().empty()) file0 = tokenList->getFiles()[0]; setmsg(msg); } ErrorLogger::ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg) : severity(Severity::none), cwe(0U), inconclusive(false) { const char * const unknown = ""; const char *attr = errmsg->Attribute("id"); id = attr ? attr : unknown; attr = errmsg->Attribute("severity"); severity = attr ? Severity::fromString(attr) : Severity::none; attr = errmsg->Attribute("cwe"); std::istringstream(attr ? attr : "0") >> cwe.id; attr = errmsg->Attribute("inconclusive"); inconclusive = attr && (std::strcmp(attr, "true") == 0); attr = errmsg->Attribute("msg"); mShortMessage = attr ? attr : ""; attr = errmsg->Attribute("verbose"); mVerboseMessage = attr ? attr : ""; for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(),"location")==0) { const char *strfile = e->Attribute("file"); const char *strinfo = e->Attribute("info"); const char *strline = e->Attribute("line"); const char *strcolumn = e->Attribute("column"); const char *file = strfile ? strfile : unknown; const char *info = strinfo ? strinfo : ""; const int line = strline ? std::atoi(strline) : 0; const int column = strcolumn ? std::atoi(strcolumn) : 0; callStack.emplace_back(file, info, line, column); } } } void ErrorLogger::ErrorMessage::setmsg(const std::string &msg) { // If a message ends to a '\n' and contains only a one '\n' // it will cause the mVerboseMessage to be empty which will show // as an empty message to the user if --verbose is used. // Even this doesn't cause problems with messages that have multiple // lines, none of the error messages should end into it. assert(!endsWith(msg,'\n')); // The summary and verbose message are separated by a newline // If there is no newline then both the summary and verbose messages // are the given message const std::string::size_type pos = msg.find('\n'); const std::string symbolName = mSymbolNames.empty() ? std::string() : mSymbolNames.substr(0, mSymbolNames.find('\n')); if (pos == std::string::npos) { mShortMessage = replaceStr(msg, "$symbol", symbolName); mVerboseMessage = replaceStr(msg, "$symbol", symbolName); } else if (msg.compare(0,8,"$symbol:") == 0) { mSymbolNames += msg.substr(8, pos-7); setmsg(msg.substr(pos + 1)); } else { mShortMessage = replaceStr(msg.substr(0, pos), "$symbol", symbolName); mVerboseMessage = replaceStr(msg.substr(pos + 1), "$symbol", symbolName); } } Suppressions::ErrorMessage ErrorLogger::ErrorMessage::toSuppressionsErrorMessage() const { Suppressions::ErrorMessage ret; ret.errorId = id; if (!callStack.empty()) { ret.setFileName(callStack.back().getfile(false)); ret.lineNumber = callStack.back().line; } ret.inconclusive = inconclusive; ret.symbolNames = mSymbolNames; return ret; } std::string ErrorLogger::ErrorMessage::serialize() const { // Serialize this message into a simple string std::ostringstream oss; oss << id.length() << " " << id; oss << Severity::toString(severity).length() << " " << Severity::toString(severity); oss << MathLib::toString(cwe.id).length() << " " << MathLib::toString(cwe.id); if (inconclusive) { const std::string inconclusive("inconclusive"); oss << inconclusive.length() << " " << inconclusive; } const std::string saneShortMessage = fixInvalidChars(mShortMessage); const std::string saneVerboseMessage = fixInvalidChars(mVerboseMessage); oss << saneShortMessage.length() << " " << saneShortMessage; oss << saneVerboseMessage.length() << " " << saneVerboseMessage; oss << callStack.size() << " "; for (std::list::const_iterator loc = callStack.begin(); loc != callStack.end(); ++loc) { std::ostringstream smallStream; smallStream << (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo(); oss << smallStream.str().length() << " " << smallStream.str(); } return oss.str(); } bool ErrorLogger::ErrorMessage::deserialize(const std::string &data) { inconclusive = false; callStack.clear(); std::istringstream iss(data); std::array results; std::size_t elem = 0; while (iss.good()) { unsigned int len = 0; if (!(iss >> len)) return false; iss.get(); std::string temp; for (unsigned int i = 0; i < len && iss.good(); ++i) { const char c = static_cast(iss.get()); temp.append(1, c); } if (temp == "inconclusive") { inconclusive = true; continue; } results[elem++] = temp; if (elem == 5) break; } if (elem != 5) throw InternalError(nullptr, "Internal Error: Deserialization of error message failed"); id = results[0]; severity = Severity::fromString(results[1]); std::istringstream scwe(results[2]); scwe >> cwe.id; mShortMessage = results[3]; mVerboseMessage = results[4]; unsigned int stackSize = 0; if (!(iss >> stackSize)) return false; while (iss.good()) { unsigned int len = 0; if (!(iss >> len)) return false; iss.get(); std::string temp; for (unsigned int i = 0; i < len && iss.good(); ++i) { const char c = static_cast(iss.get()); temp.append(1, c); } std::vector substrings; for (std::string::size_type pos = 0; pos < temp.size() && substrings.size() < 5; ++pos) { if (substrings.size() == 4) { substrings.push_back(temp.substr(pos)); break; } const std::string::size_type start = pos; pos = temp.find("\t", pos); if (pos == std::string::npos) { substrings.push_back(temp.substr(start)); break; } substrings.push_back(temp.substr(start, pos - start)); } if (substrings.size() < 4) throw InternalError(nullptr, "Internal Error: serializing/deserializing of error message failed!"); // (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo(); ErrorLogger::ErrorMessage::FileLocation loc(substrings[3], MathLib::toLongNumber(substrings[0]), MathLib::toLongNumber(substrings[1])); loc.setfile(substrings[2]); if (substrings.size() == 5) loc.setinfo(substrings[4]); callStack.push_back(loc); if (callStack.size() >= stackSize) break; } return true; } std::string ErrorLogger::ErrorMessage::getXMLHeader() { // xml_version 1 is the default xml format tinyxml2::XMLPrinter printer; // standard xml header printer.PushDeclaration("xml version=\"1.0\" encoding=\"UTF-8\""); // header printer.OpenElement("results", false); printer.PushAttribute("version", 2); printer.OpenElement("cppcheck", false); printer.PushAttribute("version", CppCheck::version()); printer.CloseElement(false); printer.OpenElement("errors", false); return std::string(printer.CStr()) + '>'; } std::string ErrorLogger::ErrorMessage::getXMLFooter() { return " \n"; } // There is no utf-8 support around but the strings should at least be safe for to tinyxml2. // See #5300 "Invalid encoding in XML output" and #6431 "Invalid XML created - Invalid encoding of string literal " std::string ErrorLogger::ErrorMessage::fixInvalidChars(const std::string& raw) { std::string result; result.reserve(raw.length()); std::string::const_iterator from=raw.begin(); while (from!=raw.end()) { if (std::isprint(static_cast(*from))) { result.push_back(*from); } else { std::ostringstream es; // straight cast to (unsigned) doesn't work out. const unsigned uFrom = (unsigned char)*from; es << '\\' << std::setbase(8) << std::setw(3) << std::setfill('0') << uFrom; result += es.str(); } ++from; } return result; } std::string ErrorLogger::ErrorMessage::toXML() const { tinyxml2::XMLPrinter printer(nullptr, false, 2); printer.OpenElement("error", false); printer.PushAttribute("id", id.c_str()); printer.PushAttribute("severity", Severity::toString(severity).c_str()); printer.PushAttribute("msg", fixInvalidChars(mShortMessage).c_str()); printer.PushAttribute("verbose", fixInvalidChars(mVerboseMessage).c_str()); if (cwe.id) printer.PushAttribute("cwe", cwe.id); if (inconclusive) printer.PushAttribute("inconclusive", "true"); for (std::list::const_reverse_iterator it = callStack.rbegin(); it != callStack.rend(); ++it) { printer.OpenElement("location", false); if (!file0.empty() && (*it).getfile() != file0) printer.PushAttribute("file0", Path::toNativeSeparators(file0).c_str()); printer.PushAttribute("file", (*it).getfile().c_str()); printer.PushAttribute("line", std::max((*it).line,0)); printer.PushAttribute("column", (*it).column); if (!it->getinfo().empty()) printer.PushAttribute("info", fixInvalidChars(it->getinfo()).c_str()); printer.CloseElement(false); } for (std::string::size_type pos = 0; pos < mSymbolNames.size();) { const std::string::size_type pos2 = mSymbolNames.find('\n', pos); std::string symbolName; if (pos2 == std::string::npos) { symbolName = mSymbolNames.substr(pos); pos = pos2; } else { symbolName = mSymbolNames.substr(pos, pos2-pos); pos = pos2 + 1; } printer.OpenElement("symbol", false); printer.PushText(symbolName.c_str()); printer.CloseElement(false); } printer.CloseElement(false); return printer.CStr(); } void ErrorLogger::ErrorMessage::findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith) { std::string::size_type index = 0; while ((index = source.find(searchFor, index)) != std::string::npos) { source.replace(index, searchFor.length(), replaceWith); index += replaceWith.length(); } } // TODO: read info from some shared resource instead? static std::string readCode(const std::string &file, int linenr, int column, const char endl[]) { std::ifstream fin(file); std::string line; while (linenr > 0 && std::getline(fin,line)) { linenr--; } const std::string::size_type endPos = line.find_last_not_of("\r\n\t "); if (endPos + 1 < line.size()) line.erase(endPos + 1); std::string::size_type pos = 0; while ((pos = line.find('\t', pos)) != std::string::npos) line[pos] = ' '; return line + endl + std::string((column>0 ? column-1 : column), ' ') + '^'; } std::string ErrorLogger::ErrorMessage::toString(bool verbose, const std::string &templateFormat, const std::string &templateLocation) const { // Save this ErrorMessage in plain text. // No template is given if (templateFormat.empty()) { std::ostringstream text; if (!callStack.empty()) text << callStackToString(callStack) << ": "; if (severity != Severity::none) { text << '(' << Severity::toString(severity); if (inconclusive) text << ", inconclusive"; text << ") "; } text << (verbose ? mVerboseMessage : mShortMessage); return text.str(); } // template is given. Reformat the output according to it std::string result = templateFormat; // Support a few special characters to allow to specific formatting, see http://sourceforge.net/apps/phpbb/cppcheck/viewtopic.php?f=4&t=494&sid=21715d362c0dbafd3791da4d9522f814 // Substitution should be done first so messages from cppcheck never get translated. findAndReplace(result, "\\b", "\b"); findAndReplace(result, "\\n", "\n"); findAndReplace(result, "\\r", "\r"); findAndReplace(result, "\\t", "\t"); findAndReplace(result, "{id}", id); if (result.find("{inconclusive:") != std::string::npos) { const std::string::size_type pos1 = result.find("{inconclusive:"); const std::string::size_type pos2 = result.find('}', pos1+1); const std::string replaceFrom = result.substr(pos1,pos2-pos1+1); const std::string replaceWith = inconclusive ? result.substr(pos1+14, pos2-pos1-14) : std::string(); findAndReplace(result, replaceFrom, replaceWith); } findAndReplace(result, "{severity}", Severity::toString(severity)); findAndReplace(result, "{cwe}", MathLib::toString(cwe.id)); findAndReplace(result, "{message}", verbose ? mVerboseMessage : mShortMessage); findAndReplace(result, "{callstack}", callStack.empty() ? emptyString : callStackToString(callStack)); if (!callStack.empty()) { findAndReplace(result, "{file}", callStack.back().getfile()); findAndReplace(result, "{line}", MathLib::toString(callStack.back().line)); findAndReplace(result, "{column}", MathLib::toString(callStack.back().column)); if (result.find("{code}") != std::string::npos) { const std::string::size_type pos = result.find('\r'); const char *endl; if (pos == std::string::npos) endl = "\n"; else if (pos+1 < result.size() && result[pos+1] == '\n') endl = "\r\n"; else endl = "\r"; findAndReplace(result, "{code}", readCode(callStack.back().getOrigFile(), callStack.back().line, callStack.back().column, endl)); } } else { findAndReplace(result, "{file}", "nofile"); findAndReplace(result, "{line}", "0"); findAndReplace(result, "{column}", "0"); findAndReplace(result, "{code}", emptyString); } if (!templateLocation.empty() && callStack.size() >= 2U) { for (const FileLocation &fileLocation : callStack) { std::string text = templateLocation; findAndReplace(text, "\\b", "\b"); findAndReplace(text, "\\n", "\n"); findAndReplace(text, "\\r", "\r"); findAndReplace(text, "\\t", "\t"); findAndReplace(text, "{file}", fileLocation.getfile()); findAndReplace(text, "{line}", MathLib::toString(fileLocation.line)); findAndReplace(text, "{column}", MathLib::toString(fileLocation.column)); findAndReplace(text, "{info}", fileLocation.getinfo().empty() ? mShortMessage : fileLocation.getinfo()); if (text.find("{code}") != std::string::npos) { const std::string::size_type pos = text.find('\r'); const char *endl; if (pos == std::string::npos) endl = "\n"; else if (pos+1 < text.size() && text[pos+1] == '\n') endl = "\r\n"; else endl = "\r"; findAndReplace(text, "{code}", readCode(fileLocation.getOrigFile(), fileLocation.line, fileLocation.column, endl)); } result += '\n' + text; } } return result; } bool ErrorLogger::reportUnmatchedSuppressions(const std::list &unmatched) { bool err = false; // Report unmatched suppressions for (const Suppressions::Suppression &s : unmatched) { // don't report "unmatchedSuppression" as unmatched if (s.errorId == "unmatchedSuppression") continue; // check if this unmatched suppression is suppressed bool suppressed = false; for (const Suppressions::Suppression &s2 : unmatched) { if (s2.errorId == "unmatchedSuppression") { if ((s2.fileName.empty() || s2.fileName == "*" || s2.fileName == s.fileName) && (s2.lineNumber == Suppressions::Suppression::NO_LINE || s2.lineNumber == s.lineNumber)) { suppressed = true; break; } } } if (suppressed) continue; std::list callStack; if (!s.fileName.empty()) callStack.emplace_back(s.fileName, s.lineNumber, 0); reportErr(ErrorLogger::ErrorMessage(callStack, emptyString, Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", false)); err = true; } return err; } std::string ErrorLogger::callStackToString(const std::list &callStack) { std::ostringstream ostr; for (std::list::const_iterator tok = callStack.begin(); tok != callStack.end(); ++tok) { ostr << (tok == callStack.begin() ? "" : " -> ") << tok->stringify(); } return ostr.str(); } ErrorLogger::ErrorMessage::FileLocation::FileLocation(const Token* tok, const TokenList* tokenList) : fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)) { } ErrorLogger::ErrorMessage::FileLocation::FileLocation(const Token* tok, const std::string &info, const TokenList* tokenList) : fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)), mInfo(info) { } std::string ErrorLogger::ErrorMessage::FileLocation::getfile(bool convert) const { if (convert) return Path::toNativeSeparators(mFileName); return mFileName; } std::string ErrorLogger::ErrorMessage::FileLocation::getOrigFile(bool convert) const { if (convert) return Path::toNativeSeparators(mOrigFileName); return mOrigFileName; } void ErrorLogger::ErrorMessage::FileLocation::setfile(const std::string &file) { mFileName = file; mFileName = Path::fromNativeSeparators(mFileName); mFileName = Path::simplifyPath(mFileName); } std::string ErrorLogger::ErrorMessage::FileLocation::stringify() const { std::ostringstream oss; oss << '[' << Path::toNativeSeparators(mFileName); if (line != Suppressions::Suppression::NO_LINE) oss << ':' << line; oss << ']'; return oss.str(); } std::string ErrorLogger::toxml(const std::string &str) { std::ostringstream xml; for (unsigned char c : str) { switch (c) { case '<': xml << "<"; break; case '>': xml << ">"; break; case '&': xml << "&"; break; case '\"': xml << """; break; case '\0': xml << "\\0"; break; default: if (c >= ' ' && c <= 0x7f) xml << c; else xml << 'x'; break; } } return xml.str(); } std::string ErrorLogger::plistHeader(const std::string &version, const std::vector &files) { std::ostringstream ostr; ostr << "\r\n" << "\r\n" << "\r\n" << "\r\n" << " clang_version\r\n" << "cppcheck version " << version << "\r\n" << " files\r\n" << " \r\n"; for (const std::string & file : files) ostr << " " << ErrorLogger::toxml(file) << "\r\n"; ostr << " \r\n" << " diagnostics\r\n" << " \r\n"; return ostr.str(); } static std::string plistLoc(const char indent[], const ErrorLogger::ErrorMessage::FileLocation &loc) { std::ostringstream ostr; ostr << indent << "\r\n" << indent << ' ' << "line" << loc.line << "\r\n" << indent << ' ' << "col" << loc.column << "\r\n" << indent << ' ' << "file" << loc.fileIndex << "\r\n" << indent << "\r\n"; return ostr.str(); } std::string ErrorLogger::plistData(const ErrorLogger::ErrorMessage &msg) { std::ostringstream plist; plist << " \r\n" << " path\r\n" << " \r\n"; std::list::const_iterator prev = msg.callStack.begin(); for (std::list::const_iterator it = msg.callStack.begin(); it != msg.callStack.end(); ++it) { if (prev != it) { plist << " \r\n" << " kindcontrol\r\n" << " edges\r\n" << " \r\n" << " \r\n" << " start\r\n" << " \r\n" << plistLoc(" ", *prev) << plistLoc(" ", *prev) << " \r\n" << " end\r\n" << " \r\n" << plistLoc(" ", *it) << plistLoc(" ", *it) << " \r\n" << " \r\n" << " \r\n" << " \r\n"; prev = it; } std::list::const_iterator next = it; ++next; const std::string message = (it->getinfo().empty() && next == msg.callStack.end() ? msg.shortMessage() : it->getinfo()); plist << " \r\n" << " kindevent\r\n" << " location\r\n" << plistLoc(" ", *it) << " ranges\r\n" << " \r\n" << " \r\n" << plistLoc(" ", *it) << plistLoc(" ", *it) << " \r\n" << " \r\n" << " depth0\r\n" << " extended_message\r\n" << " " << ErrorLogger::toxml(message) << "\r\n" << " message\r\n" << " " << ErrorLogger::toxml(message) << "\r\n" << " \r\n"; } plist << " \r\n" << " description" << ErrorLogger::toxml(msg.shortMessage()) << "\r\n" << " category" << Severity::toString(msg.severity) << "\r\n" << " type" << ErrorLogger::toxml(msg.shortMessage()) << "\r\n" << " check_name" << msg.id << "\r\n" << " \r\n" << " issue_hash_content_of_line_in_context" << 0 << "\r\n" << " issue_context_kind\r\n" << " issue_context\r\n" << " issue_hash_function_offset\r\n" << " location\r\n" << plistLoc(" ", msg.callStack.back()) << " \r\n"; return plist.str(); } std::string replaceStr(std::string s, const std::string &from, const std::string &to) { std::string::size_type pos1 = 0; while (pos1 < s.size()) { pos1 = s.find(from, pos1); if (pos1 == std::string::npos) return s; if (pos1 > 0 && (s[pos1-1] == '_' || std::isalnum(s[pos1-1]))) { pos1++; continue; } const std::string::size_type pos2 = pos1 + from.size(); if (pos2 >= s.size()) return s.substr(0,pos1) + to; if (s[pos2] == '_' || std::isalnum(s[pos2])) { pos1++; continue; } s = s.substr(0,pos1) + to + s.substr(pos2); pos1 += to.size(); } return s; } cppcheck-1.90/lib/errorlogger.h000066400000000000000000000336101357737443600165210ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef errorloggerH #define errorloggerH //--------------------------------------------------------------------------- #include "config.h" #include "suppressions.h" #include #include #include #include #include #include /** * CWE id (Common Weakness Enumeration) * See https://cwe.mitre.org/ for further reference. * */ struct CWE { explicit CWE(unsigned short cweId) : id(cweId) {} unsigned short id; }; class Token; class TokenList; namespace tinyxml2 { class XMLElement; } /// @addtogroup Core /// @{ /** @brief Simple container to be thrown when internal error is detected. */ struct InternalError { enum Type {AST, SYNTAX, UNKNOWN_MACRO, INTERNAL, LIMIT, INSTANTIATION}; InternalError(const Token *tok, const std::string &errorMsg, Type type = INTERNAL); const Token *token; std::string errorMessage; Type type; std::string id; }; /** @brief enum class for severity. Used when reporting errors. */ class CPPCHECKLIB Severity { public: /** * Message severities. */ enum SeverityType { /** * No severity (default value). */ none, /** * Programming error. * This indicates severe error like memory leak etc. * The error is certain. */ error, /** * Warning. * Used for dangerous coding style that can cause severe runtime errors. * For example: forgetting to initialize a member variable in a constructor. */ warning, /** * Style warning. * Used for general code cleanup recommendations. Fixing these * will not fix any bugs but will make the code easier to maintain. * For example: redundant code, unreachable code, etc. */ style, /** * Performance warning. * Not an error as is but suboptimal code and fixing it probably leads * to faster performance of the compiled code. */ performance, /** * Portability warning. * This warning indicates the code is not properly portable for * different platforms and bitnesses (32/64 bit). If the code is meant * to compile in different platforms and bitnesses these warnings * should be fixed. */ portability, /** * Checking information. * Information message about the checking (process) itself. These * messages inform about header files not found etc issues that are * not errors in the code but something user needs to know. */ information, /** * Debug message. * Debug-mode message useful for the developers. */ debug }; static std::string toString(SeverityType severity) { switch (severity) { case none: return ""; case error: return "error"; case warning: return "warning"; case style: return "style"; case performance: return "performance"; case portability: return "portability"; case information: return "information"; case debug: return "debug"; } throw InternalError(nullptr, "Unknown severity"); } static SeverityType fromString(const std::string &severity) { if (severity.empty()) return none; if (severity == "none") return none; if (severity == "error") return error; if (severity == "warning") return warning; if (severity == "style") return style; if (severity == "performance") return performance; if (severity == "portability") return portability; if (severity == "information") return information; if (severity == "debug") return debug; return none; } }; typedef std::pair ErrorPathItem; typedef std::list ErrorPath; /** * @brief This is an interface, which the class responsible of error logging * should implement. */ class CPPCHECKLIB ErrorLogger { protected: std::ofstream plistFile; public: /** * Wrapper for error messages, provided by reportErr() */ class CPPCHECKLIB ErrorMessage { public: /** * File name and line number. * Internally paths are stored with / separator. When getting the filename * it is by default converted to native separators. */ class CPPCHECKLIB FileLocation { public: FileLocation() : fileIndex(0), line(0), column(0) { } FileLocation(const std::string &file, int line, int column) : fileIndex(0), line(line), column(column), mOrigFileName(file), mFileName(file) { } FileLocation(const std::string &file, const std::string &info, int line, int column) : fileIndex(0), line(line), column(column), mOrigFileName(file), mFileName(file), mInfo(info) { } FileLocation(const Token* tok, const TokenList* tokenList); FileLocation(const Token* tok, const std::string &info, const TokenList* tokenList); /** * Return the filename. * @param convert If true convert path to native separators. * @return filename. */ std::string getfile(bool convert = true) const; /** * Filename with the whole path (no --rp) * @param convert If true convert path to native separators. * @return filename. */ std::string getOrigFile(bool convert = true) const; /** * Set the filename. * @param file Filename to set. */ void setfile(const std::string &file); /** * @return the location as a string. Format: [file:line] */ std::string stringify() const; unsigned int fileIndex; int line; // negative value means "no line" unsigned int column; std::string getinfo() const { return mInfo; } void setinfo(const std::string &i) { mInfo = i; } private: std::string mOrigFileName; std::string mFileName; std::string mInfo; }; ErrorMessage(const std::list &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, bool inconclusive); ErrorMessage(const std::list &callStack, const std::string& file1, Severity::SeverityType severity, const std::string &msg, const std::string &id, const CWE &cwe, bool inconclusive); ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive); ErrorMessage(const std::list& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, const CWE &cwe, bool inconclusive); ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, bool inconclusive); ErrorMessage(); explicit ErrorMessage(const tinyxml2::XMLElement * const errmsg); /** * Format the error message in XML format */ std::string toXML() const; static std::string getXMLHeader(); static std::string getXMLFooter(); /** * Format the error message into a string. * @param verbose use verbose message * @param templateFormat Empty string to use default output format * or template to be used. E.g. "{file}:{line},{severity},{id},{message}" * @param templateLocation Format Empty string to use default output format * or template to be used. E.g. "{file}:{line},{info}" * @return formatted string */ std::string toString(bool verbose, const std::string &templateFormat = emptyString, const std::string &templateLocation = emptyString) const; std::string serialize() const; bool deserialize(const std::string &data); std::list callStack; std::string id; /** source file (not header) */ std::string file0; Severity::SeverityType severity; CWE cwe; bool inconclusive; /** set short and verbose messages */ void setmsg(const std::string &msg); /** Short message (single line short message) */ const std::string &shortMessage() const { return mShortMessage; } /** Verbose message (may be the same as the short message) */ const std::string &verboseMessage() const { return mVerboseMessage; } /** Symbol names */ const std::string &symbolNames() const { return mSymbolNames; } Suppressions::ErrorMessage toSuppressionsErrorMessage() const; private: /** * Replace all occurrences of searchFor with replaceWith in the * given source. * @param source The string to modify * @param searchFor What should be searched for * @param replaceWith What will replace the found item */ static void findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith); static std::string fixInvalidChars(const std::string& raw); /** Short message */ std::string mShortMessage; /** Verbose message */ std::string mVerboseMessage; /** symbol names */ std::string mSymbolNames; }; ErrorLogger() { } virtual ~ErrorLogger() { if (plistFile.is_open()) { plistFile << ErrorLogger::plistFooter(); plistFile.close(); } } /** * Information about progress is directed here. * Override this to receive the progress messages. * * @param outmsg Message to show e.g. "Checking main.cpp..." */ virtual void reportOut(const std::string &outmsg) = 0; /** * Information about found errors and warnings is directed * here. Override this to receive the errormessages. * * @param msg Location and other information about the found error. */ virtual void reportErr(const ErrorLogger::ErrorMessage &msg) = 0; /** * Report progress to client * @param filename main file that is checked * @param stage for example preprocess / tokenize / simplify / check * @param value progress value (0-100) */ virtual void reportProgress(const std::string &filename, const char stage[], const std::size_t value) { (void)filename; (void)stage; (void)value; } /** * Output information messages. * @param msg Location and other information about the found error. */ virtual void reportInfo(const ErrorLogger::ErrorMessage &msg) { reportErr(msg); } /** * Report unmatched suppressions * @param unmatched list of unmatched suppressions (from Settings::Suppressions::getUnmatched(Local|Global)Suppressions) * @return true is returned if errors are reported */ bool reportUnmatchedSuppressions(const std::list &unmatched); static std::string callStackToString(const std::list &callStack); /** * Convert XML-sensitive characters into XML entities * @param str The input string containing XML-sensitive characters * @return The output string containing XML entities */ static std::string toxml(const std::string &str); static std::string plistHeader(const std::string &version, const std::vector &files); static std::string plistData(const ErrorLogger::ErrorMessage &msg); static const char *plistFooter() { return " \r\n" "\r\n" ""; } }; /** Replace substring. Example replaceStr("1,NR,3", "NR", "2") => "1,2,3" */ std::string replaceStr(std::string s, const std::string &from, const std::string &to); /// @} //--------------------------------------------------------------------------- #endif // errorloggerH cppcheck-1.90/lib/exprengine.cpp000066400000000000000000001662331357737443600166770ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "exprengine.h" #include "astutils.h" #include "settings.h" #include "symboldatabase.h" #include "tokenize.h" #include #include #include #include #include #ifdef USE_Z3 #include #endif namespace { struct VerifyException { VerifyException(const Token *tok, const std::string &what) : tok(tok), what(what) {} const Token *tok; const std::string what; }; } std::string ExprEngine::str(int128_t value) { std::ostringstream ostr; #ifdef __GNUC__ if (value == (int)value) { ostr << (int) value; return ostr.str(); } if (value < 0) { ostr << "-"; value = -value; } uint64_t high = value >> 64; uint64_t low = value; if (high > 0) ostr << "h" << std::hex << high << "l"; ostr << std::hex << low; #else ostr << value; #endif return ostr.str(); } static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform); namespace { class TrackExecution { public: TrackExecution() : mDataIndex(0) {} std::map> map; int getNewDataIndex() { return mDataIndex++; } void symbolRange(const Token *tok, ExprEngine::ValuePtr value) { if (!tok || !value) return; const std::string &symbolicExpression = value->getSymbolicExpression(); if (symbolicExpression[0] != '$') return; if (mSymbols.find(symbolicExpression) != mSymbols.end()) return; mSymbols.insert(symbolicExpression); map[tok].push_back(symbolicExpression + "=" + value->getRange()); } void state(const Token *tok, const std::string &s) { map[tok].push_back(s); } void print(std::ostream &out) { std::set> locations; for (auto it : map) { locations.insert(std::pair(it.first->linenr(), it.first->column())); } for (const std::pair &loc : locations) { int lineNumber = loc.first; int column = loc.second; for (auto &it : map) { const Token *tok = it.first; if (lineNumber != tok->linenr()) continue; if (column != tok->column()) continue; const std::vector &dumps = it.second; for (const std::string &dump : dumps) out << lineNumber << ":" << column << ": " << dump << "\n"; } } } private: int mDataIndex; std::set mSymbols; }; class Data : public ExprEngine::DataBase { public: Data(int *symbolValueIndex, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, TrackExecution *trackExecution) : DataBase(settings) , symbolValueIndex(symbolValueIndex) , tokenizer(tokenizer) , callbacks(callbacks) , mTrackExecution(trackExecution) , mDataIndex(trackExecution->getNewDataIndex()) {} typedef std::map> Memory; Memory memory; int * const symbolValueIndex; const Tokenizer * const tokenizer; const std::vector &callbacks; std::vector constraints; void assignValue(const Token *tok, unsigned int varId, ExprEngine::ValuePtr value) { if (varId == 0) return; mTrackExecution->symbolRange(tok, value); if (value) { if (auto arr = std::dynamic_pointer_cast(value)) { mTrackExecution->symbolRange(tok, arr->size); for (const auto &indexAndValue: arr->data) mTrackExecution->symbolRange(tok, indexAndValue.value); } else if (auto s = std::dynamic_pointer_cast(value)) { for (const auto &m: s->member) mTrackExecution->symbolRange(tok, m.second); } } memory[varId] = value; } void assignStructMember(const Token *tok, ExprEngine::StructValue *structVal, const std::string &memberName, ExprEngine::ValuePtr value) { mTrackExecution->symbolRange(tok, value); structVal->member[memberName] = value; } std::string getNewSymbolName() override { return "$" + std::to_string(++(*symbolValueIndex)); } std::shared_ptr getArrayValue(const Token *tok) { const Memory::iterator it = memory.find(tok->varId()); if (it != memory.end()) return std::dynamic_pointer_cast(it->second); if (tok->varId() == 0) return std::shared_ptr(); auto val = std::make_shared(this, tok->variable()); assignValue(tok, tok->varId(), val); return val; } ExprEngine::ValuePtr getValue(unsigned int varId, const ValueType *valueType, const Token *tok) { const Memory::const_iterator it = memory.find(varId); if (it != memory.end()) return it->second; if (!valueType) return ExprEngine::ValuePtr(); ExprEngine::ValuePtr value = getValueRangeFromValueType(getNewSymbolName(), valueType, *settings); if (value) { assignValue(tok, varId, value); } return value; } void trackProgramState(const Token *tok) { if (memory.empty()) return; const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); std::ostringstream s; s << "{"; // << mDataIndex << ":"; for (auto mem : memory) { ExprEngine::ValuePtr value = mem.second; const Variable *var = symbolDatabase->getVariableFromVarId(mem.first); if (!var) continue; s << " " << var->name() << "="; if (!value) s << "(null)"; else if (value->name[0] == '$' && value->getSymbolicExpression() != value->name) s << "(" << value->name << "," << value->getSymbolicExpression() << ")"; else s << value->name; } s << "}"; mTrackExecution->state(tok, s.str()); } ExprEngine::ValuePtr notValue(ExprEngine::ValuePtr v) { auto b = std::dynamic_pointer_cast(v); if (b) { std::string binop; if (b->binop == "==") binop = "!="; else if (b->binop == "!=") binop = "=="; else if (b->binop == ">=") binop = "<"; else if (b->binop == "<=") binop = ">"; else if (b->binop == ">") binop = "<="; else if (b->binop == "<") binop = ">="; if (!binop.empty()) return std::make_shared(binop, b->op1, b->op2); } auto zero = std::make_shared("0", 0, 0); return std::make_shared("==", v, zero); } void addConstraint(ExprEngine::ValuePtr condValue, bool trueCond) { if (!condValue) return; if (trueCond) constraints.push_back(condValue); else constraints.push_back(notValue(condValue)); } void addConstraint(ExprEngine::ValuePtr lhsValue, ExprEngine::ValuePtr rhsValue, bool equals) { if (!lhsValue || !rhsValue) return; constraints.push_back(std::make_shared(equals?"==":"!=", lhsValue, rhsValue)); } private: TrackExecution * const mTrackExecution; const int mDataIndex; }; } static ExprEngine::ValuePtr simplifyValue(ExprEngine::ValuePtr origValue) { auto b = std::dynamic_pointer_cast(origValue); if (!b) return origValue; if (!b->op1 || !b->op2) return origValue; auto intRange1 = std::dynamic_pointer_cast(b->op1); auto intRange2 = std::dynamic_pointer_cast(b->op2); if (intRange1 && intRange2 && intRange1->minValue == intRange1->maxValue && intRange2->minValue == intRange2->maxValue) { const std::string &binop = b->binop; int128_t v; if (binop == "+") v = intRange1->minValue + intRange2->minValue; else if (binop == "-") v = intRange1->minValue - intRange2->minValue; else if (binop == "*") v = intRange1->minValue * intRange2->minValue; else if (binop == "/" && intRange2->minValue != 0) v = intRange1->minValue / intRange2->minValue; else if (binop == "%" && intRange2->minValue != 0) v = intRange1->minValue % intRange2->minValue; else return origValue; return std::make_shared(ExprEngine::str(v), v, v); } return origValue; } static ExprEngine::ValuePtr translateUninitValueToRange(ExprEngine::ValuePtr value, const ::ValueType *valueType, Data &data) { if (!value) return value; if (value->type == ExprEngine::ValueType::UninitValue) { auto rangeValue = getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); if (rangeValue) return rangeValue; } if (auto conditionalValue = std::dynamic_pointer_cast(value)) { if (conditionalValue->values.size() == 1 && conditionalValue->values[0].second && conditionalValue->values[0].second->type == ExprEngine::ValueType::UninitValue) { auto rangeValue = getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); if (rangeValue) return rangeValue; } } return value; } static int128_t truncateInt(int128_t value, int bits, char sign) { value = value & (((int128_t)1 << bits) - 1); // Sign extension if (sign == 's' && value & (1ULL << (bits - 1))) value |= ~(((int128_t)1 << bits) - 1); return value; } ExprEngine::ArrayValue::ArrayValue(const std::string &name, ExprEngine::ValuePtr size, ExprEngine::ValuePtr value) : Value(name, ExprEngine::ValueType::ArrayValue) , size(size) { assign(ExprEngine::ValuePtr(), value); } ExprEngine::ArrayValue::ArrayValue(DataBase *data, const Variable *var) : Value(data->getNewSymbolName(), ExprEngine::ValueType::ArrayValue) { if (var) { int sz = 1; for (const auto &dim : var->dimensions()) { if (!dim.known) { sz = -1; break; } sz *= dim.num; } if (sz >= 1) size = std::make_shared(std::to_string(sz), sz, sz); } ValuePtr val; if (var && !var->isGlobal() && !var->isStatic()) val = std::make_shared(); else if (var && var->valueType()) { ::ValueType vt(*var->valueType()); vt.pointer = 0; val = getValueRangeFromValueType(data->getNewSymbolName(), &vt, *data->settings); } assign(ExprEngine::ValuePtr(), val); } void ExprEngine::ArrayValue::assign(ExprEngine::ValuePtr index, ExprEngine::ValuePtr value) { if (!index) data.clear(); if (value) { ExprEngine::ArrayValue::IndexAndValue indexAndValue = {index, value}; data.push_back(indexAndValue); } } void ExprEngine::ArrayValue::clear() { data.clear(); ExprEngine::ArrayValue::IndexAndValue indexAndValue = { ExprEngine::ValuePtr(), std::make_shared("0", 0, 0) }; data.push_back(indexAndValue); } static bool isEqual(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) { if (!v1 || !v2) return !v1 && !v2; return v1->name == v2->name; } static bool isNonOverlapping(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) { if (!v1 || !v2) return false; // Don't know! auto intRange1 = std::dynamic_pointer_cast(v1); auto intRange2 = std::dynamic_pointer_cast(v2); if (intRange1 && intRange2 && (intRange1->minValue > intRange2->maxValue || intRange1->maxValue < intRange2->maxValue)) return true; return false; } ExprEngine::ConditionalValue::Vector ExprEngine::ArrayValue::read(ExprEngine::ValuePtr index) const { ExprEngine::ConditionalValue::Vector ret; if (!index) return ret; for (const auto indexAndValue : data) { if (::isEqual(index, indexAndValue.index)) ret.clear(); if (isNonOverlapping(index, indexAndValue.index)) continue; // Array contains string literal data... if (!indexAndValue.index && indexAndValue.value->type == ExprEngine::ValueType::StringLiteralValue) { auto stringLiteral = std::dynamic_pointer_cast(indexAndValue.value); if (!stringLiteral) { ret.push_back(std::pair(indexAndValue.index, std::make_shared("", -128, 128))); continue; } if (auto i = std::dynamic_pointer_cast(index)) { if (stringLiteral && i->minValue >= 0 && i->minValue == i->maxValue) { int c = 0; if (i->minValue < stringLiteral->size()) c = stringLiteral->string[i->minValue]; ret.push_back(std::pair(indexAndValue.index, std::make_shared(std::to_string(c), c, c))); continue; } } int cmin = 0, cmax = 0; for (char c : stringLiteral->string) { if (c < cmin) cmin = c; else if (c > cmax) cmax = c; } ret.push_back(std::pair(indexAndValue.index, std::make_shared("", cmin, cmax))); continue; } // Rename IntRange if (auto i = std::dynamic_pointer_cast(indexAndValue.value)) { ret.push_back(std::pair(indexAndValue.index, std::make_shared(indexAndValue.value->name + ":" + index->name, i->minValue, i->maxValue))); continue; } ret.push_back(std::pair(indexAndValue.index, indexAndValue.value)); } if (ret.size() == 1) ret[0].first = ExprEngine::ValuePtr(); else if (ret.size() == 2 && !ret[0].first) { ret[0].first = std::make_shared("!=", index, ret[1].first); ret[1].first = std::make_shared("==", index, ret[1].first); } else { // FIXME!! ret.clear(); } return ret; } std::string ExprEngine::ConditionalValue::getSymbolicExpression() const { std::ostringstream ostr; ostr << "{"; bool first = true; for (auto condvalue : values) { ValuePtr cond = condvalue.first; ValuePtr value = condvalue.second; if (!first) ostr << ","; first = false; ostr << "{" << (cond ? cond->getSymbolicExpression() : std::string("(null)")) << "," << value->getSymbolicExpression() << "}"; } ostr << "}"; return ostr.str(); } std::string ExprEngine::ArrayValue::getSymbolicExpression() const { std::ostringstream ostr; ostr << "size=" << (size ? size->name : std::string("(null)")); for (const auto indexAndValue : data) { ostr << ",[" << (!indexAndValue.index ? std::string(":") : indexAndValue.index->name) << "]=" << indexAndValue.value->name; } return ostr.str(); } std::string ExprEngine::StructValue::getSymbolicExpression() const { std::ostringstream ostr; ostr << "{"; bool first = true; for (const auto& m: member) { const std::string &memberName = m.first; auto memberValue = m.second; if (!first) ostr << ","; first = false; ostr << memberName << "=" << (memberValue ? memberValue->getSymbolicExpression() : std::string("(null)")); } ostr << "}"; return ostr.str(); } std::string ExprEngine::PointerValue::getRange() const { std::string r; if (data) r = "->" + data->getSymbolicExpression(); if (null) r += std::string(r.empty() ? "" : ",") + "null"; if (uninitData) r += std::string(r.empty() ? "" : ",") + "->?"; return r; } std::string ExprEngine::IntegerTruncation::getSymbolicExpression() const { return sign + std::to_string(bits) + "(" + inputValue->getSymbolicExpression() + ")"; } #ifdef USE_Z3 struct ExprData { typedef std::map ValueExpr; typedef std::vector AssertionList; z3::context context; ValueExpr valueExpr; AssertionList assertionList; void addAssertions(z3::solver &solver) const { for (auto assertExpr : assertionList) solver.add(assertExpr); } z3::expr getExpr(const ExprEngine::BinOpResult *b) { auto op1 = getExpr(b->op1); auto op2 = getExpr(b->op2); if (b->binop == "+") return op1 + op2; if (b->binop == "-") return op1 - op2; if (b->binop == "*") return op1 * op2; if (b->binop == "/") return op1 / op2; if (b->binop == "%") return op1 % op2; if (b->binop == "==") return int_expr(op1) == int_expr(op2); if (b->binop == "!=") return op1 != op2; if (b->binop == ">=") return op1 >= op2; if (b->binop == "<=") return op1 <= op2; if (b->binop == ">") return op1 > op2; if (b->binop == "<") return op1 < op2; if (b->binop == "&&") return bool_expr(op1) && bool_expr(op2); if (b->binop == "||") return bool_expr(op1) || bool_expr(op2); if (b->binop == "<<") return op1 * z3::pw(context.int_val(2), op2); if (b->binop == ">>") return op1 / z3::pw(context.int_val(2), op2); throw VerifyException(nullptr, "Internal error: Unhandled operator " + b->binop); } z3::expr getExpr(ExprEngine::ValuePtr v) { if (!v) throw VerifyException(nullptr, "Can not solve expressions, operand value is null"); if (auto intRange = std::dynamic_pointer_cast(v)) { if (intRange->name[0] != '$') return context.int_val(int64_t(intRange->minValue)); auto it = valueExpr.find(v->name); if (it != valueExpr.end()) return it->second; auto e = context.int_const(v->name.c_str()); valueExpr.emplace(v->name, e); if (intRange->minValue >= INT_MIN && intRange->maxValue <= INT_MAX) assertionList.push_back(e >= int(intRange->minValue) && e <= int(intRange->maxValue)); else if (intRange->maxValue <= INT_MAX) assertionList.push_back(e <= int(intRange->maxValue)); else if (intRange->minValue >= INT_MIN) assertionList.push_back(e >= int(intRange->minValue)); return e; } if (auto b = std::dynamic_pointer_cast(v)) { return getExpr(b.get()); } if (auto c = std::dynamic_pointer_cast(v)) { if (c->values.empty()) throw VerifyException(nullptr, "ConditionalValue is empty"); if (c->values.size() == 1) return getExpr(c->values[0].second); return z3::ite(getExpr(c->values[1].first), getExpr(c->values[1].second), getExpr(c->values[0].second)); } if (auto integerTruncation = std::dynamic_pointer_cast(v)) { return getExpr(integerTruncation->inputValue); //return getExpr(integerTruncation->inputValue) & ((1 << integerTruncation->bits) - 1); } if (v->type == ExprEngine::ValueType::UninitValue) return context.int_val(0); throw VerifyException(nullptr, "Internal error: Unhandled value type"); } z3::expr getConstraintExpr(ExprEngine::ValuePtr v) { if (v->type == ExprEngine::ValueType::IntRange) return (getExpr(v) != 0); return getExpr(v); } private: z3::expr bool_expr(z3::expr e) { if (e.is_bool()) return e; return e != 0; } z3::expr int_expr(z3::expr e) { if (e.is_bool()) return z3::ite(e, context.int_val(1), context.int_val(0)); return e; } }; #endif bool ExprEngine::IntRange::isEqual(DataBase *dataBase, int value) const { if (value < minValue || value > maxValue) return false; const Data *data = dynamic_cast(dataBase); if (data->constraints.empty()) return true; #ifdef USE_Z3 // Check the value against the constraints ExprData exprData; z3::solver solver(exprData.context); try { z3::expr e = exprData.context.int_const(name.c_str()); exprData.valueExpr.emplace(name, e); for (auto constraint : dynamic_cast(dataBase)->constraints) solver.add(exprData.getConstraintExpr(constraint)); solver.add(e == value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3: " << exception << std::endl; return true; // Safe option is to return true } #else // The value may or may not be in range return false; #endif } bool ExprEngine::BinOpResult::isEqual(ExprEngine::DataBase *dataBase, int value) const { #ifdef USE_Z3 ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addAssertions(solver); for (auto constraint : dynamic_cast(dataBase)->constraints) solver.add(exprData.getConstraintExpr(constraint)); solver.add(e == value); return solver.check() == z3::sat; #else (void)dataBase; (void)value; return false; #endif } bool ExprEngine::BinOpResult::isGreaterThan(ExprEngine::DataBase *dataBase, int value) const { #ifdef USE_Z3 try { ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addAssertions(solver); for (auto constraint : dynamic_cast(dataBase)->constraints) solver.add(exprData.getConstraintExpr(constraint)); solver.add(e > value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3:" << exception << std::endl; return true; // Safe option is to return true } #else (void)dataBase; (void)value; return false; #endif } bool ExprEngine::BinOpResult::isLessThan(ExprEngine::DataBase *dataBase, int value) const { #ifdef USE_Z3 try { ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addAssertions(solver); for (auto constraint : dynamic_cast(dataBase)->constraints) solver.add(exprData.getConstraintExpr(constraint)); solver.add(e < value); return solver.check() == z3::sat; } catch (const z3::exception &exception) { std::cerr << "z3:" << exception << std::endl; return true; // Safe option is to return true } #else (void)dataBase; (void)value; return false; #endif } std::string ExprEngine::BinOpResult::getExpr(ExprEngine::DataBase *dataBase) const { #ifdef USE_Z3 try { ExprData exprData; z3::solver solver(exprData.context); z3::expr e = exprData.getExpr(this); exprData.addAssertions(solver); for (auto constraint : dynamic_cast(dataBase)->constraints) solver.add(exprData.getConstraintExpr(constraint)); solver.add(e); std::ostringstream os; os << solver; switch (solver.check()) { case z3::sat: os << "z3::sat"; break; case z3::unsat: os << "z3::unsat"; break; case z3::unknown: os << "z3::unknown"; break; } return os.str(); } catch (const z3::exception &exception) { std::ostringstream os; os << "z3:" << exception; return os.str(); } #else (void)dataBase; return ""; #endif } // Todo: This is taken from ValueFlow and modified.. we should reuse it static int getIntBitsFromValueType(const ValueType *vt, const cppcheck::Platform &platform) { if (!vt) return 0; switch (vt->type) { case ValueType::Type::BOOL: return 1; case ValueType::Type::CHAR: return platform.char_bit; case ValueType::Type::SHORT: return platform.short_bit; case ValueType::Type::INT: return platform.int_bit; case ValueType::Type::LONG: return platform.long_bit; case ValueType::Type::LONGLONG: return platform.long_long_bit; default: return 0; }; } static ExprEngine::ValuePtr getValueRangeFromValueType(const std::string &name, const ValueType *vt, const cppcheck::Platform &platform) { if (!vt || !(vt->isIntegral() || vt->isFloat()) || vt->pointer) return ExprEngine::ValuePtr(); int bits = getIntBitsFromValueType(vt, platform); if (bits == 1) { return std::make_shared(name, 0, 1); } else if (bits > 1) { if (vt->sign == ValueType::Sign::UNSIGNED) { return std::make_shared(name, 0, ((int128_t)1 << bits) - 1); } else { return std::make_shared(name, -((int128_t)1 << (bits - 1)), ((int128_t)1 << (bits - 1)) - 1); } } switch (vt->type) { case ValueType::Type::FLOAT: return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); case ValueType::Type::DOUBLE: return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); case ValueType::Type::LONGDOUBLE: return std::make_shared(name, std::numeric_limits::min(), std::numeric_limits::max()); default: return ExprEngine::ValuePtr(); }; } static void call(const std::vector &callbacks, const Token *tok, ExprEngine::ValuePtr value, Data *dataBase) { if (value) { for (ExprEngine::Callback f : callbacks) { try { f(tok, *value, dataBase); } catch (const VerifyException &e) { throw VerifyException(tok, e.what); } } } } static ExprEngine::ValuePtr executeExpression(const Token *tok, Data &data); static ExprEngine::ValuePtr executeReturn(const Token *tok, Data &data) { ExprEngine::ValuePtr retval = executeExpression(tok->astOperand1(), data); call(data.callbacks, tok, retval, &data); return retval; } static ExprEngine::ValuePtr truncateValue(ExprEngine::ValuePtr val, const ValueType *valueType, Data &data) { if (!valueType) return val; if (valueType->pointer != 0) return val; if (!valueType->isIntegral()) return val; // TODO int bits = getIntBitsFromValueType(valueType, *data.settings); if (bits == 0) // TODO return val; if (auto range = std::dynamic_pointer_cast(val)) { if (range->minValue == range->maxValue) { int128_t newValue = truncateInt(range->minValue, bits, valueType->sign == ValueType::Sign::SIGNED ? 's' : 'u'); if (newValue == range->minValue) return val; return std::make_shared(ExprEngine::str(newValue), newValue, newValue); } if (auto typeRange = getValueRangeFromValueType("", valueType, *data.settings)) { auto typeIntRange = std::dynamic_pointer_cast(typeRange); if (typeIntRange) { if (range->minValue >= typeIntRange->minValue && range->maxValue <= typeIntRange->maxValue) return val; } } return std::make_shared(data.getNewSymbolName(), val, bits, valueType->sign == ValueType::Sign::SIGNED ? 's' : 'u'); } // TODO return val; } static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data) { ExprEngine::ValuePtr rhsValue = executeExpression(tok->astOperand2(), data); if (!rhsValue && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->container && tok->astOperand2()->valueType()->container->stdStringLike) { auto size = std::make_shared(data.getNewSymbolName(), 0, ~0ULL); auto value = std::make_shared(data.getNewSymbolName(), -128, 127); rhsValue = std::make_shared(data.getNewSymbolName(), size, value); call(data.callbacks, tok->astOperand2(), rhsValue, &data); } if (!rhsValue) throw VerifyException(tok, "Expression '" + tok->expressionString() + "'; Failed to evaluate RHS"); ExprEngine::ValuePtr assignValue; if (tok->str() == "=") assignValue = rhsValue; else { // "+=" => "+" std::string binop(tok->str()); binop = binop.substr(0, binop.size() - 1); ExprEngine::ValuePtr lhsValue = executeExpression(tok->astOperand1(), data); assignValue = simplifyValue(std::make_shared(binop, lhsValue, rhsValue)); } const Token *lhsToken = tok->astOperand1(); assignValue = truncateValue(assignValue, lhsToken->valueType(), data); call(data.callbacks, tok, assignValue, &data); if (lhsToken->varId() > 0) { data.assignValue(lhsToken, lhsToken->varId(), assignValue); } else if (lhsToken->str() == "[") { auto arrayValue = data.getArrayValue(lhsToken->astOperand1()); if (arrayValue) { // Is it array initialization? const Token *arrayInit = lhsToken->astOperand1(); if (arrayInit && arrayInit->variable() && arrayInit->variable()->nameToken() == arrayInit) { if (assignValue->type == ExprEngine::ValueType::StringLiteralValue) arrayValue->assign(ExprEngine::ValuePtr(), assignValue); } else { auto indexValue = executeExpression(lhsToken->astOperand2(), data); arrayValue->assign(indexValue, assignValue); } } } else if (lhsToken->isUnaryOp("*")) { auto pval = executeExpression(lhsToken->astOperand1(), data); if (pval && pval->type == ExprEngine::ValueType::AddressOfValue) { auto val = std::dynamic_pointer_cast(pval); if (val) data.assignValue(lhsToken, val->varId, assignValue); } else if (pval && pval->type == ExprEngine::ValueType::BinOpResult) { auto b = std::dynamic_pointer_cast(pval); if (b && b->binop == "+") { std::shared_ptr arr; ExprEngine::ValuePtr offset; if (b->op1->type == ExprEngine::ValueType::ArrayValue) { arr = std::dynamic_pointer_cast(b->op1); offset = b->op2; } else { arr = std::dynamic_pointer_cast(b->op2); offset = b->op1; } if (arr && offset) { arr->assign(offset, assignValue); } } } } else if (Token::Match(lhsToken, ". %name%")) { auto structVal = executeExpression(lhsToken->astOperand1(), data); if (structVal && structVal->type == ExprEngine::ValueType::StructValue) data.assignStructMember(tok, &*std::static_pointer_cast(structVal), lhsToken->strAt(1), assignValue); } return assignValue; } static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data) { std::vector argValues; for (const Token *argtok : getArguments(tok)) { auto val = executeExpression(argtok, data); argValues.push_back(val); if (!argtok->valueType() || (argtok->valueType()->constness & 1) == 1) continue; if (auto arrayValue = std::dynamic_pointer_cast(val)) { ValueType vt(*argtok->valueType()); vt.pointer = 0; auto anyVal = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); arrayValue->assign(ExprEngine::ValuePtr(), anyVal); } else if (auto addressOf = std::dynamic_pointer_cast(val)) { ValueType vt(*argtok->valueType()); vt.pointer = 0; if (vt.isIntegral() && argtok->valueType()->pointer == 1) data.assignValue(argtok, addressOf->varId, getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings)); } } if (!tok->valueType() && tok->astParent()) throw VerifyException(tok, "Expression '" + tok->expressionString() + "' has unknown type!"); auto val = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); call(data.callbacks, tok, val, &data); return val; } static ExprEngine::ValuePtr executeArrayIndex(const Token *tok, Data &data) { auto arrayValue = data.getArrayValue(tok->astOperand1()); if (arrayValue) { auto indexValue = executeExpression(tok->astOperand2(), data); auto conditionalValues = arrayValue->read(indexValue); for (auto value: conditionalValues) call(data.callbacks, tok, value.second, &data); if (conditionalValues.size() == 1 && !conditionalValues[0].first) return conditionalValues[0].second; return std::make_shared(data.getNewSymbolName(), conditionalValues); } // TODO: Pointer value.. executeExpression(tok->astOperand1(), data); executeExpression(tok->astOperand2(), data); return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeCast(const Token *tok, Data &data) { const Token *expr = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); auto val = executeExpression(expr, data); if (expr->valueType() && expr->valueType()->type == ::ValueType::Type::VOID && expr->valueType()->pointer > 0) { ::ValueType vt(*tok->valueType()); vt.pointer = 0; auto range = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); if (tok->valueType()->pointer == 0) return range; bool uninit = false, null = false; if (val && val->type == ExprEngine::ValueType::PointerValue) { null = std::static_pointer_cast(val)->null; uninit = std::static_pointer_cast(val)->uninitData; } return std::make_shared(data.getNewSymbolName(), range, null, uninit); } if (val) // TODO: Cast this.. return val; return getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); } static ExprEngine::ValuePtr executeDot(const Token *tok, Data &data) { if (!tok->astOperand1() || !tok->astOperand1()->varId()) { auto v = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); call(data.callbacks, tok, v, &data); return v; } std::shared_ptr structValue = std::dynamic_pointer_cast(data.getValue(tok->astOperand1()->varId(), nullptr, nullptr)); if (!structValue) { if (tok->originalName() == "->") { std::shared_ptr pointerValue = std::dynamic_pointer_cast(data.getValue(tok->astOperand1()->varId(), nullptr, nullptr)); if (pointerValue) { call(data.callbacks, tok->astOperand1(), pointerValue, &data); structValue = std::dynamic_pointer_cast(pointerValue->data); } else { call(data.callbacks, tok->astOperand1(), data.getValue(tok->astOperand1()->varId(), nullptr, nullptr), &data); } } if (!structValue) { auto v = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); call(data.callbacks, tok, v, &data); return v; } } call(data.callbacks, tok->astOperand1(), structValue, &data); return structValue->getValueOfMember(tok->astOperand2()->str()); } static ExprEngine::ValuePtr executeBinaryOp(const Token *tok, Data &data) { ExprEngine::ValuePtr v1 = executeExpression(tok->astOperand1(), data); ExprEngine::ValuePtr v2 = executeExpression(tok->astOperand2(), data); if (v1 && v2) { auto result = simplifyValue(std::make_shared(tok->str(), v1, v2)); call(data.callbacks, tok, result, &data); return result; } return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeAddressOf(const Token *tok, Data &data) { auto addr = std::make_shared(data.getNewSymbolName(), tok->astOperand1()->varId()); call(data.callbacks, tok, addr, &data); return addr; } static ExprEngine::ValuePtr executeDeref(const Token *tok, Data &data) { ExprEngine::ValuePtr pval = executeExpression(tok->astOperand1(), data); if (!pval) { auto v = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); if (tok->astOperand1()->varId()) { pval = std::make_shared(data.getNewSymbolName(), v, false, false); data.assignValue(tok->astOperand1(), tok->astOperand1()->varId(), pval); } call(data.callbacks, tok, v, &data); return v; } auto addressOf = std::dynamic_pointer_cast(pval); if (addressOf) { auto val = data.getValue(addressOf->varId, tok->valueType(), tok); call(data.callbacks, tok, val, &data); return val; } auto pointer = std::dynamic_pointer_cast(pval); if (pointer) { auto val = pointer->data; call(data.callbacks, tok, val, &data); return val; } return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeVariable(const Token *tok, Data &data) { auto val = data.getValue(tok->varId(), tok->valueType(), tok); call(data.callbacks, tok, val, &data); return val; } static ExprEngine::ValuePtr executeKnownMacro(const Token *tok, Data &data) { auto val = std::make_shared(data.getNewSymbolName(), tok->getKnownIntValue(), tok->getKnownIntValue()); call(data.callbacks, tok, val, &data); return val; } static ExprEngine::ValuePtr executeNumber(const Token *tok, Data &data) { if (tok->valueType()->isFloat()) { long double value = MathLib::toDoubleNumber(tok->str()); auto v = std::make_shared(tok->str(), value, value); call(data.callbacks, tok, v, &data); return v; } int128_t value = MathLib::toLongNumber(tok->str()); auto v = std::make_shared(tok->str(), value, value); call(data.callbacks, tok, v, &data); return v; } static ExprEngine::ValuePtr executeStringLiteral(const Token *tok, Data &data) { std::string s = tok->str(); return std::make_shared(data.getNewSymbolName(), s.substr(1, s.size()-2)); } static ExprEngine::ValuePtr executeExpression1(const Token *tok, Data &data) { if (tok->str() == "return") return executeReturn(tok, data); if (tok->isAssignmentOp()) // TODO: Handle more operators return executeAssign(tok, data); if (tok->astOperand1() && tok->astOperand2() && tok->str() == "[") return executeArrayIndex(tok, data); if (tok->str() == "(") { if (!tok->isCast()) return executeFunctionCall(tok, data); return executeCast(tok, data); } if (tok->str() == ".") return executeDot(tok, data); if (tok->str() == "::" && tok->hasKnownIntValue()) { // TODO handle :: better auto v = tok->getKnownIntValue(); return std::make_shared(std::to_string(v), v, v); } if (tok->astOperand1() && tok->astOperand2()) return executeBinaryOp(tok, data); if (tok->isUnaryOp("&") && Token::Match(tok->astOperand1(), "%var%")) return executeAddressOf(tok, data); if (tok->isUnaryOp("*")) return executeDeref(tok, data); if (tok->varId()) return executeVariable(tok, data); if (tok->isName() && tok->hasKnownIntValue()) return executeKnownMacro(tok, data); if (tok->isNumber() || tok->tokType() == Token::Type::eChar) return executeNumber(tok, data); if (tok->tokType() == Token::Type::eString) return executeStringLiteral(tok, data); return ExprEngine::ValuePtr(); } static ExprEngine::ValuePtr executeExpression(const Token *tok, Data &data) { return translateUninitValueToRange(executeExpression1(tok, data), tok->valueType(), data); } static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data); static void execute(const Token *start, const Token *end, Data &data) { for (const Token *tok = start; tok != end; tok = tok->next()) { if (Token::Match(tok, "[;{}]")) data.trackProgramState(tok); if (Token::simpleMatch(tok, "while ( 0 ) ;")) { tok = tok->tokAt(4); continue; } if (tok->str() == "break") { const Scope *scope = tok->scope(); while (scope->type == Scope::eIf || scope->type == Scope::eElse) scope = scope->nestedIn; tok = scope->bodyEnd; } if (Token::simpleMatch(tok, "try")) // TODO this is a bailout throw VerifyException(tok, "Unhandled:" + tok->str()); // Variable declaration.. if (tok->variable() && tok->variable()->nameToken() == tok) { if (Token::Match(tok, "%varid% ; %varid% =", tok->varId())) { // if variable is not used in assignment rhs then we do not need to create a "confusing" variable value.. bool foundInRhs = false; visitAstNodes(tok->tokAt(3)->astOperand2(), [&](const Token *rhs) { if (rhs->varId()==tok->varId()) { foundInRhs = true; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); if (!foundInRhs) { tok = tok->tokAt(2); continue; } } if (tok->variable()->isArray()) { data.assignValue(tok, tok->varId(), std::make_shared(&data, tok->variable())); if (Token::Match(tok, "%name% [")) tok = tok->linkAt(1); } else if (Token::Match(tok, "%var% ;")) data.assignValue(tok, tok->varId(), createVariableValue(*tok->variable(), data)); } else if (!tok->astParent() && (tok->astOperand1() || tok->astOperand2())) { executeExpression(tok, data); if (Token::Match(tok, "throw|return")) return; } else if (Token::simpleMatch(tok, "if (")) { const Token *cond = tok->next()->astOperand2(); // TODO: C++17 condition const ExprEngine::ValuePtr condValue = executeExpression(cond, data); Data ifData(data); Data elseData(data); ifData.addConstraint(condValue, true); elseData.addConstraint(condValue, false); const Token *thenStart = tok->linkAt(1)->next(); const Token *thenEnd = thenStart->link(); execute(thenStart->next(), end, ifData); if (Token::simpleMatch(thenEnd, "} else {")) { const Token *elseStart = thenEnd->tokAt(2); execute(elseStart->next(), end, elseData); } else { execute(thenEnd, end, elseData); } return; } else if (Token::simpleMatch(tok, "switch (")) { auto condValue = executeExpression(tok->next()->astOperand2(), data); // TODO: C++17 condition const Token *bodyStart = tok->linkAt(1)->next(); const Token *bodyEnd = bodyStart->link(); const Token *defaultStart = nullptr; Data defaultData(data); for (const Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) { if (tok2->str() == "{") tok2 = tok2->link(); else if (Token::Match(tok2, "case %num% :")) { auto caseValue = std::make_shared(tok2->strAt(1), MathLib::toLongNumber(tok2->strAt(1)), MathLib::toLongNumber(tok2->strAt(1))); Data caseData(data); caseData.addConstraint(condValue, caseValue, true); defaultData.addConstraint(condValue, caseValue, false); execute(tok2->tokAt(2), end, caseData); } else if (Token::simpleMatch(tok2, "default :")) defaultStart = tok2; } execute(defaultStart ? defaultStart : bodyEnd, end, defaultData); return; } if (Token::Match(tok, "for|while (") && Token::simpleMatch(tok->linkAt(1), ") {")) { const Token *bodyStart = tok->linkAt(1)->next(); const Token *bodyEnd = bodyStart->link(); // TODO this is very rough code std::set changedVariables; for (const Token *tok2 = tok; tok2 != bodyEnd; tok2 = tok2->next()) { if (Token::Match(tok2, "%assign%")) { if (Token::Match(tok2->astOperand1(), ". %name% =") && tok2->astOperand1()->astOperand1() && tok2->astOperand1()->astOperand1()->valueType()) { const Token *structToken = tok2->astOperand1()->astOperand1(); if (!structToken || !structToken->valueType() || !structToken->varId()) throw VerifyException(tok2, "Unhandled assignment in loop"); const Scope *structScope = structToken->valueType()->typeScope; if (!structScope) throw VerifyException(tok2, "Unhandled assignment in loop"); const std::string &memberName = tok2->previous()->str(); ExprEngine::ValuePtr memberValue; for (const Variable &member : structScope->varlist) { if (memberName == member.name() && member.valueType()) { memberValue = createVariableValue(member, data); break; } } if (!memberValue) throw VerifyException(tok2, "Unhandled assignment in loop"); ExprEngine::ValuePtr structVal1 = data.getValue(structToken->varId(), structToken->valueType(), structToken); if (!structVal1) structVal1 = createVariableValue(*structToken->variable(), data); auto structVal = std::dynamic_pointer_cast(structVal1); if (!structVal) throw VerifyException(tok2, "Unhandled assignment in loop"); data.assignStructMember(tok2, &*structVal, memberName, memberValue); continue; } if (!Token::Match(tok2->astOperand1(), "%var%")) throw VerifyException(tok2, "Unhandled assignment in loop"); // give variable "any" value int varid = tok2->astOperand1()->varId(); if (changedVariables.find(varid) != changedVariables.end()) continue; changedVariables.insert(varid); data.assignValue(tok2, varid, createVariableValue(*tok2->astOperand1()->variable(), data)); } else if (Token::Match(tok2, "++|--") && tok2->astOperand1() && tok2->astOperand1()->variable()) { // give variable "any" value const Token *vartok = tok2->astOperand1(); int varid = vartok->varId(); if (changedVariables.find(varid) != changedVariables.end()) continue; changedVariables.insert(varid); data.assignValue(tok2, varid, createVariableValue(*vartok->variable(), data)); } } } if (Token::simpleMatch(tok, "} else {")) tok = tok->linkAt(2); } } void ExprEngine::executeAllFunctions(const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &trace) { const SymbolDatabase *symbolDatabase = tokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { try { executeFunction(functionScope, tokenizer, settings, callbacks, trace); } catch (const VerifyException &e) { // FIXME.. there should not be exceptions std::string functionName = functionScope->function->name(); std::cout << "Verify: Aborted analysis of function '" << functionName << "':" << e.tok->linenr() << ": " << e.what << std::endl; } catch (const std::exception &e) { // FIXME.. there should not be exceptions std::string functionName = functionScope->function->name(); std::cout << "Verify: Aborted analysis of function '" << functionName << "': " << e.what() << std::endl; } } } static ExprEngine::ValuePtr createStructVal(const Scope *structScope, bool uninitData, Data &data) { if (!structScope) return ExprEngine::ValuePtr(); std::shared_ptr structValue = std::make_shared(data.getNewSymbolName()); auto uninitValue = std::make_shared(); for (const Variable &member : structScope->varlist) { if (uninitData) { if (member.isPointer()) { structValue->member[member.name()] = uninitValue; continue; } if (member.valueType() && member.valueType()->type >= ::ValueType::Type::CHAR) { structValue->member[member.name()] = uninitValue; continue; } } if (member.valueType() && member.valueType()->isIntegral()) { ExprEngine::ValuePtr memberValue = createVariableValue(member, data); if (memberValue) structValue->member[member.name()] = memberValue; } } return structValue; } static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data) { if (!var.nameToken()) return ExprEngine::ValuePtr(); const ValueType *valueType = var.valueType(); if (!valueType || valueType->type == ValueType::Type::UNKNOWN_TYPE) valueType = var.nameToken()->valueType(); if (!valueType || valueType->type == ValueType::Type::UNKNOWN_TYPE) { // variable with unknown type if (var.isLocal() && var.isPointer() && !var.isArray()) return std::make_shared(); return ExprEngine::ValuePtr(); } if (valueType->pointer > 0) { if (var.isLocal()) return std::make_shared(); ValueType vt(*valueType); vt.pointer = 0; auto range = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); return std::make_shared(data.getNewSymbolName(), range, true, true); } if (var.isArray()) return std::make_shared(&data, &var); if (valueType->isIntegral()) return getValueRangeFromValueType(data.getNewSymbolName(), valueType, *data.settings); if (valueType->type == ValueType::Type::RECORD) return createStructVal(valueType->typeScope, var.isLocal() && !var.isStatic(), data); if (valueType->smartPointerType) { auto structValue = createStructVal(valueType->smartPointerType->classScope, var.isLocal() && !var.isStatic(), data); return std::make_shared(data.getNewSymbolName(), structValue, true, false); } if (valueType->container) { ExprEngine::ValuePtr value; if (valueType->container->stdStringLike) value = std::make_shared(data.getNewSymbolName(), -128, 127); else if (valueType->containerTypeToken) { ValueType vt = ValueType::parseDecl(valueType->containerTypeToken, data.settings); value = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); } else return ExprEngine::ValuePtr(); auto size = std::make_shared(data.getNewSymbolName(), 0, ~0ULL); return std::make_shared(data.getNewSymbolName(), size, value); } return ExprEngine::ValuePtr(); } void ExprEngine::executeFunction(const Scope *functionScope, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &trace) { if (!functionScope->bodyStart) return; const Function *function = functionScope->function; if (!function) return; if (functionScope->bodyStart->fileIndex() > 0) // TODO.. what about functions in headers? return; int symbolValueIndex = 0; TrackExecution trackExecution; Data data(&symbolValueIndex, tokenizer, settings, callbacks, &trackExecution); for (const Variable &arg : function->argumentList) data.assignValue(functionScope->bodyStart, arg.declarationId(), createVariableValue(arg, data)); execute(functionScope->bodyStart, functionScope->bodyEnd, data); if (settings->debugVerification) { // TODO generate better output!! trackExecution.print(trace); } } void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings) { std::function divByZero = [=](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!tok->astParent() || !std::strchr("/%", tok->astParent()->str()[0])) return; if (tok->astParent()->astOperand2() == tok && value.isEqual(dataBase, 0)) { std::list callstack{tok->astParent()}; ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "verificationDivByZero", "There is division, cannot determine that there can't be a division by zero.", CWE(369), false); errorLogger->reportErr(errmsg); } }; #ifdef VERIFY_INTEGEROVERFLOW std::function integerOverflow = [&](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (!tok->isArithmeticalOp() || !tok->valueType() || !tok->valueType()->isIntegral() || tok->valueType()->pointer > 0) return; const ExprEngine::BinOpResult *b = dynamic_cast(&value); if (!b) return; int bits = getIntBitsFromValueType(tok->valueType(), *settings); if (bits == 0 || bits >= 60) return; std::string errorMessage; if (tok->valueType()->sign == ::ValueType::Sign::SIGNED) { MathLib::bigint v = 1LL << (bits - 1); if (b->isGreaterThan(dataBase, v-1)) errorMessage = "greater than " + std::to_string(v - 1); if (b->isLessThan(dataBase, -v)) { if (!errorMessage.empty()) errorMessage += " or "; errorMessage += "less than " + std::to_string(-v); } } else { MathLib::bigint maxValue = (1LL << bits) - 1; if (b->isGreaterThan(dataBase, maxValue)) errorMessage = "greater than " + std::to_string(maxValue); if (b->isLessThan(dataBase, 0)) { if (!errorMessage.empty()) errorMessage += " or "; errorMessage += "less than 0"; } } if (errorMessage.empty()) return; errorMessage = "There is integer arithmetic, cannot determine that there can't be overflow (if result is " + errorMessage + ")."; if (tok->valueType()->sign == ::ValueType::Sign::UNSIGNED) errorMessage += " Note that unsigned integer overflow is defined and will wrap around."; std::list callstack{tok}; ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "verificationIntegerOverflow", errorMessage, false); errorLogger->reportErr(errmsg); }; #endif std::vector callbacks; callbacks.push_back(divByZero); #ifdef VERIFY_INTEGEROVERFLOW callbacks.push_back(integerOverflow); #endif ExprEngine::executeAllFunctions(tokenizer, settings, callbacks, std::cout); } cppcheck-1.90/lib/exprengine.h000066400000000000000000000215221357737443600163330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef exprengineH #define exprengineH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include #include #include #include class ErrorLogger; class Tokenizer; class Scope; class Settings; class Token; class Variable; #if defined(__GNUC__) && defined (__SIZEOF_INT128__) typedef __int128_t int128_t; #else typedef long long int128_t; #ifdef _MSC_VER #pragma message(__FILE__ "(" _CRT_STRINGIZE(__LINE__) ")" ": warning: TODO No 128-bit integer type is available => Limited analysis of large integers...") #else #warning TODO No 128-bit integer type is available => Limited analysis of large integers #endif #endif namespace ExprEngine { std::string str(int128_t); // TODO we need to handle floats, containers, pointers, aliases and structs and stuff enum class ValueType { UninitValue, IntRange, FloatRange, PointerValue, ConditionalValue, ArrayValue, StringLiteralValue, StructValue, AddressOfValue, BinOpResult, IntegerTruncation }; class Value; typedef std::shared_ptr ValuePtr; class DataBase { public: explicit DataBase(const Settings *settings) : settings(settings) {} virtual std::string getNewSymbolName() = 0; const Settings * const settings; }; class Value { public: Value(const std::string &name, const ValueType type) : name(name), type(type) {} virtual ~Value() {} virtual std::string getRange() const { return name; } virtual std::string getSymbolicExpression() const { return name; } virtual bool isEqual(DataBase *dataBase, int value) const { (void)dataBase; (void)value; return false; } virtual bool isGreaterThan(DataBase *dataBase, int value) const { (void)dataBase; (void)value; return false; } virtual bool isLessThan(DataBase *dataBase, int value) const { (void)dataBase; (void)value; return false; } const std::string name; ValueType type; }; class UninitValue: public Value { public: UninitValue() : Value("?", ValueType::UninitValue) {} }; class IntRange : public Value { public: IntRange(const std::string &name, int128_t minValue, int128_t maxValue) : Value(name, ValueType::IntRange) , minValue(minValue) , maxValue(maxValue) { } std::string getRange() const override { if (minValue == maxValue) return str(minValue); return str(minValue) + ":" + str(maxValue); } bool isEqual(DataBase *dataBase, int value) const override; int128_t minValue; int128_t maxValue; }; class FloatRange : public Value { public: FloatRange(const std::string &name, long double minValue, long double maxValue) : Value(name, ValueType::FloatRange) , minValue(minValue) , maxValue(maxValue) { } bool isEqual(DataBase *dataBase, int value) const override { (void)dataBase; return value >= minValue && value <= maxValue; } std::string getRange() const override { return std::to_string(minValue) + ":" + std::to_string(maxValue); } long double minValue; long double maxValue; }; class PointerValue: public Value { public: PointerValue(const std::string &name, ValuePtr data, bool null, bool uninitData) : Value(name, ValueType::PointerValue) , data(data) , null(null) , uninitData(uninitData) { } std::string getRange() const override; ValuePtr data; bool null; bool uninitData; }; class ConditionalValue : public Value { public: typedef std::vector> Vector; ConditionalValue(const std::string &name, const Vector &values) : Value(name, ValueType::ConditionalValue), values(values) {} std::string getSymbolicExpression() const override; Vector values; }; class ArrayValue: public Value { public: const int MAXSIZE = 0x100000; ArrayValue(const std::string &name, ValuePtr size, ValuePtr value); ArrayValue(DataBase *data, const Variable *var); std::string getSymbolicExpression() const override; void assign(ValuePtr index, ValuePtr value); void clear(); ConditionalValue::Vector read(ValuePtr index) const; struct IndexAndValue { ValuePtr index; ValuePtr value; }; std::vector data; ValuePtr size; }; class StringLiteralValue: public Value { public: StringLiteralValue(const std::string &name, const std::string &s) : Value(name, ValueType::StringLiteralValue), string(s) {} std::string getRange() const override { return "\"" + string + "\""; } int size() const { return string.size(); } const std::string string; }; class StructValue: public Value { public: explicit StructValue(const std::string &name) : Value(name, ValueType::StructValue) {} std::string getSymbolicExpression() const override; ValuePtr getValueOfMember(const std::string &name) const { auto it = member.find(name); return (it == member.end()) ? ValuePtr() : it->second; } std::map member; }; class AddressOfValue: public Value { public: AddressOfValue(const std::string &name, int varId) : Value(name, ValueType::AddressOfValue) , varId(varId) {} std::string getRange() const override { return "&@" + std::to_string(varId); } int varId; }; class BinOpResult : public Value { public: BinOpResult(const std::string &binop, ValuePtr op1, ValuePtr op2) : Value(getName(binop, op1, op2), ValueType::BinOpResult) , binop(binop) , op1(op1) , op2(op2) { } bool isEqual(DataBase *dataBase, int value) const override; bool isGreaterThan(DataBase *dataBase, int value) const override; virtual bool isLessThan(DataBase *dataBase, int value) const override; std::string getExpr(DataBase *dataBase) const; std::string binop; ValuePtr op1; ValuePtr op2; private: std::string getName(const std::string &binop, ValuePtr op1, ValuePtr op2) const { std::string name1 = op1 ? op1->name : std::string("null"); std::string name2 = op2 ? op2->name : std::string("null"); return "(" + name1 + ")" + binop + "(" + name2 + ")"; } }; class IntegerTruncation : public Value { public: IntegerTruncation(const std::string &name, ValuePtr inputValue, int bits, char sign) : Value(name, ValueType::IntegerTruncation) , inputValue(inputValue) , bits(bits) , sign(sign) { } std::string getSymbolicExpression() const override; ExprEngine::ValuePtr inputValue; int bits; char sign; }; typedef std::function Callback; /** Execute all functions */ void CPPCHECKLIB executeAllFunctions(const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &trace); void executeFunction(const Scope *functionScope, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &trace); void runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings); } #endif // exprengineH cppcheck-1.90/lib/importproject.cpp000066400000000000000000001352721357737443600174330ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "importproject.h" #include "path.h" #include "settings.h" #include "tinyxml2.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #define PICOJSON_USE_INT64 #include #include #include #include #include void ImportProject::ignorePaths(const std::vector &ipaths) { for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { bool ignore = false; for (std::string i : ipaths) { if (it->filename.size() > i.size() && it->filename.compare(0,i.size(),i)==0) { ignore = true; break; } if (!Path::isAbsolute(i)) { i = mPath + i; if (it->filename.size() > i.size() && it->filename.compare(0,i.size(),i)==0) { ignore = true; break; } } } if (ignore) fileSettings.erase(it++); else ++it; } } void ImportProject::ignoreOtherConfigs(const std::string &cfg) { for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { if (it->cfg != cfg) fileSettings.erase(it++); else ++it; } } void ImportProject::ignoreOtherPlatforms(cppcheck::Platform::PlatformType platformType) { for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { if (it->platformType != cppcheck::Platform::Unspecified && it->platformType != platformType) fileSettings.erase(it++); else ++it; } } void ImportProject::FileSettings::setDefines(std::string defs) { while (defs.find(";%(") != std::string::npos) { const std::string::size_type pos1 = defs.find(";%("); const std::string::size_type pos2 = defs.find(';', pos1+1); defs.erase(pos1, pos2 == std::string::npos ? pos2 : (pos2-pos1)); } while (defs.find(";;") != std::string::npos) defs.erase(defs.find(";;"),1); while (!defs.empty() && defs[0] == ';') defs.erase(0, 1); while (!defs.empty() && endsWith(defs,';')) defs.erase(defs.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it bool eq = false; for (std::size_t pos = 0; pos < defs.size(); ++pos) { if (defs[pos] == '(' || defs[pos] == '=') eq = true; else if (defs[pos] == ';') { if (!eq) { defs.insert(pos,"=1"); pos += 3; } if (pos < defs.size()) eq = false; } } if (!eq && !defs.empty()) defs += "=1"; defines.swap(defs); } static bool simplifyPathWithVariables(std::string &s, std::map &variables) { std::set expanded; std::string::size_type start = 0; while ((start = s.find("$(")) != std::string::npos) { const std::string::size_type end = s.find(')',start); if (end == std::string::npos) break; const std::string var = s.substr(start+2,end-start-2); if (expanded.find(var) != expanded.end()) break; expanded.insert(var); std::map::const_iterator it1 = variables.find(var); // variable was not found within defined variables if (it1 == variables.end()) { const char *envValue = std::getenv(var.c_str()); if (!envValue) { //! \todo generate a debug/info message about undefined variable break; } variables[var] = std::string(envValue); it1 = variables.find(var); } s = s.substr(0, start) + it1->second + s.substr(end + 1); } if (s.find("$(") != std::string::npos) return false; s = Path::simplifyPath(Path::fromNativeSeparators(s)); return true; } void ImportProject::FileSettings::setIncludePaths(const std::string &basepath, const std::list &in, std::map &variables) { std::list listInc; // only parse each includePath once - so remove duplicates std::list uniqueIncludePaths = in; uniqueIncludePaths.sort(); uniqueIncludePaths.unique(); for (const std::string &it : uniqueIncludePaths) { if (it.empty()) continue; if (it.compare(0,2,"%(")==0) continue; std::string s(Path::fromNativeSeparators(it)); if (s[0] == '/' || (s.size() > 1U && s.compare(1,2,":/") == 0)) { if (!endsWith(s,'/')) s += '/'; listInc.push_back(s); continue; } if (endsWith(s,'/')) // this is a temporary hack, simplifyPath can crash if path ends with '/' s.erase(s.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it if (s.find("$(") == std::string::npos) { s = Path::simplifyPath(basepath + s); } else { if (!simplifyPathWithVariables(s, variables)) continue; } if (s.empty()) continue; listInc.push_back(s + '/'); } includePaths.swap(listInc); } ImportProject::Type ImportProject::import(const std::string &filename, Settings *settings) { std::ifstream fin(filename); if (!fin.is_open()) return ImportProject::Type::MISSING; mPath = Path::getPathFromFilename(Path::fromNativeSeparators(filename)); if (!mPath.empty() && !endsWith(mPath,'/')) mPath += '/'; if (endsWith(filename, ".json", 5)) { importCompileCommands(fin); return ImportProject::Type::COMPILE_DB; } else if (endsWith(filename, ".sln", 4)) { importSln(fin,mPath); return ImportProject::Type::VS_SLN; } else if (endsWith(filename, ".vcxproj", 8)) { std::map variables; importVcxproj(filename, variables, emptyString); return ImportProject::Type::VS_VCXPROJ; } else if (endsWith(filename, ".bpr", 4)) { importBcb6Prj(filename); return ImportProject::Type::BORLAND; } else if (settings && endsWith(filename, ".cppcheck", 9)) { return importCppcheckGuiProject(fin, settings) ? ImportProject::Type::CPPCHECK_GUI : ImportProject::Type::MISSING; } return ImportProject::Type::UNKNOWN; } static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[]) { std::string ret; bool str = false; while (*pos < command.size() && (str || !std::strchr(until, command[*pos]))) { if (command[*pos] == '\\') ++*pos; if (*pos < command.size()) ret += command[(*pos)++]; if (endsWith(ret, '\"')) str = !str; } return ret; } void ImportProject::FileSettings::parseCommand(const std::string &command) { std::string defs; // Parse command.. std::string::size_type pos = 0; while (std::string::npos != (pos = command.find(' ',pos))) { while (pos < command.size() && command[pos] == ' ') pos++; if (pos >= command.size()) break; if (command[pos] != '/' && command[pos] != '-') continue; pos++; if (pos >= command.size()) break; const char F = command[pos++]; if (std::strchr("DUI", F)) { while (pos < command.size() && command[pos] == ' ') ++pos; } const std::string fval = readUntil(command, &pos, " ="); if (F=='D') { 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') 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 += ";"; } } } setDefines(defs); } void ImportProject::importCompileCommands(std::istream &istr) { picojson::value compileCommands; istr >> compileCommands; if (!compileCommands.is()) return; for (const picojson::value &fileInfo : compileCommands.get()) { picojson::object obj = fileInfo.get(); std::string dirpath = Path::fromNativeSeparators(obj["directory"].get()); /* CMAKE produces the directory without trailing / so add it if not * there - it is needed by setIncludePaths() */ if (!endsWith(dirpath, '/')) dirpath += '/'; const std::string directory = dirpath; std::ostringstream comm; if (obj.find("arguments") != obj.end()) { if (obj[ "arguments" ].is< picojson::array >()) { for (const picojson::value& arg : obj[ "arguments" ].get< picojson::array >()) { if (arg.is< std::string >()) { comm << arg.get< std::string >() << " "; } } } else { return; } } else if (obj.find("command") != obj.end()) { if (obj[ "command" ].is< std::string >()) { comm << obj[ "command" ].get< std::string >(); } } else { return; } const std::string command = comm.str(); const std::string file = Path::fromNativeSeparators(obj["file"].get()); // Accept file? if (!Path::acceptFile(file)) continue; struct FileSettings fs; if (Path::isAbsolute(file) || Path::fileExists(file)) fs.filename = file; else { std::string path = directory; if (!path.empty() && !endsWith(path,'/')) path += '/'; path += file; fs.filename = Path::simplifyPath(path); } fs.parseCommand(command); // read settings; -D, -I, -U, -std, -m*, -f* std::map variables; fs.setIncludePaths(directory, fs.includePaths, variables); fileSettings.push_back(fs); } } void ImportProject::importSln(std::istream &istr, const std::string &path) { std::map variables; variables["SolutionDir"] = path; std::string line; while (std::getline(istr,line)) { if (line.compare(0,8,"Project(")!=0) continue; const std::string::size_type pos = line.find(".vcxproj"); if (pos == std::string::npos) continue; const std::string::size_type pos1 = line.rfind('\"',pos); if (pos1 == std::string::npos) continue; std::string vcxproj(line.substr(pos1+1, pos-pos1+7)); if (!Path::isAbsolute(vcxproj)) vcxproj = path + vcxproj; importVcxproj(Path::fromNativeSeparators(vcxproj), variables, emptyString); } } namespace { struct ProjectConfiguration { explicit ProjectConfiguration(const tinyxml2::XMLElement *cfg) : platform(Unknown) { const char *a = cfg->Attribute("Include"); if (a) name = a; for (const tinyxml2::XMLElement *e = cfg->FirstChildElement(); e; e = e->NextSiblingElement()) { if (!e->GetText()) continue; if (std::strcmp(e->Name(),"Configuration")==0) configuration = e->GetText(); else if (std::strcmp(e->Name(),"Platform")==0) { platformStr = e->GetText(); if (platformStr == "Win32") platform = Win32; else if (platformStr == "x64") platform = x64; else platform = Unknown; } } } std::string name; std::string configuration; enum { Win32, x64, Unknown } platform; std::string platformStr; }; struct ItemDefinitionGroup { explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, const std::string &includePaths) : additionalIncludePaths(includePaths) { const char *condAttr = idg->Attribute("Condition"); if (condAttr) condition = condAttr; for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) { if (std::strcmp(e1->Name(), "ClCompile") != 0) continue; for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { if (e->GetText()) { if (std::strcmp(e->Name(), "PreprocessorDefinitions") == 0) preprocessorDefinitions = e->GetText(); else if (std::strcmp(e->Name(), "AdditionalIncludeDirectories") == 0) { if (!additionalIncludePaths.empty()) additionalIncludePaths += ';'; additionalIncludePaths += e->GetText(); } } } } } static void replaceAll(std::string &c, const std::string &from, const std::string &to) { std::string::size_type pos; while ((pos = c.find(from)) != std::string::npos) { c.erase(pos,from.size()); c.insert(pos,to); } } bool conditionIsTrue(const ProjectConfiguration &p) const { if (condition.empty()) return true; std::string c = '(' + condition + ");"; replaceAll(c, "$(Configuration)", p.configuration); replaceAll(c, "$(Platform)", p.platformStr); // TODO : Better evaluation Settings s; std::istringstream istr(c); Tokenizer tokenizer(&s, nullptr); tokenizer.tokenize(istr,"vcxproj"); for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { if (tok->str() == "(" && tok->astOperand1() && tok->astOperand2()) { if (tok->astOperand1()->expressionString() == "Configuration.Contains") return ('\'' + p.configuration + '\'') == tok->astOperand2()->str(); } if (tok->str() == "==" && tok->astOperand1() && tok->astOperand2() && tok->astOperand1()->str() == tok->astOperand2()->str()) return true; } return false; } std::string condition; std::string preprocessorDefinitions; std::string additionalIncludePaths; }; } static std::list toStringList(const std::string &s) { std::list ret; std::string::size_type pos1 = 0; std::string::size_type pos2; while ((pos2 = s.find(';',pos1)) != std::string::npos) { ret.push_back(s.substr(pos1, pos2-pos1)); pos1 = pos2 + 1; if (pos1 >= s.size()) break; } if (pos1 < s.size()) ret.push_back(s.substr(pos1)); return ret; } static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map *variables, std::string *includePath, bool *useOfMfc) { if (useOfMfc) { for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "UseOfMfc") == 0) { *useOfMfc = true; break; } } } const char* labelAttribute = node->Attribute("Label"); if (labelAttribute && std::strcmp(labelAttribute, "UserMacros") == 0) { for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) { const std::string name(propertyGroup->Name()); const char *text = propertyGroup->GetText(); (*variables)[name] = std::string(text ? text : ""); } } else if (!labelAttribute) { for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) { if (std::strcmp(propertyGroup->Name(), "IncludePath") != 0) continue; const char *text = propertyGroup->GetText(); if (!text) continue; std::string path(text); const std::string::size_type pos = path.find("$(IncludePath)"); if (pos != std::string::npos) path = path.substr(0,pos) + *includePath + path.substr(pos+14U); *includePath = path; } } } static void loadVisualStudioProperties(const std::string &props, std::map *variables, std::string *includePath, const std::string &additionalIncludeDirectories, std::list &itemDefinitionGroupList) { std::string filename(props); // variables can't be resolved if (!simplifyPathWithVariables(filename, *variables)) return; // prepend project dir (if it exists) to transform relative paths into absolute ones if (!Path::isAbsolute(filename) && variables->count("ProjectDir") > 0) filename = Path::getAbsoluteFilePath(variables->at("ProjectDir") + filename); tinyxml2::XMLDocument doc; if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) return; const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr) return; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "ImportGroup") == 0) { const char *labelAttribute = node->Attribute("Label"); if (labelAttribute == nullptr || std::strcmp(labelAttribute, "PropertySheets") != 0) continue; for (const tinyxml2::XMLElement *importGroup = node->FirstChildElement(); importGroup; importGroup = importGroup->NextSiblingElement()) { if (std::strcmp(importGroup->Name(), "Import") == 0) { const char *projectAttribute = importGroup->Attribute("Project"); if (projectAttribute == nullptr) continue; std::string loadprj(projectAttribute); if (loadprj.find('$') == std::string::npos) { loadprj = Path::getPathFromFilename(filename) + loadprj; } loadVisualStudioProperties(loadprj, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList); } } } else if (std::strcmp(node->Name(),"PropertyGroup")==0) { importPropertyGroup(node, variables, includePath, nullptr); } else if (std::strcmp(node->Name(),"ItemDefinitionGroup")==0) { itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories); } } } void ImportProject::importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories) { variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename)); std::list projectConfigurationList; std::list compileList; std::list itemDefinitionGroupList; std::string includePath; bool useOfMfc = false; tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(filename.c_str()); if (error != tinyxml2::XML_SUCCESS) return; const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr) return; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "ItemGroup") == 0) { const char *labelAttribute = node->Attribute("Label"); if (labelAttribute && std::strcmp(labelAttribute, "ProjectConfigurations") == 0) { for (const tinyxml2::XMLElement *cfg = node->FirstChildElement(); cfg; cfg = cfg->NextSiblingElement()) { if (std::strcmp(cfg->Name(), "ProjectConfiguration") == 0) { const ProjectConfiguration p(cfg); if (p.platform != ProjectConfiguration::Unknown) projectConfigurationList.emplace_back(cfg); } } } else { for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "ClCompile") == 0) { const char *include = e->Attribute("Include"); if (include && Path::acceptFile(include)) compileList.emplace_back(include); } } } } else if (std::strcmp(node->Name(), "ItemDefinitionGroup") == 0) { itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories); } else if (std::strcmp(node->Name(), "PropertyGroup") == 0) { importPropertyGroup(node, &variables, &includePath, &useOfMfc); } else if (std::strcmp(node->Name(), "ImportGroup") == 0) { const char *labelAttribute = node->Attribute("Label"); if (labelAttribute && std::strcmp(labelAttribute, "PropertySheets") == 0) { for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "Import") == 0) { const char *projectAttribute = e->Attribute("Project"); if (projectAttribute) loadVisualStudioProperties(projectAttribute, &variables, &includePath, additionalIncludeDirectories, itemDefinitionGroupList); } } } } } for (const std::string &c : compileList) { for (const ProjectConfiguration &p : projectConfigurationList) { FileSettings fs; fs.filename = Path::simplifyPath(Path::isAbsolute(c) ? c : Path::getPathFromFilename(filename) + c); fs.cfg = p.name; fs.msc = true; fs.useMfc = useOfMfc; fs.defines = "_WIN32=1"; if (p.platform == ProjectConfiguration::Win32) fs.platformType = cppcheck::Platform::Win32W; else if (p.platform == ProjectConfiguration::x64) { fs.platformType = cppcheck::Platform::Win64; fs.defines += ";_WIN64=1"; } std::string additionalIncludePaths; for (const ItemDefinitionGroup &i : itemDefinitionGroupList) { if (!i.conditionIsTrue(p)) continue; fs.defines += ';' + i.preprocessorDefinitions; additionalIncludePaths += ';' + i.additionalIncludePaths; } fs.setDefines(fs.defines); fs.setIncludePaths(Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables); fileSettings.push_back(fs); } } } void ImportProject::importBcb6Prj(const std::string &projectFilename) { tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(projectFilename.c_str()); if (error != tinyxml2::XML_SUCCESS) return; const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr) return; const std::string& projectDir = Path::simplifyPath(Path::getPathFromFilename(projectFilename)); std::list compileList; std::string includePath; std::string userdefines; std::string sysdefines; std::string cflag1; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "FILELIST") == 0) { for (const tinyxml2::XMLElement *f = node->FirstChildElement(); f; f = f->NextSiblingElement()) { if (std::strcmp(f->Name(), "FILE") == 0) { const char *filename = f->Attribute("FILENAME"); if (filename && Path::acceptFile(filename)) compileList.emplace_back(filename); } } } else if (std::strcmp(node->Name(), "MACROS") == 0) { for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) { if (std::strcmp(m->Name(), "INCLUDEPATH") == 0) { const char *v = m->Attribute("value"); if (v) includePath = v; } else if (std::strcmp(m->Name(), "USERDEFINES") == 0) { const char *v = m->Attribute("value"); if (v) userdefines = v; } else if (std::strcmp(m->Name(), "SYSDEFINES") == 0) { const char *v = m->Attribute("value"); if (v) sysdefines = v; } } } else if (std::strcmp(node->Name(), "OPTIONS") == 0) { for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) { if (std::strcmp(m->Name(), "CFLAG1") == 0) { const char *v = m->Attribute("value"); if (v) cflag1 = v; } } } } std::set cflags; // parse cflag1 and fill the cflags set { std::string arg; for (char i : cflag1) { if (i == ' ' && !arg.empty()) { cflags.insert(arg); arg.clear(); continue; } arg += i; } if (!arg.empty()) { cflags.insert(arg); } // cleanup: -t is "An alternate name for the -Wxxx switches; there is no difference" // -> Remove every known -txxx argument and replace it with its -Wxxx counterpart. // This way, we know what we have to check for later on. static const std::map synonyms = { { "-tC","-WC" }, { "-tCDR","-WCDR" }, { "-tCDV","-WCDV" }, { "-tW","-W" }, { "-tWC","-WC" }, { "-tWCDR","-WCDR" }, { "-tWCDV","-WCDV" }, { "-tWD","-WD" }, { "-tWDR","-WDR" }, { "-tWDV","-WDV" }, { "-tWM","-WM" }, { "-tWP","-WP" }, { "-tWR","-WR" }, { "-tWU","-WU" }, { "-tWV","-WV" } }; for (std::map::const_iterator i = synonyms.begin(); i != synonyms.end(); ++i) { if (cflags.erase(i->first) > 0) { cflags.insert(i->second); } } } std::string predefines; std::string cppPredefines; // Collecting predefines. See BCB6 help topic "Predefined macros" { cppPredefines += // Defined if you've selected C++ compilation; will increase in later releases. // value 0x0560 (but 0x0564 for our BCB6 SP4) // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros ";__BCPLUSPLUS__=0x0560" // Defined if in C++ mode; otherwise, undefined. ";__cplusplus=1" // Defined as 1 for C++ files(meaning that templates are supported); otherwise, it is undefined. ";__TEMPLATES__=1" // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type. ";_WCHAR_T" // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type. ";_WCHAR_T_DEFINED" // Defined in any compiler that has an optimizer. ";__BCOPT__=1" // Version number. // BCB6 is 0x056X (SP4 is 0x0564) // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros ";__BORLANDC__=0x0560" ";__TCPLUSPLUS__=0x0560" ";__TURBOC__=0x0560"; // Defined if Calling Convention is set to cdecl; otherwise undefined. const bool useCdecl = (cflags.find("-p") == cflags.end() && cflags.find("-pm") == cflags.end() && cflags.find("-pr") == cflags.end() && cflags.find("-ps") == cflags.end()); if (useCdecl) predefines += ";__CDECL=1"; // Defined by default indicating that the default char is unsigned char. Use the -K compiler option to undefine this macro. const bool treatCharAsUnsignedChar = (cflags.find("-K") != cflags.end()); if (treatCharAsUnsignedChar) predefines += ";_CHAR_UNSIGNED=1"; // Defined whenever one of the CodeGuard compiler options is used; otherwise it is undefined. const bool codeguardUsed = (cflags.find("-vGd") != cflags.end() || cflags.find("-vGt") != cflags.end() || cflags.find("-vGc") != cflags.end()); if (codeguardUsed) predefines += ";__CODEGUARD__"; // When defined, the macro indicates that the program is a console application. const bool isConsoleApp = (cflags.find("-WC") != cflags.end()); if (isConsoleApp) predefines += ";__CONSOLE__=1"; // Enable stack unwinding. This is true by default; use -xd- to disable. const bool enableStackUnwinding = (cflags.find("-xd-") == cflags.end()); if (enableStackUnwinding) predefines += ";_CPPUNWIND=1"; // Defined whenever the -WD compiler option is used; otherwise it is undefined. const bool isDLL = (cflags.find("-WD") != cflags.end()); if (isDLL) predefines += ";__DLL__=1"; // Defined when compiling in 32-bit flat memory model. // TODO: not sure how to switch to another memory model or how to read configuration from project file predefines += ";__FLAT__=1"; // Always defined. The default value is 300. You can change the value to 400 or 500 by using the /4 or /5 compiler options. if (cflags.find("-6") != cflags.end()) predefines += ";_M_IX86=600"; else if (cflags.find("-5") != cflags.end()) predefines += ";_M_IX86=500"; else if (cflags.find("-4") != cflags.end()) predefines += ";_M_IX86=400"; else predefines += ";_M_IX86=300"; // Defined only if the -WM option is used. It specifies that the multithread library is to be linked. const bool linkMtLib = (cflags.find("-WM") != cflags.end()); if (linkMtLib) predefines += ";__MT__=1"; // Defined if Calling Convention is set to Pascal; otherwise undefined. const bool usePascalCallingConvention = (cflags.find("-p") != cflags.end()); if (usePascalCallingConvention) predefines += ";__PASCAL__=1"; // Defined if you compile with the -A compiler option; otherwise, it is undefined. const bool useAnsiKeywordExtensions = (cflags.find("-A") != cflags.end()); if (useAnsiKeywordExtensions) predefines += ";__STDC__=1"; // Thread Local Storage. Always true in C++Builder. predefines += ";__TLC__=1"; // Defined for Windows-only code. const bool isWindowsTarget = (cflags.find("-WC") != cflags.end() || cflags.find("-WCDR") != cflags.end() || cflags.find("-WCDV") != cflags.end() || cflags.find("-WD") != cflags.end() || cflags.find("-WDR") != cflags.end() || cflags.find("-WDV") != cflags.end() || cflags.find("-WM") != cflags.end() || cflags.find("-WP") != cflags.end() || cflags.find("-WR") != cflags.end() || cflags.find("-WU") != cflags.end() || cflags.find("-WV") != cflags.end()); if (isWindowsTarget) predefines += ";_Windows"; // Defined for console and GUI applications. // TODO: I'm not sure about the difference to define "_Windows". // From description, I would assume __WIN32__ is only defined for // executables, while _Windows would also be defined for DLLs, etc. // However, in a newly created DLL project, both __WIN32__ and // _Windows are defined. -> treating them the same for now. // Also boost uses __WIN32__ for OS identification. const bool isConsoleOrGuiApp = isWindowsTarget; if (isConsoleOrGuiApp) predefines += ";__WIN32__=1"; } // Include paths may contain variables like "$(BCB)\include" or "$(BCB)\include\vcl". // Those get resolved by ImportProject::FileSettings::setIncludePaths by // 1. checking the provided variables map ("BCB" => "C:\\Program Files (x86)\\Borland\\CBuilder6") // 2. checking env variables as a fallback // Setting env is always possible. Configuring the variables via cli might be an addition. // Reading the BCB6 install location from registry in windows environments would also be possible, // but I didn't see any such functionality around the source. Not in favor of adding it only // for the BCB6 project loading. std::map variables; const std::string defines = predefines + ";" + sysdefines + ";" + userdefines; const std::string cppDefines = cppPredefines + ";" + defines; const bool forceCppMode = (cflags.find("-P") != cflags.end()); for (const std::string &c : compileList) { // C++ compilation is selected by file extension by default, so these // defines have to be configured on a per-file base. // // > Files with the .CPP extension compile as C++ files. Files with a .C // > extension, with no extension, or with extensions other than .CPP, // > .OBJ, .LIB, or .ASM compile as C files. // (http://docwiki.embarcadero.com/RADStudio/Tokyo/en/BCC32.EXE,_the_C%2B%2B_32-bit_Command-Line_Compiler) // // We can also force C++ compilation for all files using the -P command line switch. const bool cppMode = forceCppMode || Path::getFilenameExtensionInLowerCase(c) == ".cpp"; FileSettings fs; fs.setIncludePaths(projectDir, toStringList(includePath), variables); fs.setDefines(cppMode ? cppDefines : defines); fs.filename = Path::simplifyPath(Path::isAbsolute(c) ? c : projectDir + c); fileSettings.push_back(fs); } } static std::string joinRelativePath(const std::string &path1, const std::string &path2) { if (!path1.empty() && !Path::isAbsolute(path2)) return path1 + path2; return path2; } static std::list readXmlStringList(const tinyxml2::XMLElement *node, const std::string &path, const char name[], const char attribute[]) { std::list ret; for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { if (strcmp(child->Name(), name) != 0) continue; const char *attr = attribute ? child->Attribute(attribute) : child->GetText(); if (attr) ret.push_back(joinRelativePath(path, attr)); } return ret; } static std::string join(const std::list &strlist, const char *sep) { std::string ret; for (const std::string &s : strlist) { ret += (ret.empty() ? "" : sep) + s; } return ret; } static std::string istream_to_string(std::istream &istr) { std::istreambuf_iterator eos; return std::string(std::istreambuf_iterator(istr), eos); } bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *settings) { tinyxml2::XMLDocument doc; const std::string xmldata = istream_to_string(istr); if (doc.Parse(xmldata.data(), xmldata.size()) != tinyxml2::XML_SUCCESS) return false; const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr || strcmp(rootnode->Name(), CppcheckXml::ProjectElementName) != 0) return false; const std::string &path = mPath; std::list paths; std::list suppressions; Settings temp; guiProject.analyzeAllVsConfigs.clear(); for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (strcmp(node->Name(), CppcheckXml::RootPathName) == 0 && node->Attribute(CppcheckXml::RootPathNameAttrib)) { temp.basePaths.push_back(joinRelativePath(path, node->Attribute(CppcheckXml::RootPathNameAttrib))); temp.relativePaths = true; } else if (strcmp(node->Name(), CppcheckXml::BuildDirElementName) == 0) temp.buildDir = joinRelativePath(path, node->GetText() ? node->GetText() : ""); else if (strcmp(node->Name(), CppcheckXml::IncludeDirElementName) == 0) temp.includePaths = readXmlStringList(node, path, CppcheckXml::DirElementName, CppcheckXml::DirNameAttrib); else if (strcmp(node->Name(), CppcheckXml::DefinesElementName) == 0) temp.userDefines = join(readXmlStringList(node, "", CppcheckXml::DefineName, CppcheckXml::DefineNameAttrib), ";"); else if (strcmp(node->Name(), CppcheckXml::UndefinesElementName) == 0) { for (const std::string &u : readXmlStringList(node, "", CppcheckXml::UndefineName, nullptr)) temp.userUndefs.insert(u); } else if (strcmp(node->Name(), CppcheckXml::ImportProjectElementName) == 0) guiProject.projectFile = path + (node->GetText() ? node->GetText() : ""); else if (strcmp(node->Name(), CppcheckXml::PathsElementName) == 0) paths = readXmlStringList(node, path, CppcheckXml::PathName, CppcheckXml::PathNameAttrib); else if (strcmp(node->Name(), CppcheckXml::ExcludeElementName) == 0) guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); else if (strcmp(node->Name(), CppcheckXml::IgnoreElementName) == 0) guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0) guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr); else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0) suppressions = readXmlStringList(node, "", CppcheckXml::SuppressionElementName, nullptr); else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0) guiProject.platform = node->GetText(); else if (strcmp(node->Name(), CppcheckXml::AnalyzeAllVsConfigsElementName) == 0) guiProject.analyzeAllVsConfigs = node->GetText(); else if (strcmp(node->Name(), CppcheckXml::AddonsElementName) == 0) temp.addons = readXmlStringList(node, "", CppcheckXml::AddonElementName, nullptr); else if (strcmp(node->Name(), CppcheckXml::TagsElementName) == 0) node->Attribute(CppcheckXml::TagElementName); // FIXME: Write some warning else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0) node->Attribute(CppcheckXml::ToolElementName); // FIXME: Write some warning else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0) temp.checkHeaders = (strcmp(node->GetText(), "true") == 0); else if (strcmp(node->Name(), CppcheckXml::CheckUnusedTemplatesElementName) == 0) temp.checkUnusedTemplates = (strcmp(node->GetText(), "true") == 0); else if (strcmp(node->Name(), CppcheckXml::MaxCtuDepthElementName) == 0) temp.maxCtuDepth = std::atoi(node->GetText()); else if (strcmp(node->Name(), CppcheckXml::CheckUnknownFunctionReturn) == 0) ; // TODO else if (strcmp(node->Name(), Settings::SafeChecks::XmlRootName) == 0) { for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { if (strcmp(child->Name(), Settings::SafeChecks::XmlClasses) == 0) temp.safeChecks.classes = true; else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalFunctions) == 0) temp.safeChecks.externalFunctions = true; else if (strcmp(child->Name(), Settings::SafeChecks::XmlInternalFunctions) == 0) temp.safeChecks.internalFunctions = true; else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalVariables) == 0) temp.safeChecks.externalVariables = true; else return false; } } else return false; } settings->basePaths = temp.basePaths; settings->relativePaths |= temp.relativePaths; settings->buildDir = temp.buildDir; settings->includePaths = temp.includePaths; settings->userDefines = temp.userDefines; settings->userUndefs = temp.userUndefs; settings->addons = temp.addons; for (const std::string &p : paths) guiProject.pathNames.push_back(p); for (const std::string &supp : suppressions) settings->nomsg.addSuppressionLine(supp); settings->checkHeaders = temp.checkHeaders; settings->checkUnusedTemplates = temp.checkUnusedTemplates; settings->maxCtuDepth = temp.maxCtuDepth; settings->safeChecks = temp.safeChecks; return true; } void ImportProject::selectOneVsConfig(Settings::PlatformType platform) { std::set filenames; for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { if (it->cfg.empty()) { ++it; continue; } const ImportProject::FileSettings &fs = *it; bool remove = false; if (fs.cfg.compare(0,5,"Debug") != 0) remove = true; if (platform == Settings::Win64 && fs.platformType != platform) remove = true; else if ((platform == Settings::Win32A || platform == Settings::Win32W) && fs.platformType == Settings::Win64) remove = true; else if (fs.platformType != Settings::Win64 && platform == Settings::Win64) remove = true; else if (filenames.find(fs.filename) != filenames.end()) remove = true; if (remove) { it = fileSettings.erase(it); } else { filenames.insert(fs.filename); ++it; } } } cppcheck-1.90/lib/importproject.h000066400000000000000000000133711357737443600170730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef importprojectH #define importprojectH //--------------------------------------------------------------------------- #include "config.h" #include "platform.h" #include "utils.h" #include #include #include #include #include /// @addtogroup Core /// @{ namespace cppcheck { struct stricmp { bool operator()(const std::string &lhs, const std::string &rhs) const { return caseInsensitiveStringCompare(lhs,rhs) < 0; } }; } class Settings; /** * @brief Importing project settings. */ class CPPCHECKLIB ImportProject { public: enum class Type { UNKNOWN, MISSING, COMPILE_DB, VS_SLN, VS_VCXPROJ, BORLAND, CPPCHECK_GUI }; /** File settings. Multiple configurations for a file is allowed. */ struct CPPCHECKLIB FileSettings { FileSettings() : platformType(cppcheck::Platform::Unspecified), msc(false), useMfc(false) {} std::string cfg; std::string filename; std::string defines; std::string cppcheckDefines() const { return defines + (msc ? ";_MSC_VER=1900" : "") + (useMfc ? ";__AFXWIN_H__=1" : ""); } std::set undefs; std::list includePaths; std::list systemIncludePaths; std::string standard; cppcheck::Platform::PlatformType platformType; bool msc; bool useMfc; void parseCommand(const std::string &command); void setDefines(std::string defs); void setIncludePaths(const std::string &basepath, const std::list &in, std::map &variables); }; std::list fileSettings; void selectOneVsConfig(cppcheck::Platform::PlatformType platform); // Cppcheck GUI output struct { std::string analyzeAllVsConfigs; std::vector pathNames; std::list libraries; std::list excludedPaths; std::string projectFile; std::string platform; } guiProject; void ignorePaths(const std::vector &ipaths); void ignoreOtherConfigs(const std::string &cfg); void ignoreOtherPlatforms(cppcheck::Platform::PlatformType platformType); Type import(const std::string &filename, Settings *settings=nullptr); protected: void importCompileCommands(std::istream &istr); bool importCppcheckGuiProject(std::istream &istr, Settings *settings); private: void importSln(std::istream &istr, const std::string &path); void importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories); void importBcb6Prj(const std::string &projectFilename); std::string mPath; }; namespace CppcheckXml { const char ProjectElementName[] = "project"; const char ProjectVersionAttrib[] = "version"; const char ProjectFileVersion[] = "1"; const char BuildDirElementName[] = "builddir"; const char ImportProjectElementName[] = "importproject"; const char AnalyzeAllVsConfigsElementName[] = "analyze-all-vs-configs"; const char IncludeDirElementName[] = "includedir"; const char DirElementName[] = "dir"; const char DirNameAttrib[] = "name"; const char DefinesElementName[] = "defines"; const char DefineName[] = "define"; const char DefineNameAttrib[] = "name"; const char UndefinesElementName[] = "undefines"; const char UndefineName[] = "undefine"; const char PathsElementName[] = "paths"; const char PathName[] = "dir"; const char PathNameAttrib[] = "name"; const char RootPathName[] = "root"; const char RootPathNameAttrib[] = "name"; const char IgnoreElementName[] = "ignore"; const char IgnorePathName[] = "path"; const char IgnorePathNameAttrib[] = "name"; const char ExcludeElementName[] = "exclude"; const char ExcludePathName[] = "path"; const char ExcludePathNameAttrib[] = "name"; const char LibrariesElementName[] = "libraries"; const char LibraryElementName[] = "library"; const char PlatformElementName[] = "platform"; const char SuppressionsElementName[] = "suppressions"; const char SuppressionElementName[] = "suppression"; const char AddonElementName[] = "addon"; const char AddonsElementName[] = "addons"; const char ToolElementName[] = "tool"; const char ToolsElementName[] = "tools"; const char TagsElementName[] = "tags"; const char TagElementName[] = "tag"; const char CheckHeadersElementName[] = "check-headers"; const char CheckUnusedTemplatesElementName[] = "check-unused-templates"; const char MaxCtuDepthElementName[] = "max-ctu-depth"; const char CheckUnknownFunctionReturn[] = "check-unknown-function-return-values"; const char Name[] = "name"; } /// @} //--------------------------------------------------------------------------- #endif // importprojectH cppcheck-1.90/lib/lib.pri000066400000000000000000000072621357737443600153050ustar00rootroot00000000000000# no manual edits - this file is autogenerated by dmake include($$PWD/pcrerules.pri) include($$PWD/../externals/externals.pri) INCLUDEPATH += $$PWD HEADERS += $${PWD}/analyzerinfo.h \ $${PWD}/astutils.h \ $${PWD}/check.h \ $${PWD}/check64bit.h \ $${PWD}/checkassert.h \ $${PWD}/checkautovariables.h \ $${PWD}/checkbool.h \ $${PWD}/checkboost.h \ $${PWD}/checkbufferoverrun.h \ $${PWD}/checkclass.h \ $${PWD}/checkcondition.h \ $${PWD}/checkexceptionsafety.h \ $${PWD}/checkfunctions.h \ $${PWD}/checkinternal.h \ $${PWD}/checkio.h \ $${PWD}/checkleakautovar.h \ $${PWD}/checkmemoryleak.h \ $${PWD}/checknullpointer.h \ $${PWD}/checkother.h \ $${PWD}/checkpostfixoperator.h \ $${PWD}/checksizeof.h \ $${PWD}/checkstl.h \ $${PWD}/checkstring.h \ $${PWD}/checktype.h \ $${PWD}/checkuninitvar.h \ $${PWD}/checkunusedfunctions.h \ $${PWD}/checkunusedvar.h \ $${PWD}/checkvaarg.h \ $${PWD}/cppcheck.h \ $${PWD}/ctu.h \ $${PWD}/errorlogger.h \ $${PWD}/exprengine.h \ $${PWD}/importproject.h \ $${PWD}/library.h \ $${PWD}/mathlib.h \ $${PWD}/path.h \ $${PWD}/pathanalysis.h \ $${PWD}/pathmatch.h \ $${PWD}/platform.h \ $${PWD}/preprocessor.h \ $${PWD}/programmemory.h \ $${PWD}/settings.h \ $${PWD}/suppressions.h \ $${PWD}/symboldatabase.h \ $${PWD}/templatesimplifier.h \ $${PWD}/timer.h \ $${PWD}/token.h \ $${PWD}/tokenize.h \ $${PWD}/tokenlist.h \ $${PWD}/valueflow.h SOURCES += $${PWD}/analyzerinfo.cpp \ $${PWD}/astutils.cpp \ $${PWD}/check.cpp \ $${PWD}/check64bit.cpp \ $${PWD}/checkassert.cpp \ $${PWD}/checkautovariables.cpp \ $${PWD}/checkbool.cpp \ $${PWD}/checkboost.cpp \ $${PWD}/checkbufferoverrun.cpp \ $${PWD}/checkclass.cpp \ $${PWD}/checkcondition.cpp \ $${PWD}/checkexceptionsafety.cpp \ $${PWD}/checkfunctions.cpp \ $${PWD}/checkinternal.cpp \ $${PWD}/checkio.cpp \ $${PWD}/checkleakautovar.cpp \ $${PWD}/checkmemoryleak.cpp \ $${PWD}/checknullpointer.cpp \ $${PWD}/checkother.cpp \ $${PWD}/checkpostfixoperator.cpp \ $${PWD}/checksizeof.cpp \ $${PWD}/checkstl.cpp \ $${PWD}/checkstring.cpp \ $${PWD}/checktype.cpp \ $${PWD}/checkuninitvar.cpp \ $${PWD}/checkunusedfunctions.cpp \ $${PWD}/checkunusedvar.cpp \ $${PWD}/checkvaarg.cpp \ $${PWD}/cppcheck.cpp \ $${PWD}/ctu.cpp \ $${PWD}/errorlogger.cpp \ $${PWD}/exprengine.cpp \ $${PWD}/importproject.cpp \ $${PWD}/library.cpp \ $${PWD}/mathlib.cpp \ $${PWD}/path.cpp \ $${PWD}/pathanalysis.cpp \ $${PWD}/pathmatch.cpp \ $${PWD}/platform.cpp \ $${PWD}/preprocessor.cpp \ $${PWD}/programmemory.cpp \ $${PWD}/settings.cpp \ $${PWD}/suppressions.cpp \ $${PWD}/symboldatabase.cpp \ $${PWD}/templatesimplifier.cpp \ $${PWD}/timer.cpp \ $${PWD}/token.cpp \ $${PWD}/tokenize.cpp \ $${PWD}/tokenlist.cpp \ $${PWD}/valueflow.cpp cppcheck-1.90/lib/library.cpp000066400000000000000000001720341357737443600161730ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "library.h" #include "astutils.h" #include "mathlib.h" #include "path.h" #include "symboldatabase.h" #include "tinyxml2.h" #include "token.h" #include "tokenlist.h" #include "utils.h" #include #include #include #include static std::vector getnames(const char *names) { std::vector ret; while (const char *p = std::strchr(names,',')) { ret.emplace_back(names, p-names); names = p + 1; } ret.emplace_back(names); return ret; } static void gettokenlistfromvalid(const std::string& valid, TokenList& tokenList) { std::istringstream istr(valid + ','); tokenList.createTokens(istr); for (Token *tok = tokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok,"- %num%")) { tok->str("-" + tok->strAt(1)); tok->deleteNext(); } } } Library::Library() : mAllocId(0) { } Library::Error Library::load(const char exename[], const char path[]) { if (std::strchr(path,',') != nullptr) { std::string p(path); for (;;) { const std::string::size_type pos = p.find(','); if (pos == std::string::npos) break; const Error &e = load(exename, p.substr(0,pos).c_str()); if (e.errorcode != OK) return e; p = p.substr(pos+1); } if (!p.empty()) return load(exename, p.c_str()); return Error(); } std::string absolute_path; // open file.. tinyxml2::XMLDocument doc; tinyxml2::XMLError error = doc.LoadFile(path); if (error == tinyxml2::XML_ERROR_FILE_READ_ERROR && Path::getFilenameExtension(path).empty()) // Reading file failed, try again... error = tinyxml2::XML_ERROR_FILE_NOT_FOUND; if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { // failed to open file.. is there no extension? std::string fullfilename(path); if (Path::getFilenameExtension(fullfilename).empty()) { fullfilename += ".cfg"; error = doc.LoadFile(fullfilename.c_str()); if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) absolute_path = Path::getAbsoluteFilePath(fullfilename); } std::list cfgfolders; #ifdef FILESDIR cfgfolders.emplace_back(FILESDIR "/cfg"); #endif if (exename) { const std::string exepath(Path::fromNativeSeparators(Path::getPathFromFilename(exename))); cfgfolders.push_back(exepath + "cfg"); cfgfolders.push_back(exepath); } while (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND && !cfgfolders.empty()) { const std::string cfgfolder(cfgfolders.back()); cfgfolders.pop_back(); const char *sep = (!cfgfolder.empty() && endsWith(cfgfolder,'/') ? "" : "/"); const std::string filename(cfgfolder + sep + fullfilename); error = doc.LoadFile(filename.c_str()); if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) absolute_path = Path::getAbsoluteFilePath(filename); } } else absolute_path = Path::getAbsoluteFilePath(path); if (error == tinyxml2::XML_SUCCESS) { if (mFiles.find(absolute_path) == mFiles.end()) { Error err = load(doc); if (err.errorcode == OK) mFiles.insert(absolute_path); return err; } return Error(OK); // ignore duplicates } if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) return Error(FILE_NOT_FOUND); else { doc.PrintError(); return Error(BAD_XML); } } bool Library::loadxmldata(const char xmldata[], std::size_t len) { tinyxml2::XMLDocument doc; return (tinyxml2::XML_SUCCESS == doc.Parse(xmldata, len)) && (load(doc).errorcode == OK); } Library::Error Library::load(const tinyxml2::XMLDocument &doc) { const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); if (rootnode == nullptr) { doc.PrintError(); return Error(BAD_XML); } if (strcmp(rootnode->Name(),"def") != 0) return Error(UNSUPPORTED_FORMAT, rootnode->Name()); const char* format_string = rootnode->Attribute("format"); int format = 1; // Assume format version 1 if nothing else is specified (very old .cfg files had no 'format' attribute) if (format_string) format = atoi(format_string); if (format > 2 || format <= 0) return Error(UNSUPPORTED_FORMAT); std::set unknown_elements; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { const std::string nodename = node->Name(); if (nodename == "memory" || nodename == "resource") { // get allocationId to use.. int allocationId = 0; for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { if (strcmp(memorynode->Name(),"dealloc")==0) { const std::map::const_iterator it = mDealloc.find(memorynode->GetText()); if (it != mDealloc.end()) { allocationId = it->second.groupId; break; } } } if (allocationId == 0) { if (nodename == "memory") while (!ismemory(++mAllocId)); else while (!isresource(++mAllocId)); allocationId = mAllocId; } // add alloc/dealloc/use functions.. for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { const std::string memorynodename = memorynode->Name(); if (memorynodename == "alloc" || memorynodename == "realloc") { AllocFunc temp = {0}; temp.groupId = allocationId; if (memorynode->Attribute("init", "false")) returnuninitdata.insert(memorynode->GetText()); const char *arg = memorynode->Attribute("arg"); if (arg) temp.arg = atoi(arg); else temp.arg = -1; const char *bufferSize = memorynode->Attribute("buffer-size"); if (!bufferSize) temp.bufferSize = AllocFunc::BufferSize::none; else { if (std::strncmp(bufferSize, "malloc", 6) == 0) temp.bufferSize = AllocFunc::BufferSize::malloc; else if (std::strncmp(bufferSize, "calloc", 6) == 0) temp.bufferSize = AllocFunc::BufferSize::calloc; else if (std::strncmp(bufferSize, "strdup", 6) == 0) temp.bufferSize = AllocFunc::BufferSize::strdup; else return Error(BAD_ATTRIBUTE_VALUE, bufferSize); temp.bufferSizeArg1 = 1; temp.bufferSizeArg2 = 2; if (bufferSize[6] == 0) { // use default values } else if (bufferSize[6] == ':' && bufferSize[7] >= '1' && bufferSize[7] <= '5') { temp.bufferSizeArg1 = bufferSize[7] - '0'; if (bufferSize[8] == ',' && bufferSize[9] >= '1' && bufferSize[9] <= '5') temp.bufferSizeArg2 = bufferSize[9] - '0'; } else return Error(BAD_ATTRIBUTE_VALUE, bufferSize); } if (memorynodename == "realloc") { const char *reallocArg = memorynode->Attribute("realloc-arg"); if (reallocArg) temp.reallocArg = atoi(reallocArg); else temp.reallocArg = 1; } if (memorynodename != "realloc") mAlloc[memorynode->GetText()] = temp; else mRealloc[memorynode->GetText()] = temp; } else if (memorynodename == "dealloc") { AllocFunc temp = {0}; temp.groupId = allocationId; const char *arg = memorynode->Attribute("arg"); if (arg) temp.arg = atoi(arg); else temp.arg = 1; mDealloc[memorynode->GetText()] = temp; } else if (memorynodename == "use") functions[memorynode->GetText()].use = true; else unknown_elements.insert(memorynodename); } } else if (nodename == "define") { const char *name = node->Attribute("name"); if (name == nullptr) return Error(MISSING_ATTRIBUTE, "name"); const char *value = node->Attribute("value"); if (value == nullptr) return Error(MISSING_ATTRIBUTE, "value"); defines.push_back(std::string(name) + " " + value); } else if (nodename == "function") { const char *name = node->Attribute("name"); if (name == nullptr) return Error(MISSING_ATTRIBUTE, "name"); for (const std::string &s : getnames(name)) { const Error &err = loadFunction(node, s, unknown_elements); if (err.errorcode != ErrorCode::OK) return err; } } else if (nodename == "reflection") { for (const tinyxml2::XMLElement *reflectionnode = node->FirstChildElement(); reflectionnode; reflectionnode = reflectionnode->NextSiblingElement()) { if (strcmp(reflectionnode->Name(), "call") != 0) { unknown_elements.insert(reflectionnode->Name()); continue; } const char * const argString = reflectionnode->Attribute("arg"); if (!argString) return Error(MISSING_ATTRIBUTE, "arg"); mReflection[reflectionnode->GetText()] = atoi(argString); } } else if (nodename == "markup") { const char * const extension = node->Attribute("ext"); if (!extension) return Error(MISSING_ATTRIBUTE, "ext"); mMarkupExtensions.insert(extension); mReportErrors[extension] = (node->Attribute("reporterrors", "true") != nullptr); mProcessAfterCode[extension] = (node->Attribute("aftercode", "true") != nullptr); for (const tinyxml2::XMLElement *markupnode = node->FirstChildElement(); markupnode; markupnode = markupnode->NextSiblingElement()) { const std::string markupnodename = markupnode->Name(); if (markupnodename == "keywords") { for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { if (strcmp(librarynode->Name(), "keyword") == 0) { const char* nodeName = librarynode->Attribute("name"); if (nodeName == nullptr) return Error(MISSING_ATTRIBUTE, "name"); mKeywords[extension].insert(nodeName); } else unknown_elements.insert(librarynode->Name()); } } else if (markupnodename == "exported") { for (const tinyxml2::XMLElement *exporter = markupnode->FirstChildElement(); exporter; exporter = exporter->NextSiblingElement()) { if (strcmp(exporter->Name(), "exporter") != 0) { unknown_elements.insert(exporter->Name()); continue; } const char * const prefix = exporter->Attribute("prefix"); if (!prefix) return Error(MISSING_ATTRIBUTE, "prefix"); for (const tinyxml2::XMLElement *e = exporter->FirstChildElement(); e; e = e->NextSiblingElement()) { const std::string ename = e->Name(); if (ename == "prefix") mExporters[prefix].addPrefix(e->GetText()); else if (ename == "suffix") mExporters[prefix].addSuffix(e->GetText()); else unknown_elements.insert(ename); } } } else if (markupnodename == "imported") { for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { if (strcmp(librarynode->Name(), "importer") == 0) mImporters[extension].insert(librarynode->GetText()); else unknown_elements.insert(librarynode->Name()); } } else if (markupnodename == "codeblocks") { for (const tinyxml2::XMLElement *blocknode = markupnode->FirstChildElement(); blocknode; blocknode = blocknode->NextSiblingElement()) { const std::string blocknodename = blocknode->Name(); if (blocknodename == "block") { const char * blockName = blocknode->Attribute("name"); if (blockName) mExecutableBlocks[extension].addBlock(blockName); } else if (blocknodename == "structure") { const char * start = blocknode->Attribute("start"); if (start) mExecutableBlocks[extension].setStart(start); const char * end = blocknode->Attribute("end"); if (end) mExecutableBlocks[extension].setEnd(end); const char * offset = blocknode->Attribute("offset"); if (offset) mExecutableBlocks[extension].setOffset(atoi(offset)); } else unknown_elements.insert(blocknodename); } } else unknown_elements.insert(markupnodename); } } else if (nodename == "container") { const char* const id = node->Attribute("id"); if (!id) return Error(MISSING_ATTRIBUTE, "id"); Container& container = containers[id]; const char* const inherits = node->Attribute("inherits"); if (inherits) { const std::map::const_iterator i = containers.find(inherits); if (i != containers.end()) container = i->second; // Take values from parent and overwrite them if necessary else return Error(BAD_ATTRIBUTE_VALUE, inherits); } const char* const startPattern = node->Attribute("startPattern"); if (startPattern) { container.startPattern = startPattern; container.startPattern2 = container.startPattern + " !!::"; } const char* const endPattern = node->Attribute("endPattern"); if (endPattern) container.endPattern = endPattern; const char* const itEndPattern = node->Attribute("itEndPattern"); if (itEndPattern) container.itEndPattern = itEndPattern; const char* const opLessAllowed = node->Attribute("opLessAllowed"); if (opLessAllowed) container.opLessAllowed = std::string(opLessAllowed) == "true"; const char* const hasInitializerListConstructor = node->Attribute("hasInitializerListConstructor"); if (hasInitializerListConstructor) container.hasInitializerListConstructor = std::string(hasInitializerListConstructor) == "true"; for (const tinyxml2::XMLElement *containerNode = node->FirstChildElement(); containerNode; containerNode = containerNode->NextSiblingElement()) { const std::string containerNodeName = containerNode->Name(); if (containerNodeName == "size" || containerNodeName == "access" || containerNodeName == "other") { for (const tinyxml2::XMLElement *functionNode = containerNode->FirstChildElement(); functionNode; functionNode = functionNode->NextSiblingElement()) { if (std::string(functionNode->Name()) != "function") { unknown_elements.insert(functionNode->Name()); continue; } const char* const functionName = functionNode->Attribute("name"); if (!functionName) return Error(MISSING_ATTRIBUTE, "name"); const char* const action_ptr = functionNode->Attribute("action"); Container::Action action = Container::Action::NO_ACTION; if (action_ptr) { std::string actionName = action_ptr; if (actionName == "resize") action = Container::Action::RESIZE; else if (actionName == "clear") action = Container::Action::CLEAR; else if (actionName == "push") action = Container::Action::PUSH; else if (actionName == "pop") action = Container::Action::POP; else if (actionName == "find") action = Container::Action::FIND; else if (actionName == "insert") action = Container::Action::INSERT; else if (actionName == "erase") action = Container::Action::ERASE; else if (actionName == "change-content") action = Container::Action::CHANGE_CONTENT; else if (actionName == "change-internal") action = Container::Action::CHANGE_INTERNAL; else if (actionName == "change") action = Container::Action::CHANGE; else return Error(BAD_ATTRIBUTE_VALUE, actionName); } const char* const yield_ptr = functionNode->Attribute("yields"); Container::Yield yield = Container::Yield::NO_YIELD; if (yield_ptr) { std::string yieldName = yield_ptr; if (yieldName == "at_index") yield = Container::Yield::AT_INDEX; else if (yieldName == "item") yield = Container::Yield::ITEM; else if (yieldName == "buffer") yield = Container::Yield::BUFFER; else if (yieldName == "buffer-nt") yield = Container::Yield::BUFFER_NT; else if (yieldName == "start-iterator") yield = Container::Yield::START_ITERATOR; else if (yieldName == "end-iterator") yield = Container::Yield::END_ITERATOR; else if (yieldName == "iterator") yield = Container::Yield::ITERATOR; else if (yieldName == "size") yield = Container::Yield::SIZE; else if (yieldName == "empty") yield = Container::Yield::EMPTY; else return Error(BAD_ATTRIBUTE_VALUE, yieldName); } container.functions[functionName].action = action; container.functions[functionName].yield = yield; } if (containerNodeName == "size") { const char* const templateArg = containerNode->Attribute("templateParameter"); if (templateArg) container.size_templateArgNo = atoi(templateArg); } else if (containerNodeName == "access") { const char* const indexArg = containerNode->Attribute("indexOperator"); if (indexArg) container.arrayLike_indexOp = std::string(indexArg) == "array-like"; } } else if (containerNodeName == "type") { const char* const templateArg = containerNode->Attribute("templateParameter"); if (templateArg) container.type_templateArgNo = atoi(templateArg); const char* const string = containerNode->Attribute("string"); if (string) container.stdStringLike = std::string(string) == "std-like"; const char* const associative = containerNode->Attribute("associative"); if (associative) container.stdAssociativeLike = std::string(associative) == "std-like"; } else unknown_elements.insert(containerNodeName); } } else if (nodename == "smart-pointer") { const char *className = node->Attribute("class-name"); if (className) smartPointers.insert(className); } else if (nodename == "type-checks") { for (const tinyxml2::XMLElement *checkNode = node->FirstChildElement(); checkNode; checkNode = checkNode->NextSiblingElement()) { const std::string &checkName = checkNode->Name(); for (const tinyxml2::XMLElement *checkTypeNode = checkNode->FirstChildElement(); checkTypeNode; checkTypeNode = checkTypeNode->NextSiblingElement()) { const std::string checkTypeName = checkTypeNode->Name(); const char *typeName = checkTypeNode->GetText(); if (!typeName) continue; if (checkTypeName == "check") mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::check; else if (checkTypeName == "suppress") mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::suppress; } } } else if (nodename == "podtype") { const char * const name = node->Attribute("name"); if (!name) return Error(MISSING_ATTRIBUTE, "name"); PodType podType = {0}; podType.stdtype = PodType::NO; const char * const stdtype = node->Attribute("stdtype"); if (stdtype) { if (std::strcmp(stdtype, "bool") == 0) podType.stdtype = PodType::BOOL; else if (std::strcmp(stdtype, "char") == 0) podType.stdtype = PodType::CHAR; else if (std::strcmp(stdtype, "short") == 0) podType.stdtype = PodType::SHORT; else if (std::strcmp(stdtype, "int") == 0) podType.stdtype = PodType::INT; else if (std::strcmp(stdtype, "long") == 0) podType.stdtype = PodType::LONG; else if (std::strcmp(stdtype, "long long") == 0) podType.stdtype = PodType::LONGLONG; } const char * const size = node->Attribute("size"); if (size) podType.size = atoi(size); const char * const sign = node->Attribute("sign"); if (sign) podType.sign = *sign; for (const std::string &s : getnames(name)) mPodTypes[s] = podType; } else if (nodename == "platformtype") { const char * const type_name = node->Attribute("name"); if (type_name == nullptr) return Error(MISSING_ATTRIBUTE, "name"); const char *value = node->Attribute("value"); if (value == nullptr) return Error(MISSING_ATTRIBUTE, "value"); PlatformType type; type.mType = value; std::set platform; for (const tinyxml2::XMLElement *typenode = node->FirstChildElement(); typenode; typenode = typenode->NextSiblingElement()) { const std::string typenodename = typenode->Name(); if (typenodename == "platform") { const char * const type_attribute = typenode->Attribute("type"); if (type_attribute == nullptr) return Error(MISSING_ATTRIBUTE, "type"); platform.insert(type_attribute); } else if (typenodename == "signed") type.mSigned = true; else if (typenodename == "unsigned") type.mUnsigned = true; else if (typenodename == "long") type.mLong = true; else if (typenodename == "pointer") type.mPointer= true; else if (typenodename == "ptr_ptr") type.mPtrPtr = true; else if (typenodename == "const_ptr") type.mConstPtr = true; else unknown_elements.insert(typenodename); } if (platform.empty()) { const PlatformType * const type_ptr = platform_type(type_name, emptyString); if (type_ptr) { if (*type_ptr == type) return Error(DUPLICATE_PLATFORM_TYPE, type_name); return Error(PLATFORM_TYPE_REDEFINED, type_name); } mPlatformTypes[type_name] = type; } else { for (const std::string &p : platform) { const PlatformType * const type_ptr = platform_type(type_name, p); if (type_ptr) { if (*type_ptr == type) return Error(DUPLICATE_PLATFORM_TYPE, type_name); return Error(PLATFORM_TYPE_REDEFINED, type_name); } mPlatforms[p].mPlatformTypes[type_name] = type; } } } else unknown_elements.insert(nodename); } if (!unknown_elements.empty()) { std::string str; for (std::set::const_iterator i = unknown_elements.begin(); i != unknown_elements.end();) { str += *i; if (++i != unknown_elements.end()) str += ", "; } return Error(UNKNOWN_ELEMENT, str); } return Error(OK); } Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set &unknown_elements) { if (name.empty()) return Error(OK); Function& func = functions[name]; for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) { const std::string functionnodename = functionnode->Name(); if (functionnodename == "noreturn") mNoReturn[name] = (strcmp(functionnode->GetText(), "true") == 0); else if (functionnodename == "pure") func.ispure = true; else if (functionnodename == "const") { func.ispure = true; func.isconst = true; // a constant function is pure } else if (functionnodename == "leak-ignore") func.leakignore = true; else if (functionnodename == "use-retval") func.useretval = true; else if (functionnodename == "returnValue") { if (const char *expr = functionnode->GetText()) mReturnValue[name] = expr; if (const char *type = functionnode->Attribute("type")) mReturnValueType[name] = type; if (const char *container = functionnode->Attribute("container")) mReturnValueContainer[name] = std::atoi(container); if (const char *unknownReturnValues = functionnode->Attribute("unknownValues")) { if (std::strcmp(unknownReturnValues, "all") == 0) { std::vector values{LLONG_MIN, LLONG_MAX}; mUnknownReturnValues[name] = values; } } } else if (functionnodename == "arg") { const char* argNrString = functionnode->Attribute("nr"); if (!argNrString) return Error(MISSING_ATTRIBUTE, "nr"); const bool bAnyArg = strcmp(argNrString, "any") == 0; const bool bVariadicArg = strcmp(argNrString, "variadic") == 0; const int nr = (bAnyArg || bVariadicArg) ? -1 : std::atoi(argNrString); ArgumentChecks &ac = func.argumentChecks[nr]; ac.optional = functionnode->Attribute("default") != nullptr; ac.variadic = bVariadicArg; const char * const argDirection = functionnode->Attribute("direction"); if (argDirection) { const size_t argDirLen = strlen(argDirection); if (!strncmp(argDirection, "in", argDirLen)) { ac.direction = ArgumentChecks::Direction::DIR_IN; } else if (!strncmp(argDirection, "out", argDirLen)) { ac.direction = ArgumentChecks::Direction::DIR_OUT; } else if (!strncmp(argDirection, "inout", argDirLen)) { ac.direction = ArgumentChecks::Direction::DIR_INOUT; } } for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { const std::string argnodename = argnode->Name(); int indirect = 0; const char * const indirectStr = node->Attribute("indirect"); if (indirectStr) indirect = atoi(indirectStr); if (argnodename == "not-bool") ac.notbool = true; else if (argnodename == "not-null") ac.notnull = true; else if (argnodename == "not-uninit") ac.notuninit = indirect; else if (argnodename == "formatstr") ac.formatstr = true; else if (argnodename == "strz") ac.strz = true; else if (argnodename == "valid") { // Validate the validation expression const char *p = argnode->GetText(); bool error = false; bool range = false; bool has_dot = false; if (!p) return Error(BAD_ATTRIBUTE_VALUE, "\"\""); error = *p == '.'; for (; *p; p++) { if (std::isdigit(*p)) error |= (*(p+1) == '-'); else if (*p == ':') { error |= range | (*(p+1) == '.'); range = true; has_dot = false; } else if (*p == '-') error |= (!std::isdigit(*(p+1))); else if (*p == ',') { range = false; error |= *(p+1) == '.'; has_dot = false; } else if (*p == '.') { error |= has_dot | (!std::isdigit(*(p+1))); has_dot = true; } else error = true; } if (error) return Error(BAD_ATTRIBUTE_VALUE, argnode->GetText()); // Set validation expression ac.valid = argnode->GetText(); } else if (argnodename == "minsize") { const char *typeattr = argnode->Attribute("type"); if (!typeattr) return Error(MISSING_ATTRIBUTE, "type"); ArgumentChecks::MinSize::Type type; if (strcmp(typeattr,"strlen")==0) type = ArgumentChecks::MinSize::Type::STRLEN; else if (strcmp(typeattr,"argvalue")==0) type = ArgumentChecks::MinSize::Type::ARGVALUE; else if (strcmp(typeattr,"sizeof")==0) type = ArgumentChecks::MinSize::Type::SIZEOF; else if (strcmp(typeattr,"mul")==0) type = ArgumentChecks::MinSize::Type::MUL; else if (strcmp(typeattr,"value")==0) type = ArgumentChecks::MinSize::Type::VALUE; else return Error(BAD_ATTRIBUTE_VALUE, typeattr); if (type == ArgumentChecks::MinSize::Type::VALUE) { const char *valueattr = argnode->Attribute("value"); if (!valueattr) return Error(MISSING_ATTRIBUTE, "value"); long long minsizevalue = 0; try { minsizevalue = MathLib::toLongNumber(valueattr); } catch (const InternalError&) { return Error(BAD_ATTRIBUTE_VALUE, valueattr); } if (minsizevalue <= 0) return Error(BAD_ATTRIBUTE_VALUE, valueattr); ac.minsizes.emplace_back(type, 0); ac.minsizes.back().value = minsizevalue; } else { const char *argattr = argnode->Attribute("arg"); if (!argattr) return Error(MISSING_ATTRIBUTE, "arg"); if (strlen(argattr) != 1 || argattr[0]<'0' || argattr[0]>'9') return Error(BAD_ATTRIBUTE_VALUE, argattr); ac.minsizes.reserve(type == ArgumentChecks::MinSize::Type::MUL ? 2 : 1); ac.minsizes.emplace_back(type, argattr[0] - '0'); if (type == ArgumentChecks::MinSize::Type::MUL) { const char *arg2attr = argnode->Attribute("arg2"); if (!arg2attr) return Error(MISSING_ATTRIBUTE, "arg2"); if (strlen(arg2attr) != 1 || arg2attr[0]<'0' || arg2attr[0]>'9') return Error(BAD_ATTRIBUTE_VALUE, arg2attr); ac.minsizes.back().arg2 = arg2attr[0] - '0'; } } } else if (argnodename == "iterator") { ac.iteratorInfo.it = true; const char* str = argnode->Attribute("type"); ac.iteratorInfo.first = str ? (std::strcmp(str, "first") == 0) : false; ac.iteratorInfo.last = str ? (std::strcmp(str, "last") == 0) : false; str = argnode->Attribute("container"); ac.iteratorInfo.container = str ? std::atoi(str) : 0; } else unknown_elements.insert(argnodename); } if (ac.notuninit == 0) ac.notuninit = ac.notnull ? 1 : 0; } else if (functionnodename == "ignorefunction") { func.ignore = true; } else if (functionnodename == "formatstr") { func.formatstr = true; const tinyxml2::XMLAttribute* scan = functionnode->FindAttribute("scan"); const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure"); func.formatstr_scan = scan && scan->BoolValue(); func.formatstr_secure = secure && secure->BoolValue(); } else if (functionnodename == "warn") { WarnInfo wi; const char* const severity = functionnode->Attribute("severity"); if (severity == nullptr) return Error(MISSING_ATTRIBUTE, "severity"); wi.severity = Severity::fromString(severity); const char* const cstd = functionnode->Attribute("cstd"); if (cstd) { if (!wi.standards.setC(cstd)) return Error(BAD_ATTRIBUTE_VALUE, cstd); } else wi.standards.c = Standards::C89; const char* const cppstd = functionnode->Attribute("cppstd"); if (cppstd) { if (!wi.standards.setCPP(cppstd)) return Error(BAD_ATTRIBUTE_VALUE, cppstd); } else wi.standards.cpp = Standards::CPP03; const char* const reason = functionnode->Attribute("reason"); const char* const alternatives = functionnode->Attribute("alternatives"); if (reason && alternatives) { // Construct message wi.message = std::string(reason) + " function '" + name + "' called. It is recommended to use "; std::vector alt = getnames(alternatives); for (std::size_t i = 0; i < alt.size(); ++i) { wi.message += "'" + alt[i] + "'"; if (i == alt.size() - 1) wi.message += " instead."; else if (i == alt.size() - 2) wi.message += " or "; else wi.message += ", "; } } else { const char * const message = functionnode->GetText(); if (!message) { return Error(MISSING_ATTRIBUTE, "\"reason\" and \"alternatives\" or some text."); } else wi.message = message; } functionwarn[name] = wi; } else unknown_elements.insert(functionnodename); } return Error(OK); } bool Library::isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const { const ArgumentChecks *ac = getarg(ftok, argnr); if (!ac || ac->valid.empty()) return true; else if (ac->valid.find('.') != std::string::npos) return isFloatArgValid(ftok, argnr, argvalue); TokenList tokenList(nullptr); gettokenlistfromvalid(ac->valid, tokenList); for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { if (tok->isNumber() && argvalue == MathLib::toLongNumber(tok->str())) return true; if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toLongNumber(tok->str()) && argvalue <= MathLib::toLongNumber(tok->strAt(2))) return true; if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toLongNumber(tok->str())) return true; if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toLongNumber(tok->strAt(1))) return true; } return false; } bool Library::isFloatArgValid(const Token *ftok, int argnr, double argvalue) const { const ArgumentChecks *ac = getarg(ftok, argnr); if (!ac || ac->valid.empty()) return true; TokenList tokenList(nullptr); gettokenlistfromvalid(ac->valid, tokenList); for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toDoubleNumber(tok->str()) && argvalue <= MathLib::toDoubleNumber(tok->strAt(2))) return true; if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toDoubleNumber(tok->str())) return true; if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toDoubleNumber(tok->strAt(1))) return true; } return false; } std::string Library::getFunctionName(const Token *ftok, bool *error) const { if (!ftok) { *error = true; return ""; } if (ftok->isName()) { for (const Scope *scope = ftok->scope(); scope; scope = scope->nestedIn) { if (!scope->isClassOrStruct()) continue; const std::vector &derivedFrom = scope->definedType->derivedFrom; for (const Type::BaseInfo & baseInfo : derivedFrom) { const std::string name(baseInfo.name + "::" + ftok->str()); if (functions.find(name) != functions.end() && matchArguments(ftok, name)) return name; } } return ftok->str(); } if (ftok->str() == "::") { if (!ftok->astOperand2()) return getFunctionName(ftok->astOperand1(), error); return getFunctionName(ftok->astOperand1(),error) + "::" + getFunctionName(ftok->astOperand2(),error); } if (ftok->str() == "." && ftok->astOperand1()) { const std::string type = astCanonicalType(ftok->astOperand1()); if (type.empty()) { *error = true; return ""; } return type + "::" + getFunctionName(ftok->astOperand2(),error); } *error = true; return ""; } std::string Library::getFunctionName(const Token *ftok) const { if (!Token::Match(ftok, "%name% (") && (ftok->strAt(-1) != "&" || ftok->previous()->astOperand2())) return ""; // Lookup function name using AST.. if (ftok->astParent()) { bool error = false; const Token * tok = ftok->astParent()->isUnaryOp("&") ? ftok->astParent()->astOperand1() : ftok->next()->astOperand1(); const std::string ret = getFunctionName(tok, &error); return error ? std::string() : ret; } // Lookup function name without using AST.. if (Token::simpleMatch(ftok->previous(), ".")) return ""; if (!Token::Match(ftok->tokAt(-2), "%name% ::")) return ftok->str(); std::string ret(ftok->str()); ftok = ftok->tokAt(-2); while (Token::Match(ftok, "%name% ::")) { ret = ftok->str() + "::" + ret; ftok = ftok->tokAt(-2); } return ret; } bool Library::isnullargbad(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); if (!arg) { // scan format string argument should not be null const std::string funcname = getFunctionName(ftok); const std::map::const_iterator it = functions.find(funcname); if (it != functions.cend() && it->second.formatstr && it->second.formatstr_scan) return true; } return arg && arg->notnull; } bool Library::isuninitargbad(const Token *ftok, int argnr, int indirect) const { const ArgumentChecks *arg = getarg(ftok, argnr); if (!arg) { // non-scan format string argument should not be uninitialized const std::string funcname = getFunctionName(ftok); const std::map::const_iterator it = functions.find(funcname); if (it != functions.cend() && it->second.formatstr && !it->second.formatstr_scan) return true; } return arg && arg->notuninit >= indirect; } /** get allocation info for function */ const Library::AllocFunc* Library::getAllocFuncInfo(const Token *tok) const { const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mAlloc, funcname); } /** get deallocation info for function */ const Library::AllocFunc* Library::getDeallocFuncInfo(const Token *tok) const { const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mDealloc, funcname); } /** get reallocation info for function */ const Library::AllocFunc* Library::getReallocFuncInfo(const Token *tok) const { const std::string funcname = getFunctionName(tok); return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mRealloc, funcname); } /** get allocation id for function */ int Library::getAllocId(const Token *tok, int arg) const { const Library::AllocFunc* af = getAllocFuncInfo(tok); return (af && af->arg == arg) ? af->groupId : 0; } /** get deallocation id for function */ int Library::getDeallocId(const Token *tok, int arg) const { const Library::AllocFunc* af = getDeallocFuncInfo(tok); return (af && af->arg == arg) ? af->groupId : 0; } /** get reallocation id for function */ int Library::getReallocId(const Token *tok, int arg) const { const Library::AllocFunc* af = getReallocFuncInfo(tok); return (af && af->arg == arg) ? af->groupId : 0; } const Library::ArgumentChecks * Library::getarg(const Token *ftok, int argnr) const { if (isNotLibraryFunction(ftok)) return nullptr; const std::map::const_iterator it1 = functions.find(getFunctionName(ftok)); if (it1 == functions.cend()) return nullptr; const std::map::const_iterator it2 = it1->second.argumentChecks.find(argnr); if (it2 != it1->second.argumentChecks.cend()) return &it2->second; const std::map::const_iterator it3 = it1->second.argumentChecks.find(-1); if (it3 != it1->second.argumentChecks.cend()) return &it3->second; return nullptr; } bool Library::isScopeNoReturn(const Token *end, std::string *unknownFunc) const { if (unknownFunc) unknownFunc->clear(); if (Token::Match(end->tokAt(-2), "!!{ ; }")) { const Token *lastTop = end->tokAt(-2)->astTop(); if (Token::simpleMatch(lastTop, "<<") && Token::simpleMatch(lastTop->astOperand1(), "(") && Token::Match(lastTop->astOperand1()->previous(), "%name% (")) return isnoreturn(lastTop->astOperand1()->previous()); } if (!Token::simpleMatch(end->tokAt(-2), ") ; }")) return false; const Token *funcname = end->linkAt(-2)->previous(); const Token *start = funcname; if (Token::Match(funcname->tokAt(-3),"( * %name% )")) { funcname = funcname->previous(); start = funcname->tokAt(-3); } else if (funcname->isName()) { while (Token::Match(start, "%name%|.|::")) start = start->previous(); } else { return false; } if (Token::Match(start,"[;{}]") && Token::Match(funcname, "%name% )| (")) { if (funcname->str() == "exit") return true; if (!isnotnoreturn(funcname)) { if (unknownFunc && !isnoreturn(funcname)) *unknownFunc = funcname->str(); return true; } } return false; } const Library::Container* Library::detectContainer(const Token* typeStart, bool iterator) const { for (std::map::const_iterator i = containers.begin(); i != containers.end(); ++i) { const Container& container = i->second; if (container.startPattern.empty()) continue; if (!Token::Match(typeStart, container.startPattern2.c_str())) continue; if (!iterator && container.endPattern.empty()) // If endPattern is undefined, it will always match, but itEndPattern has to be defined. return &container; for (const Token* tok = typeStart; tok && !tok->varId(); tok = tok->next()) { if (tok->link()) { const std::string& endPattern = iterator ? container.itEndPattern : container.endPattern; if (Token::Match(tok->link(), endPattern.c_str())) return &container; break; } } } return nullptr; } bool Library::isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback) { if (!cond) return false; if (cond->str() == "(") { const Token* tok = cond->astOperand1(); if (tok && tok->str() == ".") { if (tok->astOperand1() && tok->astOperand1()->valueType()) { if (const Library::Container *container = tok->astOperand1()->valueType()->container) { return tok->astOperand2() && y == container->getYield(tok->astOperand2()->str()); } } else if (!fallback.empty()) { return Token::simpleMatch(cond, "( )") && cond->previous()->str() == fallback; } } } return false; } // returns true if ftok is not a library function bool Library::isNotLibraryFunction(const Token *ftok) const { if (ftok->function() && ftok->function()->nestedIn && ftok->function()->nestedIn->type != Scope::eGlobal) return true; // variables are not library functions. if (ftok->varId()) return true; return !matchArguments(ftok, getFunctionName(ftok)); } bool Library::matchArguments(const Token *ftok, const std::string &functionName) const { const int callargs = numberOfArguments(ftok); const std::map::const_iterator it = functions.find(functionName); if (it == functions.cend()) return (callargs == 0); int args = 0; int firstOptionalArg = -1; for (std::map::const_iterator it2 = it->second.argumentChecks.cbegin(); it2 != it->second.argumentChecks.cend(); ++it2) { if (it2->first > args) args = it2->first; if (it2->second.optional && (firstOptionalArg == -1 || firstOptionalArg > it2->first)) firstOptionalArg = it2->first; if (it2->second.formatstr || it2->second.variadic) return args <= callargs; } return (firstOptionalArg < 0) ? args == callargs : (callargs >= firstOptionalArg-1 && callargs <= args); } const Library::WarnInfo* Library::getWarnInfo(const Token* ftok) const { if (isNotLibraryFunction(ftok)) return nullptr; std::map::const_iterator i = functionwarn.find(getFunctionName(ftok)); if (i == functionwarn.cend()) return nullptr; return &i->second; } bool Library::formatstr_function(const Token* ftok) const { if (isNotLibraryFunction(ftok)) return false; const std::map::const_iterator it = functions.find(getFunctionName(ftok)); if (it != functions.cend()) return it->second.formatstr; return false; } int Library::formatstr_argno(const Token* ftok) const { const std::map& argumentChecksFunc = functions.at(getFunctionName(ftok)).argumentChecks; for (std::map::const_iterator i = argumentChecksFunc.cbegin(); i != argumentChecksFunc.cend(); ++i) { if (i->second.formatstr) { return i->first - 1; } } return -1; } bool Library::formatstr_scan(const Token* ftok) const { return functions.at(getFunctionName(ftok)).formatstr_scan; } bool Library::formatstr_secure(const Token* ftok) const { return functions.at(getFunctionName(ftok)).formatstr_secure; } bool Library::isUseRetVal(const Token* ftok) const { if (isNotLibraryFunction(ftok)) return false; const std::map::const_iterator it = functions.find(getFunctionName(ftok)); if (it != functions.cend()) return it->second.useretval; return false; } const std::string& Library::returnValue(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return emptyString; const std::map::const_iterator it = mReturnValue.find(getFunctionName(ftok)); return it != mReturnValue.end() ? it->second : emptyString; } const std::string& Library::returnValueType(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return emptyString; const std::map::const_iterator it = mReturnValueType.find(getFunctionName(ftok)); return it != mReturnValueType.end() ? it->second : emptyString; } int Library::returnValueContainer(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return -1; const std::map::const_iterator it = mReturnValueContainer.find(getFunctionName(ftok)); return it != mReturnValueContainer.end() ? it->second : -1; } std::vector Library::unknownReturnValues(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return std::vector(); const std::map>::const_iterator it = mUnknownReturnValues.find(getFunctionName(ftok)); return (it == mUnknownReturnValues.end()) ? std::vector() : it->second; } bool Library::hasminsize(const Token *ftok) const { if (isNotLibraryFunction(ftok)) return false; const std::map::const_iterator it1 = functions.find(getFunctionName(ftok)); if (it1 == functions.cend()) return false; for (std::map::const_iterator it2 = it1->second.argumentChecks.cbegin(); it2 != it1->second.argumentChecks.cend(); ++it2) { if (!it2->second.minsizes.empty()) return true; } return false; } Library::ArgumentChecks::Direction Library::getArgDirection(const Token* ftok, int argnr) const { const ArgumentChecks* arg = getarg(ftok, argnr); if (arg) return arg->direction; if (formatstr_function(ftok)) { const int fs_argno = formatstr_argno(ftok); if (fs_argno >= 0 && argnr >= fs_argno) { if (formatstr_scan(ftok)) return ArgumentChecks::Direction::DIR_OUT; else return ArgumentChecks::Direction::DIR_IN; } } return ArgumentChecks::Direction::DIR_UNKNOWN; } bool Library::ignorefunction(const std::string& functionName) const { const std::map::const_iterator it = functions.find(functionName); if (it != functions.cend()) return it->second.ignore; return false; } bool Library::isUse(const std::string& functionName) const { const std::map::const_iterator it = functions.find(functionName); if (it != functions.cend()) return it->second.use; return false; } bool Library::isLeakIgnore(const std::string& functionName) const { const std::map::const_iterator it = functions.find(functionName); if (it != functions.cend()) return it->second.leakignore; return false; } bool Library::isFunctionConst(const std::string& functionName, bool pure) const { const std::map::const_iterator it = functions.find(functionName); if (it != functions.cend()) return pure ? it->second.ispure : it->second.isconst; return false; } bool Library::isFunctionConst(const Token *ftok) const { if (ftok->function() && ftok->function()->isAttributeConst()) return true; if (isNotLibraryFunction(ftok)) return false; const std::map::const_iterator it = functions.find(getFunctionName(ftok)); return (it != functions.end() && it->second.isconst); } bool Library::isnoreturn(const Token *ftok) const { if (ftok->function() && ftok->function()->isAttributeNoreturn()) return true; if (isNotLibraryFunction(ftok)) return false; const std::map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); return (it != mNoReturn.end() && it->second); } bool Library::isnotnoreturn(const Token *ftok) const { if (ftok->function() && ftok->function()->isAttributeNoreturn()) return false; if (isNotLibraryFunction(ftok)) return false; const std::map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); return (it != mNoReturn.end() && !it->second); } bool Library::markupFile(const std::string &path) const { return mMarkupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != mMarkupExtensions.end(); } bool Library::processMarkupAfterCode(const std::string &path) const { const std::map::const_iterator it = mProcessAfterCode.find(Path::getFilenameExtensionInLowerCase(path)); return (it == mProcessAfterCode.end() || it->second); } bool Library::reportErrors(const std::string &path) const { const std::map::const_iterator it = mReportErrors.find(Path::getFilenameExtensionInLowerCase(path)); return (it == mReportErrors.end() || it->second); } bool Library::isexecutableblock(const std::string &file, const std::string &token) const { const std::map::const_iterator it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); return (it != mExecutableBlocks.end() && it->second.isBlock(token)); } int Library::blockstartoffset(const std::string &file) const { int offset = -1; const std::map::const_iterator map_it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != mExecutableBlocks.end()) { offset = map_it->second.offset(); } return offset; } const std::string& Library::blockstart(const std::string &file) const { const std::map::const_iterator map_it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != mExecutableBlocks.end()) { return map_it->second.start(); } return emptyString; } const std::string& Library::blockend(const std::string &file) const { const std::map::const_iterator map_it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); if (map_it != mExecutableBlocks.end()) { return map_it->second.end(); } return emptyString; } bool Library::iskeyword(const std::string &file, const std::string &keyword) const { const std::map >::const_iterator it = mKeywords.find(Path::getFilenameExtensionInLowerCase(file)); return (it != mKeywords.end() && it->second.count(keyword)); } bool Library::isimporter(const std::string& file, const std::string &importer) const { const std::map >::const_iterator it = mImporters.find(Path::getFilenameExtensionInLowerCase(file)); return (it != mImporters.end() && it->second.count(importer) > 0); } bool Library::isSmartPointer(const Token *tok) const { std::string typestr; while (Token::Match(tok, "%name%|::")) { typestr += tok->str(); tok = tok->next(); } return smartPointers.find(typestr) != smartPointers.end(); } CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok) { if (!tok) return nullptr; // TODO: Support dereferencing iterators // TODO: Support dereferencing with -> if (tok->isUnaryOp("*") && astIsPointer(tok->astOperand1())) { for (const ValueFlow::Value& v:tok->astOperand1()->values()) { if (!v.isLocalLifetimeValue()) continue; if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) continue; return getLibraryContainer(v.tokvalue); } } if (!tok->valueType()) return nullptr; return tok->valueType()->container; } Library::TypeCheck Library::getTypeCheck(const std::string &check, const std::string &typeName) const { auto it = mTypeChecks.find(std::pair(check, typeName)); return it == mTypeChecks.end() ? TypeCheck::def : it->second; } cppcheck-1.90/lib/library.h000066400000000000000000000502241357737443600156340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef libraryH #define libraryH //--------------------------------------------------------------------------- #include "config.h" #include "errorlogger.h" #include "mathlib.h" #include "standards.h" #include #include #include #include #include #include class Token; namespace tinyxml2 { class XMLDocument; class XMLElement; } /// @addtogroup Core /// @{ /** * @brief Library definitions handling */ class CPPCHECKLIB Library { friend class TestSymbolDatabase; // For testing only public: Library(); enum ErrorCode { OK, FILE_NOT_FOUND, BAD_XML, UNKNOWN_ELEMENT, MISSING_ATTRIBUTE, BAD_ATTRIBUTE_VALUE, UNSUPPORTED_FORMAT, DUPLICATE_PLATFORM_TYPE, PLATFORM_TYPE_REDEFINED }; class Error { public: Error() : errorcode(OK) {} explicit Error(ErrorCode e) : errorcode(e) {} template Error(ErrorCode e, T&& r) : errorcode(e), reason(r) {} ErrorCode errorcode; std::string reason; }; Error load(const char exename [], const char path []); Error load(const tinyxml2::XMLDocument &doc); /** this is primarily meant for unit tests. it only returns true/false */ bool loadxmldata(const char xmldata[], std::size_t len); struct AllocFunc { int groupId; int arg; enum class BufferSize {none,malloc,calloc,strdup}; BufferSize bufferSize; int bufferSizeArg1; int bufferSizeArg2; int reallocArg; }; /** get allocation info for function */ const AllocFunc* getAllocFuncInfo(const Token *tok) const; /** get deallocation info for function */ const AllocFunc* getDeallocFuncInfo(const Token *tok) const; /** get reallocation info for function */ const AllocFunc* getReallocFuncInfo(const Token *tok) const; /** get allocation id for function */ int getAllocId(const Token *tok, int arg) const; /** get deallocation id for function */ int getDeallocId(const Token *tok, int arg) const; /** get reallocation id for function */ int getReallocId(const Token *tok, int arg) const; /** get allocation info for function by name (deprecated, use other alloc) */ const AllocFunc* getAllocFuncInfo(const char name[]) const { return getAllocDealloc(mAlloc, name); } /** get deallocation info for function by name (deprecated, use other alloc) */ const AllocFunc* getDeallocFuncInfo(const char name[]) const { return getAllocDealloc(mDealloc, name); } /** get allocation id for function by name (deprecated, use other alloc) */ int allocId(const char name[]) const { const AllocFunc* af = getAllocDealloc(mAlloc, name); return af ? af->groupId : 0; } /** get deallocation id for function by name (deprecated, use other alloc) */ int deallocId(const char name[]) const { const AllocFunc* af = getAllocDealloc(mDealloc, name); return af ? af->groupId : 0; } /** set allocation id for function */ void setalloc(const std::string &functionname, int id, int arg) { mAlloc[functionname].groupId = id; mAlloc[functionname].arg = arg; } void setdealloc(const std::string &functionname, int id, int arg) { mDealloc[functionname].groupId = id; mDealloc[functionname].arg = arg; } void setrealloc(const std::string &functionname, int id, int arg, int reallocArg = 1) { mRealloc[functionname].groupId = id; mRealloc[functionname].arg = arg; mRealloc[functionname].reallocArg = reallocArg; } /** add noreturn function setting */ void setnoreturn(const std::string& funcname, bool noreturn) { mNoReturn[funcname] = noreturn; } /** is allocation type memory? */ static bool ismemory(const int id) { return ((id > 0) && ((id & 1) == 0)); } static bool ismemory(const AllocFunc* const func) { return ((func->groupId > 0) && ((func->groupId & 1) == 0)); } /** is allocation type resource? */ static bool isresource(const int id) { return ((id > 0) && ((id & 1) == 1)); } static bool isresource(const AllocFunc* const func) { return ((func->groupId > 0) && ((func->groupId & 1) == 1)); } bool formatstr_function(const Token* ftok) const; int formatstr_argno(const Token* ftok) const; bool formatstr_scan(const Token* ftok) const; bool formatstr_secure(const Token* ftok) const; struct WarnInfo { std::string message; Standards standards; Severity::SeverityType severity; }; std::map functionwarn; const WarnInfo* getWarnInfo(const Token* ftok) const; // returns true if ftok is not a library function bool isNotLibraryFunction(const Token *ftok) const; bool matchArguments(const Token *ftok, const std::string &functionName) const; bool isUseRetVal(const Token* ftok) const; const std::string& returnValue(const Token *ftok) const; const std::string& returnValueType(const Token *ftok) const; int returnValueContainer(const Token *ftok) const; std::vector unknownReturnValues(const Token *ftok) const; bool isnoreturn(const Token *ftok) const; bool isnotnoreturn(const Token *ftok) const; bool isScopeNoReturn(const Token *end, std::string *unknownFunc) const; class Container { public: Container() : type_templateArgNo(-1), size_templateArgNo(-1), arrayLike_indexOp(false), stdStringLike(false), stdAssociativeLike(false), opLessAllowed(true), hasInitializerListConstructor(false) { } enum class Action { RESIZE, CLEAR, PUSH, POP, FIND, INSERT, ERASE, CHANGE_CONTENT, CHANGE, CHANGE_INTERNAL, NO_ACTION }; enum class Yield { AT_INDEX, ITEM, BUFFER, BUFFER_NT, START_ITERATOR, END_ITERATOR, ITERATOR, SIZE, EMPTY, NO_YIELD }; struct Function { Action action; Yield yield; }; std::string startPattern, startPattern2, endPattern, itEndPattern; std::map functions; int type_templateArgNo; int size_templateArgNo; bool arrayLike_indexOp; bool stdStringLike; bool stdAssociativeLike; bool opLessAllowed; bool hasInitializerListConstructor; Action getAction(const std::string& function) const { const std::map::const_iterator i = functions.find(function); if (i != functions.end()) return i->second.action; return Action::NO_ACTION; } Yield getYield(const std::string& function) const { const std::map::const_iterator i = functions.find(function); if (i != functions.end()) return i->second.yield; return Yield::NO_YIELD; } }; std::map containers; const Container* detectContainer(const Token* typeStart, bool iterator = false) const; class ArgumentChecks { public: ArgumentChecks() : notbool(false), notnull(false), notuninit(-1), formatstr(false), strz(false), optional(false), variadic(false), iteratorInfo(), direction(Direction::DIR_UNKNOWN) { } bool notbool; bool notnull; int notuninit; bool formatstr; bool strz; bool optional; bool variadic; std::string valid; class IteratorInfo { public: IteratorInfo() : container(0), it(false), first(false), last(false) {} int container; bool it; bool first; bool last; }; IteratorInfo iteratorInfo; class MinSize { public: enum Type { NONE, STRLEN, ARGVALUE, SIZEOF, MUL, VALUE }; MinSize(Type t, int a) : type(t), arg(a), arg2(0), value(0) {} Type type; int arg; int arg2; long long value; }; std::vector minsizes; enum class Direction { DIR_IN, ///< Input to called function. Data is treated as read-only. DIR_OUT, ///< Output to caller. Data is passed by reference or address and is potentially written. DIR_INOUT, ///< Input to called function, and output to caller. Data is passed by reference or address and is potentially modified. DIR_UNKNOWN ///< direction not known / specified }; Direction direction; }; struct Function { std::map argumentChecks; // argument nr => argument data bool use; bool leakignore; bool isconst; bool ispure; bool useretval; bool ignore; // ignore functions/macros from a library (gtk, qt etc) bool formatstr; bool formatstr_scan; bool formatstr_secure; Function() : use(false), leakignore(false), isconst(false), ispure(false), useretval(false), ignore(false), formatstr(false), formatstr_scan(false), formatstr_secure(false) {} }; std::map functions; bool isUse(const std::string& functionName) const; bool isLeakIgnore(const std::string& functionName) const; bool isFunctionConst(const std::string& functionName, bool pure) const; bool isFunctionConst(const Token *ftok) const; bool isboolargbad(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg && arg->notbool; } bool isnullargbad(const Token *ftok, int argnr) const; bool isuninitargbad(const Token *ftok, int argnr, int indirect = 0) const; bool isargformatstr(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg && arg->formatstr; } bool isargstrz(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg && arg->strz; } bool isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const; bool isFloatArgValid(const Token *ftok, int argnr, double argvalue) const; const std::string& validarg(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg ? arg->valid : emptyString; } const ArgumentChecks::IteratorInfo *getArgIteratorInfo(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg && arg->iteratorInfo.it ? &arg->iteratorInfo : nullptr; } bool hasminsize(const Token *ftok) const; const std::vector *argminsizes(const Token *ftok, int argnr) const { const ArgumentChecks *arg = getarg(ftok, argnr); return arg ? &arg->minsizes : nullptr; } ArgumentChecks::Direction getArgDirection(const Token* ftok, int argnr) const; bool markupFile(const std::string &path) const; bool processMarkupAfterCode(const std::string &path) const; const std::set &markupExtensions() const { return mMarkupExtensions; } bool reportErrors(const std::string &path) const; bool ignorefunction(const std::string &functionName) const; bool isexecutableblock(const std::string &file, const std::string &token) const; int blockstartoffset(const std::string &file) const; const std::string& blockstart(const std::string &file) const; const std::string& blockend(const std::string &file) const; bool iskeyword(const std::string &file, const std::string &keyword) const; bool isexporter(const std::string &prefix) const { return mExporters.find(prefix) != mExporters.end(); } bool isexportedprefix(const std::string &prefix, const std::string &token) const { const std::map::const_iterator it = mExporters.find(prefix); return (it != mExporters.end() && it->second.isPrefix(token)); } bool isexportedsuffix(const std::string &prefix, const std::string &token) const { const std::map::const_iterator it = mExporters.find(prefix); return (it != mExporters.end() && it->second.isSuffix(token)); } bool isimporter(const std::string& file, const std::string &importer) const; bool isreflection(const std::string &token) const { return mReflection.find(token) != mReflection.end(); } int reflectionArgument(const std::string &token) const { const std::map::const_iterator it = mReflection.find(token); if (it != mReflection.end()) return it->second; return -1; } std::set returnuninitdata; std::vector defines; // to provide some library defines std::set smartPointers; bool isSmartPointer(const Token *tok) const; struct PodType { unsigned int size; char sign; enum { NO, BOOL, CHAR, SHORT, INT, LONG, LONGLONG } stdtype; }; const struct PodType *podtype(const std::string &name) const { const std::map::const_iterator it = mPodTypes.find(name); return (it != mPodTypes.end()) ? &(it->second) : nullptr; } struct PlatformType { PlatformType() : mSigned(false) , mUnsigned(false) , mLong(false) , mPointer(false) , mPtrPtr(false) , mConstPtr(false) { } bool operator == (const PlatformType & type) const { return (mSigned == type.mSigned && mUnsigned == type.mUnsigned && mLong == type.mLong && mPointer == type.mPointer && mPtrPtr == type.mPtrPtr && mConstPtr == type.mConstPtr && mType == type.mType); } bool operator != (const PlatformType & type) const { return !(*this == type); } std::string mType; bool mSigned; bool mUnsigned; bool mLong; bool mPointer; bool mPtrPtr; bool mConstPtr; }; struct Platform { const PlatformType *platform_type(const std::string &name) const { const std::map::const_iterator it = mPlatformTypes.find(name); return (it != mPlatformTypes.end()) ? &(it->second) : nullptr; } std::map mPlatformTypes; }; const PlatformType *platform_type(const std::string &name, const std::string & platform) const { const std::map::const_iterator it = mPlatforms.find(platform); if (it != mPlatforms.end()) { const PlatformType * const type = it->second.platform_type(name); if (type) return type; } const std::map::const_iterator it2 = mPlatformTypes.find(name); return (it2 != mPlatformTypes.end()) ? &(it2->second) : nullptr; } /** * Get function name for function call */ std::string getFunctionName(const Token *ftok) const; static bool isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback=""); /** Suppress/check a type */ enum class TypeCheck { def, check, suppress }; TypeCheck getTypeCheck(const std::string &check, const std::string &typeName) const; private: // load a xml node Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set &unknown_elements); class ExportedFunctions { public: void addPrefix(const std::string& prefix) { mPrefixes.insert(prefix); } void addSuffix(const std::string& suffix) { mSuffixes.insert(suffix); } bool isPrefix(const std::string& prefix) const { return (mPrefixes.find(prefix) != mPrefixes.end()); } bool isSuffix(const std::string& suffix) const { return (mSuffixes.find(suffix) != mSuffixes.end()); } private: std::set mPrefixes; std::set mSuffixes; }; class CodeBlock { public: CodeBlock() : mOffset(0) {} void setStart(const char* s) { mStart = s; } void setEnd(const char* e) { mEnd = e; } void setOffset(const int o) { mOffset = o; } void addBlock(const char* blockName) { mBlocks.insert(blockName); } const std::string& start() const { return mStart; } const std::string& end() const { return mEnd; } int offset() const { return mOffset; } bool isBlock(const std::string& blockName) const { return mBlocks.find(blockName) != mBlocks.end(); } private: std::string mStart; std::string mEnd; int mOffset; std::set mBlocks; }; int mAllocId; std::set mFiles; std::map mAlloc; // allocation functions std::map mDealloc; // deallocation functions std::map mRealloc; // reallocation functions std::map mNoReturn; // is function noreturn? std::map mReturnValue; std::map mReturnValueType; std::map mReturnValueContainer; std::map> mUnknownReturnValues; std::map mReportErrors; std::map mProcessAfterCode; std::set mMarkupExtensions; // file extensions of markup files std::map > mKeywords; // keywords for code in the library std::map mExecutableBlocks; // keywords for blocks of executable code std::map mExporters; // keywords that export variables/functions to libraries (meta-code/macros) std::map > mImporters; // keywords that import variables/functions std::map mReflection; // invocation of reflection std::map mPodTypes; // pod types std::map mPlatformTypes; // platform independent typedefs std::map mPlatforms; // platform dependent typedefs std::map, TypeCheck> mTypeChecks; const ArgumentChecks * getarg(const Token *ftok, int argnr) const; std::string getFunctionName(const Token *ftok, bool *error) const; static const AllocFunc* getAllocDealloc(const std::map &data, const std::string &name) { const std::map::const_iterator it = data.find(name); return (it == data.end()) ? nullptr : &it->second; } }; CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok); /// @} //--------------------------------------------------------------------------- #endif // libraryH cppcheck-1.90/lib/matchcompiler.h000066400000000000000000000036561357737443600170260ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2016 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef matchcompilerH #define matchcompilerH #include namespace MatchCompiler { template class ConstString { public: typedef const char(&StringRef)[n]; explicit ConstString(StringRef s) :_s(s) { } operator StringRef() const { return _s; } private: StringRef _s; }; template inline bool equalN(const char s1[], const char s2[]) { return (*s1 == *s2) && equalN(s1+1, s2+1); } template <> inline bool equalN<0>(const char [], const char []) { return true; } template inline bool operator==(const std::string & s1, ConstString const & s2) { return equalN(s1.c_str(), s2); } template inline bool operator!=(const std::string & s1, ConstString const & s2) { return !operator==(s1,s2); } template inline ConstString makeConstString(const char(&s)[n]) { return ConstString(s); } } #endif // matchcompilerH cppcheck-1.90/lib/mathlib.cpp000066400000000000000000001264561357737443600161560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mathlib.h" #include "errorlogger.h" #include "utils.h" #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER <= 1700 // VS2012 doesn't have std::isinf and std::isnan #define ISINF(x) (!_finite(x)) #define ISNAN(x) (_isnan(x)) #elif defined(__INTEL_COMPILER) #define ISINF(x) (isinf(x)) #define ISNAN(x) (isnan(x)) #else // Use C++11 functions #define ISINF(x) (std::isinf(x)) #define ISNAN(x) (std::isnan(x)) #endif const int MathLib::bigint_bits = 64; MathLib::value::value(const std::string &s) : mIntValue(0), mDoubleValue(0), mIsUnsigned(false) { if (MathLib::isFloat(s)) { mType = MathLib::value::FLOAT; mDoubleValue = MathLib::toDoubleNumber(s); return; } if (!MathLib::isInt(s)) throw InternalError(nullptr, "Invalid value: " + s); mType = MathLib::value::INT; mIntValue = MathLib::toLongNumber(s); if (isIntHex(s) && mIntValue < 0) mIsUnsigned = true; // read suffix if (s.size() >= 2U) { for (std::size_t i = s.size() - 1U; i > 0U; --i) { const char c = s[i]; if (c == 'u' || c == 'U') mIsUnsigned = true; else if (c == 'l' || c == 'L') { if (mType == MathLib::value::INT) mType = MathLib::value::LONG; else if (mType == MathLib::value::LONG) mType = MathLib::value::LONGLONG; } else if (i > 2U && c == '4' && s[i-1] == '6' && s[i-2] == 'i') mType = MathLib::value::LONGLONG; } } } std::string MathLib::value::str() const { std::ostringstream ostr; if (mType == MathLib::value::FLOAT) { if (ISNAN(mDoubleValue)) return "nan.0"; if (ISINF(mDoubleValue)) return (mDoubleValue > 0) ? "inf.0" : "-inf.0"; ostr.precision(9); ostr << std::fixed << mDoubleValue; // remove trailing zeros std::string ret(ostr.str()); std::string::size_type pos = ret.size() - 1U; while (ret[pos] == '0') pos--; if (ret[pos] == '.') ++pos; return ret.substr(0, pos+1); } if (mIsUnsigned) ostr << static_cast(mIntValue) << "U"; else ostr << mIntValue; if (mType == MathLib::value::LONG) ostr << "L"; else if (mType == MathLib::value::LONGLONG) ostr << "LL"; return ostr.str(); } void MathLib::value::promote(const MathLib::value &v) { if (isInt() && v.isInt()) { if (mType < v.mType) { mType = v.mType; mIsUnsigned = v.mIsUnsigned; } else if (mType == v.mType) { mIsUnsigned |= v.mIsUnsigned; } } else if (!isFloat()) { mIsUnsigned = false; mDoubleValue = mIntValue; mType = MathLib::value::FLOAT; } } MathLib::value MathLib::value::calc(char op, const MathLib::value &v1, const MathLib::value &v2) { value temp(v1); temp.promote(v2); if (temp.isFloat()) { switch (op) { case '+': temp.mDoubleValue += v2.getDoubleValue(); break; case '-': temp.mDoubleValue -= v2.getDoubleValue(); break; case '*': temp.mDoubleValue *= v2.getDoubleValue(); break; case '/': temp.mDoubleValue /= v2.getDoubleValue(); break; case '%': case '&': case '|': case '^': throw InternalError(nullptr, "Invalid calculation"); default: throw InternalError(nullptr, "Unhandled calculation"); } } else if (temp.mIsUnsigned) { switch (op) { case '+': temp.mIntValue += (unsigned long long)v2.mIntValue; break; case '-': temp.mIntValue -= (unsigned long long)v2.mIntValue; break; case '*': temp.mIntValue *= (unsigned long long)v2.mIntValue; break; case '/': if (v2.mIntValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); if (v1.mIntValue == std::numeric_limits::min() && std::abs(v2.mIntValue)<=1) throw InternalError(nullptr, "Internal Error: Division overflow"); temp.mIntValue /= (unsigned long long)v2.mIntValue; break; case '%': if (v2.mIntValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); temp.mIntValue %= (unsigned long long)v2.mIntValue; break; case '&': temp.mIntValue &= (unsigned long long)v2.mIntValue; break; case '|': temp.mIntValue |= (unsigned long long)v2.mIntValue; break; case '^': temp.mIntValue ^= (unsigned long long)v2.mIntValue; break; default: throw InternalError(nullptr, "Unhandled calculation"); } } else { switch (op) { case '+': temp.mIntValue += v2.mIntValue; break; case '-': temp.mIntValue -= v2.mIntValue; break; case '*': temp.mIntValue *= v2.mIntValue; break; case '/': if (v2.mIntValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); if (v1.mIntValue == std::numeric_limits::min() && std::abs(v2.mIntValue)<=1) throw InternalError(nullptr, "Internal Error: Division overflow"); temp.mIntValue /= v2.mIntValue; break; case '%': if (v2.mIntValue == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); temp.mIntValue %= v2.mIntValue; break; case '&': temp.mIntValue &= v2.mIntValue; break; case '|': temp.mIntValue |= v2.mIntValue; break; case '^': temp.mIntValue ^= v2.mIntValue; break; default: throw InternalError(nullptr, "Unhandled calculation"); } } return temp; } int MathLib::value::compare(const MathLib::value &v) const { value temp(*this); temp.promote(v); if (temp.isFloat()) { if (temp.mDoubleValue < v.getDoubleValue()) return -1; if (temp.mDoubleValue > v.getDoubleValue()) return 1; return 0; } if (temp.mIsUnsigned) { if ((unsigned long long)mIntValue < (unsigned long long)v.mIntValue) return -1; if ((unsigned long long)mIntValue > (unsigned long long)v.mIntValue) return 1; return 0; } if (mIntValue < v.mIntValue) return -1; if (mIntValue > v.mIntValue) return 1; return 0; } MathLib::value MathLib::value::add(int v) const { MathLib::value temp(*this); if (temp.isInt()) temp.mIntValue += v; else temp.mDoubleValue += v; return temp; } MathLib::value MathLib::value::shiftLeft(const MathLib::value &v) const { if (!isInt() || !v.isInt()) throw InternalError(nullptr, "Shift operand is not integer"); MathLib::value ret(*this); if (v.mIntValue >= MathLib::bigint_bits) { return ret; } ret.mIntValue <<= v.mIntValue; return ret; } MathLib::value MathLib::value::shiftRight(const MathLib::value &v) const { if (!isInt() || !v.isInt()) throw InternalError(nullptr, "Shift operand is not integer"); MathLib::value ret(*this); if (v.mIntValue >= MathLib::bigint_bits) { return ret; } ret.mIntValue >>= v.mIntValue; return ret; } MathLib::biguint MathLib::toULongNumber(const std::string & str) { // hexadecimal numbers: if (isIntHex(str)) { if (str[0] == '-') { biguint ret = 0; std::istringstream istr(str); istr >> std::hex >> ret; return ret; } else { unsigned long long ret = 0; std::istringstream istr(str); istr >> std::hex >> ret; return (biguint)ret; } } // octal numbers: if (isOct(str)) { biguint ret = 0; std::istringstream istr(str); istr >> std::oct >> ret; return ret; } // binary numbers: if (isBin(str)) { biguint ret = 0; for (std::string::size_type i = str[0] == '0'?2:3; i < str.length(); i++) { ret <<= 1; if (str[i] == '1') ret |= 1; } /* if (str[0] == '-') ret = -ret; */ return ret; } if (isFloat(str)) { // Things are going to be less precise now: the value can't b represented in the biguint type. // Use min/max values as an approximation. See #5843 const double doubleval = std::atof(str.c_str()); if (doubleval > (double)std::numeric_limits::max()) return std::numeric_limits::max(); else return static_cast(doubleval); } biguint ret = 0; std::istringstream istr(str); istr >> ret; return ret; } static unsigned int encodeMultiChar(const std::string& str) { unsigned int retval = 0; for (char it : str) { retval = (retval << 8) | it; } return retval; } static bool isoctal(int c) { return c>='0' && c<='7'; } MathLib::bigint MathLib::characterLiteralToLongNumber(const std::string& str) { if (str.empty()) return 0; // <- only possible in unit testing // '\xF6' if (str.size() == 4 && str.compare(0,2,"\\x")==0 && std::isxdigit(str[2]) && std::isxdigit(str[3])) { return std::strtoul(str.substr(2).c_str(), nullptr, 16); } // '\123' if (str.size() == 4 && str[0] == '\\' && isoctal(str[1]) && isoctal(str[2]) && isoctal(str[3])) { return (char)std::strtoul(str.substr(1).c_str(), nullptr, 8); } // C99 6.4.4.4 // The value of an integer character constant containing more than one character (e.g., 'ab'), // or containing a character or escape sequence that does not map to a single-byte execution character, // is implementation-defined. // clang and gcc seem to use the following encoding: 'AB' as (('A' << 8) | 'B') const std::string& normStr = normalizeCharacterLiteral(str); return encodeMultiChar(normStr); } std::string MathLib::normalizeCharacterLiteral(const std::string& iLiteral) { std::string normalizedLiteral; const std::string::size_type iLiteralLen = iLiteral.size(); for (std::string::size_type idx = 0; idx < iLiteralLen ; ++idx) { if (iLiteral[idx] != '\\') { normalizedLiteral.push_back(iLiteral[idx]); continue; } ++idx; if (idx == iLiteralLen) { throw InternalError(nullptr, "Internal Error. MathLib::normalizeCharacterLiteral: Unhandled char constant '" + iLiteral + "'."); } switch (iLiteral[idx]) { case 'x': // Hexa-decimal number: skip \x and interpret the next two characters { if (++idx == iLiteralLen) throw InternalError(nullptr, "Internal Error. MathLib::normalizeCharacterLiteral: Unhandled char constant '" + iLiteral + "'."); std::string tempBuf; tempBuf.push_back(iLiteral[idx]); if (++idx != iLiteralLen) tempBuf.push_back(iLiteral[idx]); normalizedLiteral.push_back(static_cast(MathLib::toULongNumber("0x" + tempBuf))); continue; } case 'u': case 'U': // Unicode string; just skip the \u or \U if (idx + 1 == iLiteralLen) throw InternalError(nullptr, "Internal Error. MathLib::characterLiteralToLongNumber: Unhandled char constant '" + iLiteral + "'."); continue; } // Single digit octal number if (1 == iLiteralLen - idx) { switch (iLiteral[idx]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': normalizedLiteral.push_back(iLiteral[idx]-'0'); break; case 'a': normalizedLiteral.push_back('\a'); break; case 'b': normalizedLiteral.push_back('\b'); break; case 'e': normalizedLiteral.push_back(0x1B); // clang, gcc, tcc interpnormalizedLiteral this as 0x1B - escape character break; case 'f': normalizedLiteral.push_back('\f'); break; case 'n': normalizedLiteral.push_back('\n'); break; case 'r': normalizedLiteral.push_back('\r'); break; case 't': normalizedLiteral.push_back('\t'); break; case 'v': normalizedLiteral.push_back('\v'); break; case '\\': case '\?': case '\'': case '\"': normalizedLiteral.push_back(iLiteral[idx]); break; default: throw InternalError(nullptr, "Internal Error. MathLib::normalizeCharacterLiteral: Unhandled char constant '" + iLiteral + "'."); } continue; } // 2-3 digit octal number if (!MathLib::isOctalDigit(iLiteral[idx])) throw InternalError(nullptr, "Internal Error. MathLib::normalizeCharacterLiteral: Unhandled char constant '" + iLiteral + "'."); std::string tempBuf; tempBuf.push_back(iLiteral[idx]); ++idx; if (MathLib::isOctalDigit(iLiteral[idx])) { tempBuf.push_back(iLiteral[idx]); ++idx; if (MathLib::isOctalDigit(iLiteral[idx])) { tempBuf.push_back(iLiteral[idx]); } } normalizedLiteral.push_back(static_cast(MathLib::toLongNumber("0" + tempBuf))); } return normalizedLiteral; } MathLib::bigint MathLib::toLongNumber(const std::string & str) { // hexadecimal numbers: if (isIntHex(str)) { if (str[0] == '-') { bigint ret = 0; std::istringstream istr(str); istr >> std::hex >> ret; return ret; } else { unsigned long long ret = 0; std::istringstream istr(str); istr >> std::hex >> ret; return (bigint)ret; } } // octal numbers: if (isOct(str)) { bigint ret = 0; std::istringstream istr(str); istr >> std::oct >> ret; return ret; } // binary numbers: if (isBin(str)) { bigint ret = 0; for (std::string::size_type i = str[0] == '0'?2:3; i < str.length(); i++) { ret <<= 1; if (str[i] == '1') ret |= 1; } if (str[0] == '-') ret = -ret; return ret; } if (isFloat(str)) { // Things are going to be less precise now: the value can't be represented in the bigint type. // Use min/max values as an approximation. See #5843 const double doubleval = toDoubleNumber(str); if (doubleval > (double)std::numeric_limits::max()) return std::numeric_limits::max(); else if (doubleval < (double)std::numeric_limits::min()) return std::numeric_limits::min(); else return static_cast(doubleval); } if (isCharLiteral(str)) { return characterLiteralToLongNumber(getCharLiteral(str)); } if (str[0] == '-') { bigint ret = 0; std::istringstream istr(str); istr >> ret; return ret; } biguint ret = 0; std::istringstream istr(str); istr >> ret; return ret; } // in-place conversion of (sub)string to double. Requires no heap. static double myStod(const std::string& str, std::string::const_iterator from, std::string::const_iterator to, int base) { double result = 0.; bool positivesign = true; std::string::const_iterator it; if ('+' == *from) { it = from + 1; } else if ('-' == *from) { it = from + 1; positivesign = false; } else it = from; const std::size_t decimalsep = str.find('.', it-str.begin()); int distance; if (std::string::npos == decimalsep) { distance = to - it; } else if (decimalsep > (to - str.begin())) return 0.; // error handling?? else distance = int(decimalsep)-(from - str.begin()); auto digitval = [&](char c) { if ((10 < base) && (c > '9')) return 10 + std::tolower(c) - 'a'; else return c - '0'; }; for (; it!=to; ++it) { if ('.' == *it) continue; else --distance; result += digitval(*it)* std::pow(base, distance); } return (positivesign)?result:-result; } // Assuming a limited support of built-in hexadecimal floats (see C99, C++17) that is a fall-back implementation. // Performance has been optimized WRT to heap activity, however the calculation part is not optimized. static double floatHexToDoubleNumber(const std::string& str) { const std::size_t p = str.find_first_of("pP",3); const double factor1 = myStod(str, str.begin() + 2, str.begin()+p, 16); const bool suffix = (str.back() == 'f') || (str.back() == 'F') || (str.back() == 'l') || (str.back() == 'L'); const double exponent = myStod(str, str.begin() + p + 1, (suffix)?str.end()-1:str.end(), 10); const double factor2 = std::pow(2, exponent); return factor1 * factor2; } double MathLib::toDoubleNumber(const std::string &str) { if (isCharLiteral(str)) return characterLiteralToLongNumber(getCharLiteral(str)); if (isIntHex(str)) return static_cast(toLongNumber(str)); // nullcheck if (isNullValue(str)) return 0.0; #ifdef __clang__ if (isFloat(str)) // Workaround libc++ bug at http://llvm.org/bugs/show_bug.cgi?id=17782 // TODO : handle locale return std::strtod(str.c_str(), nullptr); #endif if (isFloatHex(str)) return floatHexToDoubleNumber(str); // otherwise, convert to double std::istringstream istr(str); istr.imbue(std::locale::classic()); double ret; istr >> ret; return ret; } template<> std::string MathLib::toString(double value) { std::ostringstream result; result.precision(12); result << value; if (result.str() == "-0") return "0.0"; if (result.str().find('.') == std::string::npos) return result.str() + ".0"; return result.str(); } bool MathLib::isFloat(const std::string &str) { return isDecimalFloat(str) || isFloatHex(str); } bool MathLib::isDecimalFloat(const std::string &str) { if (str.empty()) return false; enum class State { START, BASE_DIGITS1, LEADING_DECIMAL, TRAILING_DECIMAL, BASE_DIGITS2, E, MANTISSA_PLUSMINUS, MANTISSA_DIGITS, SUFFIX_F, SUFFIX_L } state = State::START; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case State::START: if (*it=='.') state = State::LEADING_DECIMAL; else if (std::isdigit(static_cast(*it))) state = State::BASE_DIGITS1; else return false; break; case State::LEADING_DECIMAL: if (std::isdigit(static_cast(*it))) state = State::BASE_DIGITS2; else return false; break; case State::BASE_DIGITS1: if (*it=='e' || *it=='E') state = State::E; else if (*it=='.') state = State::TRAILING_DECIMAL; else if (!std::isdigit(static_cast(*it))) return false; break; case State::TRAILING_DECIMAL: if (*it=='e' || *it=='E') state = State::E; else if (*it=='f' || *it=='F') state = State::SUFFIX_F; else if (*it=='l' || *it=='L') state = State::SUFFIX_L; else if (std::isdigit(static_cast(*it))) state = State::BASE_DIGITS2; else return false; break; case State::BASE_DIGITS2: if (*it=='e' || *it=='E') state = State::E; else if (*it=='f' || *it=='F') state = State::SUFFIX_F; else if (*it=='l' || *it=='L') state = State::SUFFIX_L; else if (!std::isdigit(static_cast(*it))) return false; break; case State::E: if (*it=='+' || *it=='-') state = State::MANTISSA_PLUSMINUS; else if (std::isdigit(static_cast(*it))) state = State::MANTISSA_DIGITS; else return false; break; case State::MANTISSA_PLUSMINUS: if (!std::isdigit(static_cast(*it))) return false; else state = State::MANTISSA_DIGITS; break; case State::MANTISSA_DIGITS: if (*it=='f' || *it=='F') state = State::SUFFIX_F; else if (*it=='l' || *it=='L') state = State::SUFFIX_L; else if (!std::isdigit(static_cast(*it))) return false; break; case State::SUFFIX_F: return false; case State::SUFFIX_L: return false; } } return (state==State::BASE_DIGITS2 || state==State::MANTISSA_DIGITS || state==State::TRAILING_DECIMAL || state==State::SUFFIX_F || state==State::SUFFIX_L); } bool MathLib::isNegative(const std::string &str) { if (str.empty()) return false; return (str[0] == '-'); } bool MathLib::isPositive(const std::string &str) { if (str.empty()) return false; return !MathLib::isNegative(str); } static bool isValidIntegerSuffixIt(std::string::const_iterator it, std::string::const_iterator end, bool supportMicrosoftExtensions=true) { enum { START, SUFFIX_U, SUFFIX_UL, SUFFIX_ULL, SUFFIX_L, SUFFIX_LU, SUFFIX_LL, SUFFIX_LLU, SUFFIX_I, SUFFIX_I6, SUFFIX_I64, SUFFIX_UI, SUFFIX_UI6, SUFFIX_UI64 } state = START; for (; it != end; ++it) { switch (state) { case START: if (*it == 'u' || *it == 'U') state = SUFFIX_U; else if (*it == 'l' || *it == 'L') state = SUFFIX_L; else if (supportMicrosoftExtensions && (*it == 'i' || *it == 'I')) state = SUFFIX_I; else return false; break; case SUFFIX_U: if (*it == 'l' || *it == 'L') state = SUFFIX_UL; // UL else if (supportMicrosoftExtensions && (*it == 'i' || *it == 'I')) state = SUFFIX_UI; else return false; break; case SUFFIX_UL: if (*it == 'l' || *it == 'L') state = SUFFIX_ULL; // ULL else return false; break; case SUFFIX_L: if (*it == 'u' || *it == 'U') state = SUFFIX_LU; // LU else if (*it == 'l' || *it == 'L') state = SUFFIX_LL; // LL else return false; break; case SUFFIX_LU: return false; case SUFFIX_LL: if (*it == 'u' || *it == 'U') state = SUFFIX_LLU; // LLU else return false; break; case SUFFIX_I: if (*it == '6') state = SUFFIX_I6; else return false; break; case SUFFIX_I6: if (*it == '4') state = SUFFIX_I64; else return false; break; case SUFFIX_UI: if (*it == '6') state = SUFFIX_UI6; else return false; break; case SUFFIX_UI6: if (*it == '4') state = SUFFIX_UI64; else return false; break; default: return false; } } return ((state == SUFFIX_U) || (state == SUFFIX_L) || (state == SUFFIX_UL) || (state == SUFFIX_LU) || (state == SUFFIX_LL) || (state == SUFFIX_ULL) || (state == SUFFIX_LLU) || (state == SUFFIX_I64) || (state == SUFFIX_UI64)); } bool MathLib::isValidIntegerSuffix(const std::string& str, bool supportMicrosoftExtensions) { return isValidIntegerSuffixIt(str.begin(), str.end(), supportMicrosoftExtensions); } /*! \brief Does the string represent an octal number? * In case leading or trailing white space is provided, the function * returns false. * Additional information can be found here: * http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html * * \param str The string to check. In case the string is empty, the function returns false. * \return Return true in case a octal number is provided and false otherwise. **/ bool MathLib::isOct(const std::string& str) { enum class Status { START, OCTAL_PREFIX, DIGITS } state = Status::START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case Status::START: if (*it == '0') state = Status::OCTAL_PREFIX; else return false; break; case Status::OCTAL_PREFIX: if (isOctalDigit(static_cast(*it))) state = Status::DIGITS; else return false; break; case Status::DIGITS: if (isOctalDigit(static_cast(*it))) state = Status::DIGITS; else return isValidIntegerSuffixIt(it,str.end()); break; } } return state == Status::DIGITS; } bool MathLib::isIntHex(const std::string& str) { enum Status { START, HEX_0, HEX_X, DIGIT } state = START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case START: if (*it == '0') state = HEX_0; else return false; break; case HEX_0: if (*it == 'x' || *it == 'X') state = HEX_X; else return false; break; case HEX_X: if (isxdigit(static_cast(*it))) state = DIGIT; else return false; break; case DIGIT: if (isxdigit(static_cast(*it))) ; // state = DIGIT; else return isValidIntegerSuffixIt(it,str.end()); break; } } return DIGIT==state; } bool MathLib::isFloatHex(const std::string& str) { enum Status { START, HEX_0, HEX_X, WHOLE_NUMBER_DIGIT, POINT, FRACTION, EXPONENT_P, EXPONENT_SIGN, EXPONENT_DIGITS, EXPONENT_SUFFIX } state = START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case START: if (*it == '0') state = HEX_0; else return false; break; case HEX_0: if (*it == 'x' || *it == 'X') state = HEX_X; else return false; break; case HEX_X: if (isxdigit(static_cast(*it))) state = WHOLE_NUMBER_DIGIT; else if (*it == '.') state = POINT; else return false; break; case WHOLE_NUMBER_DIGIT: if (isxdigit(static_cast(*it))) ; // state = WHOLE_NUMBER_DIGITS; else if (*it=='.') state = FRACTION; else if (*it=='p' || *it=='P') state = EXPONENT_P; else return false; break; case POINT: case FRACTION: if (isxdigit(static_cast(*it))) state=FRACTION; else if (*it == 'p' || *it == 'P') state = EXPONENT_P; else return false; break; case EXPONENT_P: if (isdigit(static_cast(*it))) state = EXPONENT_DIGITS; else if (*it == '+' || *it == '-') state = EXPONENT_SIGN; else return false; break; case EXPONENT_SIGN: if (isdigit(static_cast(*it))) state = EXPONENT_DIGITS; else return false; break; case EXPONENT_DIGITS: if (isdigit(static_cast(*it))) ; // state = EXPONENT_DIGITS; else if (*it == 'f' || *it == 'F' || *it == 'l' || *it == 'L') state = EXPONENT_SUFFIX; else return false; break; case EXPONENT_SUFFIX: return false; } } return (EXPONENT_DIGITS==state) || (EXPONENT_SUFFIX == state); } /*! \brief Does the string represent a binary number? * In case leading or trailing white space is provided, the function * returns false. * Additional information can be found here: * http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html * * \param str The string to check. In case the string is empty, the function returns false. * \return Return true in case a binary number is provided and false otherwise. **/ bool MathLib::isBin(const std::string& str) { enum Status { START, GNU_BIN_PREFIX_0, GNU_BIN_PREFIX_B, DIGIT } state = START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case START: if (*it == '0') state = GNU_BIN_PREFIX_0; else return false; break; case GNU_BIN_PREFIX_0: if (*it == 'b' || *it == 'B') state = GNU_BIN_PREFIX_B; else return false; break; case GNU_BIN_PREFIX_B: if (*it == '0' || *it == '1') state = DIGIT; else return false; break; case DIGIT: if (*it == '0' || *it == '1') ; // state = DIGIT; else return isValidIntegerSuffixIt(it,str.end()); break; } } return state == DIGIT; } bool MathLib::isDec(const std::string & str) { enum Status { START, DIGIT } state = START; if (str.empty()) return false; std::string::const_iterator it = str.begin(); if ('+' == *it || '-' == *it) ++it; for (; it != str.end(); ++it) { switch (state) { case START: if (isdigit(static_cast(*it))) state = DIGIT; else return false; break; case DIGIT: if (isdigit(static_cast(*it))) state = DIGIT; else return isValidIntegerSuffixIt(it,str.end()); break; } } return state == DIGIT; } bool MathLib::isInt(const std::string & str) { return isDec(str) || isIntHex(str) || isOct(str) || isBin(str); } std::string MathLib::getSuffix(const std::string& value) { if (value.size() > 3 && value[value.size() - 3] == 'i' && value[value.size() - 2] == '6' && value[value.size() - 1] == '4') { if (value[value.size() - 4] == 'u') return "ULL"; return "LL"; } bool isUnsigned = false; unsigned int longState = 0; for (std::size_t i = 1U; i < value.size(); ++i) { const char c = value[value.size() - i]; if (c == 'u' || c == 'U') isUnsigned = true; else if (c == 'L' || c == 'l') longState++; else break; } if (longState == 0) return isUnsigned ? "U" : ""; if (longState == 1) return isUnsigned ? "UL" : "L"; if (longState == 2) return isUnsigned ? "ULL" : "LL"; else return ""; } static std::string intsuffix(const std::string & first, const std::string & second) { const std::string suffix1 = MathLib::getSuffix(first); const std::string suffix2 = MathLib::getSuffix(second); if (suffix1 == "ULL" || suffix2 == "ULL") return "ULL"; if (suffix1 == "LL" || suffix2 == "LL") return "LL"; if (suffix1 == "UL" || suffix2 == "UL") return "UL"; if (suffix1 == "L" || suffix2 == "L") return "L"; if (suffix1 == "U" || suffix2 == "U") return "U"; return suffix1.empty() ? suffix2 : suffix1; } std::string MathLib::add(const std::string & first, const std::string & second) { #ifdef TEST_MATHLIB_VALUE return (value(first) + value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) + toLongNumber(second)) + intsuffix(first, second); } double d1 = toDoubleNumber(first); double d2 = toDoubleNumber(second); int count = 0; while (d1 > 100000.0 * d2 && toString(d1+d2)==first && ++count<5) d2 *= 10.0; while (d2 > 100000.0 * d1 && toString(d1+d2)==second && ++count<5) d1 *= 10.0; return toString(d1 + d2); #endif } std::string MathLib::subtract(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE return (value(first) - value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) - toLongNumber(second)) + intsuffix(first, second); } if (first == second) return "0.0" ; double d1 = toDoubleNumber(first); double d2 = toDoubleNumber(second); int count = 0; while (d1 > 100000.0 * d2 && toString(d1-d2)==first && ++count<5) d2 *= 10.0; while (d2 > 100000.0 * d1 && toString(d1-d2)==second && ++count<5) d1 *= 10.0; return toString(d1 - d2); #endif } std::string MathLib::incdec(const std::string & var, const std::string & op) { #ifdef TEST_MATHLIB_VALUE if (op == "++") return value(var).add(1).str(); else if (op == "--") return value(var).add(-1).str(); #else if (op == "++") return MathLib::add(var, "1"); else if (op == "--") return MathLib::subtract(var, "1"); #endif throw InternalError(nullptr, std::string("Unexpected operation '") + op + "' in MathLib::incdec(). Please report this to Cppcheck developers."); } std::string MathLib::divide(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE return (value(first) / value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { const bigint a = toLongNumber(first); const bigint b = toLongNumber(second); if (b == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); if (a == std::numeric_limits::min() && std::abs(b)<=1) throw InternalError(nullptr, "Internal Error: Division overflow"); return toString(toLongNumber(first) / b) + intsuffix(first, second); } else if (isNullValue(second)) { if (isNullValue(first)) return "nan.0"; return isPositive(first) ? "inf.0" : "-inf.0"; } return toString(toDoubleNumber(first) / toDoubleNumber(second)); #endif } std::string MathLib::multiply(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE return (value(first) * value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { return toString(toLongNumber(first) * toLongNumber(second)) + intsuffix(first, second); } return toString(toDoubleNumber(first) * toDoubleNumber(second)); #endif } std::string MathLib::mod(const std::string &first, const std::string &second) { #ifdef TEST_MATHLIB_VALUE return (value(first) % value(second)).str(); #else if (MathLib::isInt(first) && MathLib::isInt(second)) { const bigint b = toLongNumber(second); if (b == 0) throw InternalError(nullptr, "Internal Error: Division by zero"); return toString(toLongNumber(first) % b) + intsuffix(first, second); } return toString(std::fmod(toDoubleNumber(first),toDoubleNumber(second))); #endif } std::string MathLib::calculate(const std::string &first, const std::string &second, char action) { switch (action) { case '+': return MathLib::add(first, second); case '-': return MathLib::subtract(first, second); case '*': return MathLib::multiply(first, second); case '/': return MathLib::divide(first, second); case '%': return MathLib::mod(first, second); case '&': return MathLib::toString(MathLib::toLongNumber(first) & MathLib::toLongNumber(second)) + intsuffix(first,second); case '|': return MathLib::toString(MathLib::toLongNumber(first) | MathLib::toLongNumber(second)) + intsuffix(first,second); case '^': return MathLib::toString(MathLib::toLongNumber(first) ^ MathLib::toLongNumber(second)) + intsuffix(first,second); default: throw InternalError(nullptr, std::string("Unexpected action '") + action + "' in MathLib::calculate(). Please report this to Cppcheck developers."); } } std::string MathLib::sin(const std::string &tok) { return toString(std::sin(toDoubleNumber(tok))); } std::string MathLib::cos(const std::string &tok) { return toString(std::cos(toDoubleNumber(tok))); } std::string MathLib::tan(const std::string &tok) { return toString(std::tan(toDoubleNumber(tok))); } std::string MathLib::abs(const std::string &tok) { return toString(std::abs(toDoubleNumber(tok))); } bool MathLib::isEqual(const std::string &first, const std::string &second) { // this conversion is needed for formatting // e.g. if first=0.1 and second=1.0E-1, the direct comparison of the strings would fail return toString(toDoubleNumber(first)) == toString(toDoubleNumber(second)); } bool MathLib::isNotEqual(const std::string &first, const std::string &second) { return !isEqual(first, second); } bool MathLib::isGreater(const std::string &first, const std::string &second) { return toDoubleNumber(first) > toDoubleNumber(second); } bool MathLib::isGreaterEqual(const std::string &first, const std::string &second) { return toDoubleNumber(first) >= toDoubleNumber(second); } bool MathLib::isLess(const std::string &first, const std::string &second) { return toDoubleNumber(first) < toDoubleNumber(second); } bool MathLib::isLessEqual(const std::string &first, const std::string &second) { return toDoubleNumber(first) <= toDoubleNumber(second); } /*! \brief Does the string represent the numerical value of 0? * In case leading or trailing white space is provided, the function * returns false. * Requirement for this function: * - This code is allowed to be slow because of simplicity of the code. * * \param[in] str The string to check. In case the string is empty, the function returns false. * \return Return true in case the string represents a numerical null value. **/ bool MathLib::isNullValue(const std::string &str) { if (str.empty() || (!std::isdigit(static_cast(str[0])) && (str[0] != '.' && str[0] != '-' && str[0] != '+'))) return false; // Has to be a number for (char i : str) { if (std::isdigit(static_cast(i)) && i != '0') // May not contain digits other than 0 return false; if (i == 'E' || i == 'e') return true; } return true; } bool MathLib::isOctalDigit(char c) { return (c >= '0' && c <= '7'); } bool MathLib::isDigitSeparator(const std::string& iCode, std::string::size_type iPos) { if (iPos == 0 || iPos >= iCode.size() || iCode[iPos] != '\'') return false; std::string::size_type i = iPos - 1; while (std::isxdigit(iCode[i])) { if (i == 0) return true; // Only xdigits before ' --i; } if (i == iPos - 1) { // No xdigit before ' return false; } else { switch (iCode[i]) { case ' ': case '.': case ',': case 'x': case '(': case '{': case '+': case '-': case '*': case '%': case '/': case '&': case '|': case '^': case '~': case '=': return true; case '\'': return isDigitSeparator(iCode, i); default: return false; } } } MathLib::value operator+(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('+',v1,v2); } MathLib::value operator-(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('-',v1,v2); } MathLib::value operator*(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('*',v1,v2); } MathLib::value operator/(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('/',v1,v2); } MathLib::value operator%(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('%',v1,v2); } MathLib::value operator&(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('&',v1,v2); } MathLib::value operator|(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('|',v1,v2); } MathLib::value operator^(const MathLib::value &v1, const MathLib::value &v2) { return MathLib::value::calc('^',v1,v2); } MathLib::value operator<<(const MathLib::value &v1, const MathLib::value &v2) { return v1.shiftLeft(v2); } MathLib::value operator>>(const MathLib::value &v1, const MathLib::value &v2) { return v1.shiftRight(v2); } cppcheck-1.90/lib/mathlib.h000066400000000000000000000147601357737443600156150ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef mathlibH #define mathlibH //--------------------------------------------------------------------------- #include "config.h" #include #include /// @addtogroup Core /// @{ /** @brief simple math functions that uses operands stored in std::string. useful when performing math on tokens. */ class CPPCHECKLIB MathLib { friend class TestMathLib; public: /** @brief value class */ class value { private: long long mIntValue; double mDoubleValue; enum { INT, LONG, LONGLONG, FLOAT } mType; bool mIsUnsigned; void promote(const value &v); public: explicit value(const std::string &s); std::string str() const; bool isInt() const { return mType != FLOAT; } bool isFloat() const { return mType == FLOAT; } double getDoubleValue() const { return isFloat() ? mDoubleValue : (double)mIntValue; } static value calc(char op, const value &v1, const value &v2); int compare(const value &v) const; value add(int v) const; value shiftLeft(const value &v) const; value shiftRight(const value &v) const; }; typedef long long bigint; typedef unsigned long long biguint; static const int bigint_bits; static bigint toLongNumber(const std::string & str); static biguint toULongNumber(const std::string & str); template static std::string toString(T value) { std::ostringstream result; result << value; return result.str(); } static double toDoubleNumber(const std::string & str); static bool isInt(const std::string & str); static bool isFloat(const std::string &str); static bool isDecimalFloat(const std::string &str); static bool isNegative(const std::string &str); static bool isPositive(const std::string &str); static bool isDec(const std::string & str); static bool isFloatHex(const std::string& str); static bool isIntHex(const std::string& str); static bool isOct(const std::string& str); static bool isBin(const std::string& str); static std::string getSuffix(const std::string& value); /** * \param[in] str string * \param[in] supportMicrosoftExtensions support Microsoft extension: i64 * \return true if str is a non-empty valid integer suffix */ static bool isValidIntegerSuffix(const std::string& str, bool supportMicrosoftExtensions=true); static std::string add(const std::string & first, const std::string & second); static std::string subtract(const std::string & first, const std::string & second); static std::string multiply(const std::string & first, const std::string & second); static std::string divide(const std::string & first, const std::string & second); static std::string mod(const std::string & first, const std::string & second); static std::string incdec(const std::string & var, const std::string & op); static std::string calculate(const std::string & first, const std::string & second, char action); static std::string sin(const std::string & tok); static std::string cos(const std::string & tok); static std::string tan(const std::string & tok); static std::string abs(const std::string & tok); static bool isEqual(const std::string & first, const std::string & second); static bool isNotEqual(const std::string & first, const std::string & second); static bool isGreater(const std::string & first, const std::string & second); static bool isGreaterEqual(const std::string & first, const std::string & second); static bool isLess(const std::string & first, const std::string & second); static bool isLessEqual(const std::string & first, const std::string & second); static bool isNullValue(const std::string & str); /** * Return true if given character is 0,1,2,3,4,5,6 or 7. * @param[in] c The character to check * @return true if given character is octal digit. */ static bool isOctalDigit(char c); /** * \param[in] str character literal * @return Number of internal representation of the character literal * */ static MathLib::bigint characterLiteralToLongNumber(const std::string& str); /** * \param[in] iCode Code being considered * \param[in] iPos A posision within iCode * \return Whether iCode[iPos] is a C++14 digit separator */ static bool isDigitSeparator(const std::string& iCode, std::string::size_type iPos); private: /* * \param iLiteral A character literal * \return The equivalent character literal with all escapes interpreted */ static std::string normalizeCharacterLiteral(const std::string& iLiteral); }; MathLib::value operator+(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator-(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator*(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator/(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator%(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator&(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator|(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator^(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator<<(const MathLib::value &v1, const MathLib::value &v2); MathLib::value operator>>(const MathLib::value &v1, const MathLib::value &v2); template<> CPPCHECKLIB std::string MathLib::toString(double value); // Declare specialization to avoid linker problems /// @} //--------------------------------------------------------------------------- #endif // mathlibH cppcheck-1.90/lib/path.cpp000066400000000000000000000155411357737443600154620ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #if defined(__GNUC__) && (defined(_WIN32) || defined(__CYGWIN__)) #undef __STRICT_ANSI__ #endif #include "path.h" #include "utils.h" #include #include #include #include #include #include #ifndef _WIN32 #include #else #include #endif #if defined(__CYGWIN__) #include #endif #include /** Is the filesystem case insensitive? */ static bool caseInsensitiveFilesystem() { #ifdef _WIN32 return true; #else // TODO: Non-windows filesystems might be case insensitive return false; #endif } std::string Path::toNativeSeparators(std::string path) { #if defined(_WIN32) const char separ = '/'; const char native = '\\'; #else const char separ = '\\'; const char native = '/'; #endif std::replace(path.begin(), path.end(), separ, native); return path; } std::string Path::fromNativeSeparators(std::string path) { const char nonnative = '\\'; const char newsepar = '/'; std::replace(path.begin(), path.end(), nonnative, newsepar); return path; } std::string Path::simplifyPath(std::string originalPath) { return simplecpp::simplifyPath(originalPath); } std::string Path::getPathFromFilename(const std::string &filename) { const std::size_t pos = filename.find_last_of("\\/"); if (pos != std::string::npos) return filename.substr(0, 1 + pos); return ""; } bool Path::sameFileName(const std::string &fname1, const std::string &fname2) { return caseInsensitiveFilesystem() ? (caseInsensitiveStringCompare(fname1, fname2) == 0) : (fname1 == fname2); } // This wrapper exists because Sun's CC does not allow a static_cast // from extern "C" int(*)(int) to int(*)(int). static int tolowerWrapper(int c) { return std::tolower(c); } std::string Path::removeQuotationMarks(std::string path) { path.erase(std::remove(path.begin(), path.end(), '\"'), path.end()); return path; } std::string Path::getFilenameExtension(const std::string &path) { const std::string::size_type dotLocation = path.find_last_of('.'); if (dotLocation == std::string::npos) return ""; std::string extension = path.substr(dotLocation); if (caseInsensitiveFilesystem()) { // on a case insensitive filesystem the case doesn't matter so // let's return the extension in lowercase std::transform(extension.begin(), extension.end(), extension.begin(), tolowerWrapper); } return extension; } std::string Path::getFilenameExtensionInLowerCase(const std::string &path) { std::string extension = getFilenameExtension(path); std::transform(extension.begin(), extension.end(), extension.begin(), tolowerWrapper); return extension; } std::string Path::getCurrentPath() { char currentPath[4096]; #ifndef _WIN32 if (getcwd(currentPath, 4096) != nullptr) #else if (_getcwd(currentPath, 4096) != nullptr) #endif return std::string(currentPath); return emptyString; } bool Path::isAbsolute(const std::string& path) { const std::string& nativePath = toNativeSeparators(path); #ifdef _WIN32 if (path.length() < 2) return false; // On Windows, 'C:\foo\bar' is an absolute path, while 'C:foo\bar' is not if (nativePath.compare(0, 2, "\\\\") == 0 || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0)) return true; #else if (!nativePath.empty() && nativePath[0] == '/') return true; #endif return false; } std::string Path::getRelativePath(const std::string& absolutePath, const std::vector& basePaths) { for (const std::string &bp : basePaths) { if (absolutePath == bp || bp.empty()) // Seems to be a file, or path is empty continue; if (absolutePath.compare(0, bp.length(), bp) != 0) continue; if (endsWith(bp,'/')) return absolutePath.substr(bp.length()); else if (absolutePath.size() > bp.size() && absolutePath[bp.length()] == '/') return absolutePath.substr(bp.length() + 1); } return absolutePath; } bool Path::isC(const std::string &path) { // In unix, ".C" is considered C++ file const std::string extension = getFilenameExtension(path); return extension == ".c" || extension == ".cl"; } bool Path::isCPP(const std::string &path) { const std::string extension = getFilenameExtensionInLowerCase(path); return extension == ".cpp" || extension == ".cxx" || extension == ".cc" || extension == ".c++" || extension == ".hpp" || extension == ".hxx" || extension == ".hh" || extension == ".tpp" || extension == ".txx" || getFilenameExtension(path) == ".C"; // In unix, ".C" is considered C++ file } bool Path::acceptFile(const std::string &path, const std::set &extra) { return !Path::isHeader(path) && (Path::isCPP(path) || Path::isC(path) || extra.find(getFilenameExtension(path)) != extra.end()); } bool Path::isHeader(const std::string &path) { const std::string extension = getFilenameExtensionInLowerCase(path); return (extension.compare(0, 2, ".h") == 0); } std::string Path::getAbsoluteFilePath(const std::string& filePath) { std::string absolute_path; #ifdef _WIN32 char absolute[_MAX_PATH]; if (_fullpath(absolute, filePath.c_str(), _MAX_PATH)) absolute_path = absolute; #elif defined(__linux__) || defined(__sun) || defined(__hpux) || defined(__GNUC__) || defined(__CPPCHECK__) char * absolute = realpath(filePath.c_str(), nullptr); if (absolute) absolute_path = absolute; free(absolute); #else #error Platform absolute path function needed #endif return absolute_path; } std::string Path::stripDirectoryPart(const std::string &file) { #if defined(_WIN32) && !defined(__MINGW32__) const char native = '\\'; #else const char native = '/'; #endif const std::string::size_type p = file.rfind(native); if (p != std::string::npos) { return file.substr(p + 1); } return file; } bool Path::fileExists(const std::string &file) { std::ifstream f(file.c_str()); return f.is_open(); } cppcheck-1.90/lib/path.h000066400000000000000000000147101357737443600151240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef pathH #define pathH //--------------------------------------------------------------------------- #include "config.h" #include #include #include /// @addtogroup Core /// @{ /** * @brief Path handling routines. * Internally cppcheck wants to store paths with / separator which is also * native separator for Unix-derived systems. When giving path to user * or for other functions we convert path separators back to native type. */ class CPPCHECKLIB Path { public: /** * Convert path to use native separators. * @param path Path string to convert. * @return converted path. */ static std::string toNativeSeparators(std::string path); /** * Convert path to use internal path separators. * @param path Path string to convert. * @return converted path. */ static std::string fromNativeSeparators(std::string path); /** * @brief Simplify path "foo/bar/.." => "foo" * @param originalPath path to be simplified, must have / -separators. * @return simplified path */ static std::string simplifyPath(std::string originalPath); /** * @brief Lookup the path part from a filename (e.g., '/tmp/a.h' -> '/tmp/', 'a.h' -> '') * @param filename filename to lookup, must have / -separators. * @return path part of the filename */ static std::string getPathFromFilename(const std::string &filename); /** * @brief Compare filenames to see if they are the same. * On Linux the comparison is case-sensitive. On Windows it is case-insensitive. * @param fname1 one filename * @param fname2 other filename * @return true if the filenames match on the current platform */ static bool sameFileName(const std::string &fname1, const std::string &fname2); /** * @brief Remove quotation marks (") from the path. * @param path path to be cleaned. * @return Cleaned path without quotation marks. */ static std::string removeQuotationMarks(std::string path); /** * @brief Get an extension of the filename. * @param path Path containing filename. * @return Filename extension (containing the dot, e.g. ".h" or ".CPP"). */ static std::string getFilenameExtension(const std::string &path); /** * @brief Get an extension of the filename in lower case. * @param path Path containing filename. * @return Filename extension (containing the dot, e.g. ".h"). */ static std::string getFilenameExtensionInLowerCase(const std::string &path); /** * @brief Returns the absolute path of current working directory * @return absolute path of current working directory */ static std::string getCurrentPath(); /** * @brief Check if given path is absolute * @param path Path to check * @return true if given path is absolute */ static bool isAbsolute(const std::string& path); /** * @brief Create a relative path from an absolute one, if absolute path is inside the basePaths. * @param absolutePath Path to be made relative. * @param basePaths Paths to which it may be made relative. * @return relative path, if possible. Otherwise absolutePath is returned unchanged */ static std::string getRelativePath(const std::string& absolutePath, const std::vector& basePaths); /** * @brief Get an absolute file path from a relative one. * @param filePath File path to be made absolute. * @return absolute path, if possible. Otherwise an empty path is returned */ static std::string getAbsoluteFilePath(const std::string& filePath); /** * @brief Check if the file extension indicates that it's a C/C++ source file. * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx * @param filename filename to check. path info is optional * @return true if the file extension indicates it should be checked */ static bool acceptFile(const std::string &filename) { const std::set extra; return acceptFile(filename, extra); } /** * @brief Check if the file extension indicates that it's a C/C++ source file. * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx * @param path filename to check. path info is optional * @param extra extra file extensions * @return true if the file extension indicates it should be checked */ static bool acceptFile(const std::string &path, const std::set &extra); /** * @brief Identify language based on file extension. * @param path filename to check. path info is optional * @return true if extension is meant for C files */ static bool isC(const std::string &path); /** * @brief Identify language based on file extension. * @param path filename to check. path info is optional * @return true if extension is meant for C++ files */ static bool isCPP(const std::string &path); /** * @brief Is filename a header based on file extension * @param path filename to check. path info is optional * @return true if filename extension is meant for headers */ static bool isHeader(const std::string &path); /** * @brief Get filename without a directory path part. * @param file filename to be stripped. path info is optional * @return filename without directory path part. */ static std::string stripDirectoryPart(const std::string &file); static bool fileExists(const std::string &file); }; /// @} //--------------------------------------------------------------------------- #endif // pathH cppcheck-1.90/lib/pathanalysis.cpp000066400000000000000000000150611357737443600172230ustar00rootroot00000000000000#include "pathanalysis.h" #include "library.h" #include "mathlib.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" const Scope* PathAnalysis::findOuterScope(const Scope * scope) { if (!scope) return nullptr; if (scope->isLocal() && scope->type != Scope::eSwitch) return findOuterScope(scope->nestedIn); return scope; } static const Token* getCondTok(const Token* tok) { if (!tok) return nullptr; if (Token::simpleMatch(tok, "(")) return getCondTok(tok->previous()); if (Token::simpleMatch(tok, "for") && Token::simpleMatch(tok->next()->astOperand2(), ";") && tok->next()->astOperand2()->astOperand2()) return tok->next()->astOperand2()->astOperand2()->astOperand1(); if (Token::simpleMatch(tok->next()->astOperand2(), ";")) return tok->next()->astOperand2()->astOperand1(); return tok->next()->astOperand2(); } std::pair PathAnalysis::checkCond(const Token * tok, bool& known) { if (tok->hasKnownIntValue()) { known = true; return std::make_pair(tok->values().front().intvalue, !tok->values().front().intvalue); } auto it = std::find_if(tok->values().begin(), tok->values().end(), [](const ValueFlow::Value& v) { return v.isIntValue(); }); // If all possible values are the same, then assume all paths have the same value if (it != tok->values().end() && std::all_of(it, tok->values().end(), [&](const ValueFlow::Value& v) { if (v.isIntValue()) return v.intvalue == it->intvalue; return true; })) { known = false; return std::make_pair(it->intvalue, !it->intvalue); } return std::make_pair(true, true); } PathAnalysis::Progress PathAnalysis::forwardRecursive(const Token* tok, Info info, const std::function& f) const { if (!tok) return Progress::Continue; if (tok->astOperand1() && forwardRecursive(tok->astOperand1(), info, f) == Progress::Break) return Progress::Break; info.tok = tok; if (f(info) == Progress::Break) return Progress::Break; if (tok->astOperand2() && forwardRecursive(tok->astOperand2(), info, f) == Progress::Break) return Progress::Break; return Progress::Continue; } PathAnalysis::Progress PathAnalysis::forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const { for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { if (Token::Match(tok, "asm|goto|break|continue")) return Progress::Break; if (Token::Match(tok, "return|throw")) { forwardRecursive(tok, info, f); return Progress::Break; } if (Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), ") {") && Token::Match(tok->link()->linkAt(-1)->previous(), "if|while|for (")) { const Token * blockStart = tok->link()->linkAt(-1)->previous(); const Token * condTok = getCondTok(blockStart); if (!condTok) continue; info.errorPath.emplace_back(condTok, "Assuming condition is true."); // Traverse a loop a second time if (Token::Match(blockStart, "for|while (")) { const Token* endCond = blockStart->linkAt(1); bool traverseLoop = true; // Only traverse simple for loops if (Token::simpleMatch(blockStart, "for") && !Token::Match(endCond->tokAt(-3), "; ++|--|%var% %var%|++|-- ) {")) traverseLoop = false; // Traverse loop a second time if (traverseLoop) { // Traverse condition if (forwardRecursive(condTok, info, f) == Progress::Break) return Progress::Break; // TODO: Should we traverse the body: forwardRange(tok->link(), tok, info, f)? } } } if (Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) { const Token * endCond = tok->next()->link(); const Token * endBlock = endCond->next()->link(); const Token * condTok = getCondTok(tok); if (!condTok) continue; // Traverse condition if (forwardRange(tok->next(), tok->next()->link(), info, f) == Progress::Break) return Progress::Break; Info i = info; i.known = false; i.errorPath.emplace_back(condTok, "Assuming condition is true."); // Check if condition is true or false bool checkThen = false; bool checkElse = false; std::tie(checkThen, checkElse) = checkCond(condTok, i.known); // Traverse then block if (checkThen) { if (forwardRange(endCond->next(), endBlock, i, f) == Progress::Break) return Progress::Break; } // Traverse else block if (Token::simpleMatch(endBlock, "} else {")) { if (checkElse) { i.errorPath.back().second = "Assuming condition is false."; Progress result = forwardRange(endCond->next(), endBlock, i, f); if (result == Progress::Break) return Progress::Break; } tok = endBlock->linkAt(2); } else { tok = endBlock; } } else if (Token::simpleMatch(tok, "} else {")) { tok = tok->linkAt(2); } else { info.tok = tok; if (f(info) == Progress::Break) return Progress::Break; } // Prevent infinite recursion if (tok->next() == start) break; } return Progress::Continue; } void PathAnalysis::forward(const std::function& f) const { const Scope * endScope = findOuterScope(start->scope()); if (!endScope) return; const Token * endToken = endScope->bodyEnd; Info info{start, ErrorPath{}, true}; forwardRange(start, endToken, info, f); } bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath) { PathAnalysis::Info info = PathAnalysis{start, library} .forwardFind([&](const PathAnalysis::Info& i) { return (i.tok == dest); }); if (!info.tok) return false; if (errorPath) errorPath->insert(errorPath->end(), info.errorPath.begin(), info.errorPath.end()); return true; } cppcheck-1.90/lib/pathanalysis.h000066400000000000000000000034701357737443600166710ustar00rootroot00000000000000#ifndef GUARD_PATHANALYSIS_H #define GUARD_PATHANALYSIS_H #include #include "errorlogger.h" #include "utils.h" class Library; class Settings; class Scope; class Token; struct PathAnalysis { enum class Progress { Continue, Break }; PathAnalysis(const Token* start, const Library& library) : start(start), library(&library) {} const Token * start; const Library * library; struct Info { const Token* tok; ErrorPath errorPath; bool known; }; void forward(const std::function& f) const; template void forwardAll(F f) { forward([&](const Info& info) { f(info); return Progress::Continue; }); } template Info forwardFind(Predicate pred) { Info result{}; forward([&](const Info& info) { if (pred(info)) { result = info; return Progress::Break; } return Progress::Continue; }); return result; } private: Progress forwardRecursive(const Token* tok, Info info, const std::function& f) const; Progress forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const; static const Scope* findOuterScope(const Scope * scope); static std::pair checkCond(const Token * tok, bool& known); }; /** * @brief Returns true if there is a path between the two tokens * * @param start Starting point of the path * @param dest The path destination * @param errorPath Adds the path traversal to the errorPath */ bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath); #endif cppcheck-1.90/lib/pathmatch.cpp000066400000000000000000000062661357737443600165030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "pathmatch.h" #include "path.h" #include "utils.h" #include #include #include PathMatch::PathMatch(const std::vector &excludedPaths, bool caseSensitive) : mExcludedPaths(excludedPaths), mCaseSensitive(caseSensitive) { if (!mCaseSensitive) for (std::vector::iterator i = mExcludedPaths.begin(); i != mExcludedPaths.end(); ++i) std::transform(i->begin(), i->end(), i->begin(), ::tolower); mWorkingDirectory.push_back(Path::getCurrentPath()); } bool PathMatch::match(const std::string &path) const { if (path.empty()) return false; for (std::vector::const_iterator i = mExcludedPaths.begin(); i != mExcludedPaths.end(); ++i) { const std::string excludedPath((!Path::isAbsolute(path) && Path::isAbsolute(*i)) ? Path::getRelativePath(*i, mWorkingDirectory) : *i); std::string findpath = Path::fromNativeSeparators(path); if (!mCaseSensitive) std::transform(findpath.begin(), findpath.end(), findpath.begin(), ::tolower); // Filtering directory name if (endsWith(excludedPath,'/')) { if (!endsWith(findpath,'/')) findpath = removeFilename(findpath); if (excludedPath.length() > findpath.length()) continue; // Match relative paths starting with mask // -isrc matches src/foo.cpp if (findpath.compare(0, excludedPath.size(), excludedPath) == 0) return true; // Match only full directory name in middle or end of the path // -isrc matches myproject/src/ but does not match // myproject/srcfiles/ or myproject/mysrc/ if (findpath.find("/" + excludedPath) != std::string::npos) return true; } // Filtering filename else { if (excludedPath.length() > findpath.length()) continue; // Check if path ends with mask // -ifoo.cpp matches (./)foo.c, src/foo.cpp and proj/src/foo.cpp // -isrc/file.cpp matches src/foo.cpp and proj/src/foo.cpp if (findpath.compare(findpath.size() - excludedPath.size(), findpath.size(), excludedPath) == 0) return true; } } return false; } std::string PathMatch::removeFilename(const std::string &path) { const std::size_t ind = path.find_last_of('/'); return path.substr(0, ind + 1); } cppcheck-1.90/lib/pathmatch.h000066400000000000000000000034771357737443600161510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef PATHMATCH_H #define PATHMATCH_H #include "config.h" #include #include /// @addtogroup CLI /// @{ /** * @brief Simple path matching for ignoring paths in CLI. */ class CPPCHECKLIB PathMatch { public: /** * The constructor. * @param excludedPaths List of masks. * @param caseSensitive Match the case of the characters when * matching paths? */ explicit PathMatch(const std::vector &excludedPaths, bool caseSensitive = true); /** * @brief Match path against list of masks. * @param path Path to match. * @return true if any of the masks match the path, false otherwise. */ bool match(const std::string &path) const; protected: /** * @brief Remove filename part from the path. * @param path Path to edit. * @return path without filename part. */ static std::string removeFilename(const std::string &path); private: std::vector mExcludedPaths; bool mCaseSensitive; std::vector mWorkingDirectory; }; /// @} #endif // PATHMATCH_H cppcheck-1.90/lib/pcrerules.pri000066400000000000000000000007241357737443600165370ustar00rootroot00000000000000# If HAVE_RULES=yes is passed to qmake, use PCRE and enable rules contains(HAVE_RULES, [yY][eE][sS]) { CONFIG += use_pcre_rules } use_pcre_rules { DEFINES += HAVE_RULES LIBS += -L../externals -lpcre INCLUDEPATH += ../externals message("Rules enabled - to disable them and remove the dependency on PCRE, pass HAVE_RULES=no to qmake.") } else { message("Rules disabled - to enable them, make PCRE available and pass HAVE_RULES=yes to qmake.") } cppcheck-1.90/lib/platform.cpp000066400000000000000000000204501357737443600163450ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "platform.h" #include "path.h" #include "tinyxml2.h" #include #include #include #include cppcheck::Platform::Platform() { // This assumes the code you are checking is for the same architecture this is compiled on. #if defined(_WIN64) platform(Win64); #elif defined(_WIN32) platform(Win32A); #else platform(Native); #endif } bool cppcheck::Platform::platform(cppcheck::Platform::PlatformType type) { switch (type) { case Unspecified: // unknown type sizes (sizes etc are set but are not known) case Native: // same as system this code was compile on platformType = type; sizeof_bool = sizeof(bool); sizeof_short = sizeof(short); sizeof_int = sizeof(int); sizeof_long = sizeof(long); sizeof_long_long = sizeof(long long); sizeof_float = sizeof(float); sizeof_double = sizeof(double); sizeof_long_double = sizeof(long double); sizeof_wchar_t = sizeof(wchar_t); sizeof_size_t = sizeof(std::size_t); sizeof_pointer = sizeof(void *); if (type == Unspecified) { defaultSign = '\0'; } else { defaultSign = (std::numeric_limits::is_signed) ? 's' : 'u'; } char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case Win32W: case Win32A: platformType = type; sizeof_bool = 1; // 4 in Visual C++ 4.2 sizeof_short = 2; sizeof_int = 4; sizeof_long = 4; sizeof_long_long = 8; sizeof_float = 4; sizeof_double = 8; sizeof_long_double = 8; sizeof_wchar_t = 2; sizeof_size_t = 4; sizeof_pointer = 4; defaultSign = '\0'; char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case Win64: platformType = type; sizeof_bool = 1; sizeof_short = 2; sizeof_int = 4; sizeof_long = 4; sizeof_long_long = 8; sizeof_float = 4; sizeof_double = 8; sizeof_long_double = 8; sizeof_wchar_t = 2; sizeof_size_t = 8; sizeof_pointer = 8; defaultSign = '\0'; char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case Unix32: platformType = type; sizeof_bool = 1; sizeof_short = 2; sizeof_int = 4; sizeof_long = 4; sizeof_long_long = 8; sizeof_float = 4; sizeof_double = 8; sizeof_long_double = 12; sizeof_wchar_t = 4; sizeof_size_t = 4; sizeof_pointer = 4; defaultSign = '\0'; char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case Unix64: platformType = type; sizeof_bool = 1; sizeof_short = 2; sizeof_int = 4; sizeof_long = 8; sizeof_long_long = 8; sizeof_float = 4; sizeof_double = 8; sizeof_long_double = 16; sizeof_wchar_t = 4; sizeof_size_t = 8; sizeof_pointer = 8; defaultSign = '\0'; char_bit = 8; short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; return true; case PlatformFile: // sizes are not set. return false; } // unsupported platform return false; } bool cppcheck::Platform::loadPlatformFile(const char exename[], const std::string &filename) { // open file.. tinyxml2::XMLDocument doc; if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) { std::vector filenames; filenames.push_back(filename + ".xml"); if (exename && (std::string::npos != Path::fromNativeSeparators(exename).find('/'))) { filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + "platforms/" + filename); filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + "platforms/" + filename + ".xml"); } #ifdef FILESDIR std::string filesdir = FILESDIR; if (!filesdir.empty() && filesdir[filesdir.size()-1] != '/') filesdir += '/'; filenames.push_back(filesdir + ("platforms/" + filename)); filenames.push_back(filesdir + ("platforms/" + filename + ".xml")); #endif bool success = false; for (const std::string & f : filenames) { if (doc.LoadFile(f.c_str()) == tinyxml2::XML_SUCCESS) { success = true; break; } } if (!success) return false; } return loadFromXmlDocument(&doc); } bool cppcheck::Platform::loadFromXmlDocument(const tinyxml2::XMLDocument *doc) { const tinyxml2::XMLElement * const rootnode = doc->FirstChildElement(); if (!rootnode || std::strcmp(rootnode->Name(), "platform") != 0) return false; for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "default-sign") == 0) defaultSign = *node->GetText(); else if (std::strcmp(node->Name(), "char_bit") == 0) char_bit = std::atoi(node->GetText()); else if (std::strcmp(node->Name(), "sizeof") == 0) { for (const tinyxml2::XMLElement *sz = node->FirstChildElement(); sz; sz = sz->NextSiblingElement()) { if (std::strcmp(sz->Name(), "short") == 0) sizeof_short = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "bool") == 0) sizeof_bool = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "int") == 0) sizeof_int = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "long") == 0) sizeof_long = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "long-long") == 0) sizeof_long_long = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "float") == 0) sizeof_float = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "double") == 0) sizeof_double = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "long-double") == 0) sizeof_long_double = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "pointer") == 0) sizeof_pointer = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "size_t") == 0) sizeof_size_t = std::atoi(sz->GetText()); else if (std::strcmp(sz->Name(), "wchar_t") == 0) sizeof_wchar_t = std::atoi(sz->GetText()); } } } short_bit = char_bit * sizeof_short; int_bit = char_bit * sizeof_int; long_bit = char_bit * sizeof_long; long_long_bit = char_bit * sizeof_long_long; platformType = PlatformFile; return true; } cppcheck-1.90/lib/platform.h000066400000000000000000000115351357737443600160160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef platformH #define platformH //--------------------------------------------------------------------------- #include "config.h" #include "utils.h" #include #include /// @addtogroup Core /// @{ namespace tinyxml2 { class XMLDocument; } namespace cppcheck { /** * @brief Platform settings */ class CPPCHECKLIB Platform { private: static long long min_value(int bit) { if (bit >= 64) return LLONG_MIN; return -(1LL << (bit-1)); } static long long max_value(int bit) { if (bit >= 64) return (~0ULL) >> 1; return (1LL << (bit-1)) - 1LL; } public: Platform(); virtual ~Platform() {} bool isIntValue(long long value) const { return value >= min_value(int_bit) && value <= max_value(int_bit); } bool isLongValue(long long value) const { return value >= min_value(long_bit) && value <= max_value(long_bit); } nonneg int char_bit; /// bits in char nonneg int short_bit; /// bits in short nonneg int int_bit; /// bits in int nonneg int long_bit; /// bits in long nonneg int long_long_bit; /// bits in long long /** size of standard types */ nonneg int sizeof_bool; nonneg int sizeof_short; nonneg int sizeof_int; nonneg int sizeof_long; nonneg int sizeof_long_long; nonneg int sizeof_float; nonneg int sizeof_double; nonneg int sizeof_long_double; nonneg int sizeof_wchar_t; nonneg int sizeof_size_t; nonneg int sizeof_pointer; char defaultSign; // unsigned:'u', signed:'s', unknown:'\0' enum PlatformType { Unspecified, // No platform specified Native, // whatever system this code was compiled on Win32A, Win32W, Win64, Unix32, Unix64, PlatformFile }; /** platform type */ PlatformType platformType; /** set the platform type for predefined platforms */ bool platform(PlatformType type); /** * load platform file * @param exename application path * @param filename platform filename * @return returns true if file was loaded successfully */ bool loadPlatformFile(const char exename[], const std::string &filename); /** load platform from xml document, primarily for testing */ bool loadFromXmlDocument(const tinyxml2::XMLDocument *doc); /** * @brief Returns true if platform type is Windows * @return true if Windows platform type. */ bool isWindowsPlatform() const { return platformType == Win32A || platformType == Win32W || platformType == Win64; } const char *platformString() const { return platformString(platformType); } static const char *platformString(PlatformType pt) { switch (pt) { case Unspecified: return "Unspecified"; case Native: return "Native"; case Win32A: return "win32A"; case Win32W: return "win32W"; case Win64: return "win64"; case Unix32: return "unix32"; case Unix64: return "unix64"; case PlatformFile: return "platformFile"; default: return "unknown"; } } long long unsignedCharMax() const { return max_value(char_bit + 1); } long long signedCharMax() const { return max_value(char_bit); } long long signedCharMin() const { return min_value(char_bit); } }; } /// @} //--------------------------------------------------------------------------- #endif // platformH cppcheck-1.90/lib/preprocessor.cpp000066400000000000000000001134261357737443600172550ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "preprocessor.h" #include "errorlogger.h" #include "library.h" #include "path.h" #include "settings.h" #include "simplecpp.h" #include "suppressions.h" #include #include #include #include // back_inserter #include /** * Remove heading and trailing whitespaces from the input parameter. * If string is all spaces/tabs, return empty string. * @param s The string to trim. */ static std::string trim(const std::string& s) { const std::string::size_type beg = s.find_first_not_of(" \t"); if (beg == std::string::npos) return ""; const std::string::size_type end = s.find_last_not_of(" \t"); return s.substr(beg, end - beg + 1); } Directive::Directive(const std::string &_file, const int _linenr, const std::string &_str): file(_file), linenr(_linenr), str(trim(_str)) { } std::atomic Preprocessor::missingIncludeFlag; std::atomic Preprocessor::missingSystemIncludeFlag; char Preprocessor::macroChar = char(1); Preprocessor::Preprocessor(Settings& settings, ErrorLogger *errorLogger) : mSettings(settings), mErrorLogger(errorLogger) { } Preprocessor::~Preprocessor() { for (std::map::iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) delete it->second; } namespace { struct BadInlineSuppression { BadInlineSuppression(const simplecpp::Location &l, const std::string &msg) : location(l), errmsg(msg) {} simplecpp::Location location; std::string errmsg; }; } static void inlineSuppressions(const simplecpp::TokenList &tokens, Settings &mSettings, std::list *bad) { std::list inlineSuppressions; for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { if (tok->comment) { Suppressions::Suppression s; std::string errmsg; if (!s.parseComment(tok->str(), &errmsg)) continue; if (!errmsg.empty()) bad->push_back(BadInlineSuppression(tok->location, errmsg)); if (!s.errorId.empty()) inlineSuppressions.push_back(s); continue; } if (inlineSuppressions.empty()) continue; // Relative filename std::string relativeFilename(tok->location.file()); if (mSettings.relativePaths) { for (const std::string & basePath : mSettings.basePaths) { const std::string bp = basePath + "/"; if (relativeFilename.compare(0,bp.size(),bp)==0) { relativeFilename = relativeFilename.substr(bp.size()); } } } relativeFilename = Path::simplifyPath(relativeFilename); // Add the suppressions. for (Suppressions::Suppression &suppr : inlineSuppressions) { suppr.fileName = relativeFilename; suppr.lineNumber = tok->location.line; mSettings.nomsg.addSuppression(suppr); } inlineSuppressions.clear(); } } void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens) { if (!mSettings.inlineSuppressions) return; std::list err; ::inlineSuppressions(tokens, mSettings, &err); for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { if (it->second) ::inlineSuppressions(*it->second, mSettings, &err); } for (const BadInlineSuppression &bad : err) { error(bad.location.file(), bad.location.line, bad.errmsg); } } void Preprocessor::setDirectives(const simplecpp::TokenList &tokens) { // directive list.. mDirectives.clear(); std::vector list; list.reserve(1U + mTokenLists.size()); list.push_back(&tokens); for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { list.push_back(it->second); } for (const simplecpp::TokenList *tokenList : list) { for (const simplecpp::Token *tok = tokenList->cfront(); tok; tok = tok->next) { if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line)) continue; if (tok->next && tok->next->str() == "endfile") continue; Directive directive(tok->location.file(), tok->location.line, emptyString); for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) { if (tok2->comment) continue; if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str().size())) directive.str += ' '; if (directive.str == "#" && tok2->str() == "file") directive.str += "include"; else directive.str += tok2->str(); } mDirectives.push_back(directive); } } } static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) { return tok1 && tok2 && tok1->location.sameline(tok2->location); } static std::string readcondition(const simplecpp::Token *iftok, const std::set &defined, const std::set &undefined) { const simplecpp::Token *cond = iftok->next; if (!sameline(iftok,cond)) return ""; const simplecpp::Token *next1 = cond->next; const simplecpp::Token *next2 = next1 ? next1->next : nullptr; const simplecpp::Token *next3 = next2 ? next2->next : nullptr; unsigned int len = 1; if (sameline(iftok,next1)) len = 2; if (sameline(iftok,next2)) len = 3; if (sameline(iftok,next3)) len = 4; if (len == 1 && cond->str() == "0") return "0"; if (len == 1 && cond->name) { if (defined.find(cond->str()) == defined.end()) return cond->str(); } if (len == 2 && cond->op == '!' && next1->name) { if (defined.find(next1->str()) == defined.end()) return next1->str() + "=0"; } if (len == 3 && cond->op == '(' && next1->name && next2->op == ')') { if (defined.find(next1->str()) == defined.end() && undefined.find(next1->str()) == undefined.end()) return next1->str(); } if (len == 3 && cond->name && next1->str() == "==" && next2->number) { if (defined.find(cond->str()) == defined.end()) return cond->str() + '=' + cond->next->next->str(); } std::set configset; for (; sameline(iftok,cond); cond = cond->next) { if (cond->op == '!') { if (!sameline(iftok,cond->next) || !cond->next->name) break; if (cond->next->str() == "defined") continue; configset.insert(cond->next->str() + "=0"); continue; } if (cond->str() != "defined") continue; const simplecpp::Token *dtok = cond->next; if (!dtok) break; if (dtok->op == '(') dtok = dtok->next; if (sameline(iftok,dtok) && dtok->name && defined.find(dtok->str()) == defined.end() && undefined.find(dtok->str()) == undefined.end()) configset.insert(dtok->str()); } std::string cfg; for (const std::string &s : configset) { if (!cfg.empty()) cfg += ';'; cfg += s; } return cfg; } static bool hasDefine(const std::string &userDefines, const std::string &cfg) { if (cfg.empty()) { return false; } std::string::size_type pos = 0; while (pos < userDefines.size()) { pos = userDefines.find(cfg, pos); if (pos == std::string::npos) break; const std::string::size_type pos2 = pos + cfg.size(); if ((pos == 0 || userDefines[pos-1U] == ';') && (pos2 == userDefines.size() || userDefines[pos2] == '=')) return true; pos = pos2; } return false; } static std::string cfg(const std::vector &configs, const std::string &userDefines) { std::set configs2(configs.begin(), configs.end()); std::string ret; for (const std::string &cfg : configs2) { if (cfg.empty()) continue; if (cfg == "0") return ""; if (hasDefine(userDefines, cfg)) continue; if (!ret.empty()) ret += ';'; ret += cfg; } return ret; } static bool isUndefined(const std::string &cfg, const std::set &undefined) { for (std::string::size_type pos1 = 0U; pos1 < cfg.size();) { const std::string::size_type pos2 = cfg.find(';',pos1); const std::string def = (pos2 == std::string::npos) ? cfg.substr(pos1) : cfg.substr(pos1, pos2 - pos1); const std::string::size_type eq = def.find('='); if (eq == std::string::npos && undefined.find(def) != undefined.end()) return true; if (eq != std::string::npos && undefined.find(def.substr(0,eq)) != undefined.end() && def.substr(eq) != "=0") return true; pos1 = (pos2 == std::string::npos) ? pos2 : pos2 + 1U; } return false; } static bool getConfigsElseIsFalse(const std::vector &configs_if, const std::string &userDefines) { return std::any_of(configs_if.cbegin(), configs_if.cend(), [=](const std::string &cfg) { return hasDefine(userDefines, cfg); }); } static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok) { int level = 0; while (nullptr != (cmdtok = cmdtok->next)) { if (cmdtok->op == '#' && !sameline(cmdtok->previous,cmdtok) && sameline(cmdtok, cmdtok->next)) { if (cmdtok->next->str().compare(0,2,"if")==0) ++level; else if (cmdtok->next->str() == "endif") { --level; if (level < 0) return cmdtok; } } } return nullptr; } static void getConfigs(const simplecpp::TokenList &tokens, std::set &defined, const std::string &userDefines, const std::set &undefined, std::set &ret) { std::vector configs_if; std::vector configs_ifndef; std::string elseError; for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { if (tok->op != '#' || sameline(tok->previous, tok)) continue; const simplecpp::Token *cmdtok = tok->next; if (!sameline(tok, cmdtok)) continue; if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef" || cmdtok->str() == "if") { std::string config; if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef") { const simplecpp::Token *expr1 = cmdtok->next; if (sameline(tok,expr1) && expr1->name && !sameline(tok,expr1->next)) config = expr1->str(); if (defined.find(config) != defined.end()) config.clear(); } else if (cmdtok->str() == "if") { config = readcondition(cmdtok, defined, undefined); } // skip undefined configurations.. if (isUndefined(config, undefined)) config.clear(); configs_if.push_back((cmdtok->str() == "ifndef") ? std::string() : config); configs_ifndef.push_back((cmdtok->str() == "ifndef") ? config : std::string()); ret.insert(cfg(configs_if,userDefines)); } else if (cmdtok->str() == "elif" || cmdtok->str() == "else") { if (getConfigsElseIsFalse(configs_if,userDefines)) { tok = gotoEndIf(tok); if (!tok) break; tok = tok->previous; continue; } if (cmdtok->str() == "else" && cmdtok->next && !sameline(cmdtok,cmdtok->next) && sameline(cmdtok->next, cmdtok->next->next) && cmdtok->next->op == '#' && cmdtok->next->next->str() == "error") { const std::string &ifcfg = cfg(configs_if, userDefines); if (!ifcfg.empty()) { if (!elseError.empty()) elseError += ';'; elseError += ifcfg; } } if (!configs_if.empty()) configs_if.pop_back(); if (cmdtok->str() == "elif") { std::string config = readcondition(cmdtok, defined, undefined); if (isUndefined(config,undefined)) config.clear(); configs_if.push_back(config); ret.insert(cfg(configs_if, userDefines)); } else if (!configs_ifndef.empty()) { configs_if.push_back(configs_ifndef.back()); ret.insert(cfg(configs_if, userDefines)); } } else if (cmdtok->str() == "endif" && !sameline(tok, cmdtok->next)) { if (!configs_if.empty()) configs_if.pop_back(); if (!configs_ifndef.empty()) configs_ifndef.pop_back(); } else if (cmdtok->str() == "error") { if (!configs_ifndef.empty() && !configs_ifndef.back().empty()) { if (configs_ifndef.size() == 1U) ret.erase(""); std::vector configs(configs_if); configs.push_back(configs_ifndef.back()); ret.erase(cfg(configs, userDefines)); if (!elseError.empty()) elseError += ';'; elseError += cfg(configs_ifndef, userDefines); } if (!configs_if.empty() && !configs_if.back().empty()) { const std::string &last = configs_if.back(); if (last.size() > 2U && last.compare(last.size()-2U,2,"=0") == 0) { std::vector configs(configs_if); ret.erase(cfg(configs, userDefines)); configs[configs.size() - 1U] = last.substr(0,last.size()-2U); if (configs.size() == 1U) ret.erase(""); if (!elseError.empty()) elseError += ';'; elseError += cfg(configs, userDefines); } } } else if (cmdtok->str() == "define" && sameline(tok, cmdtok->next) && cmdtok->next->name) { defined.insert(cmdtok->next->str()); } } if (!elseError.empty()) ret.insert(elseError); } std::set Preprocessor::getConfigs(const simplecpp::TokenList &tokens) const { std::set ret = { "" }; if (!tokens.cfront()) return ret; std::set defined = { "__cplusplus" }; ::getConfigs(tokens, defined, mSettings.userDefines, mSettings.userUndefs, ret); for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { if (!mSettings.configurationExcluded(it->first)) ::getConfigs(*(it->second), defined, mSettings.userDefines, mSettings.userUndefs, ret); } return ret; } void Preprocessor::preprocess(std::istream &istr, std::map &result, const std::string &filename, const std::list &includePaths) { (void)includePaths; simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1(istr, files, filename, &outputList); const std::set configs = getConfigs(tokens1); for (const std::string &cfg : configs) { if (mSettings.userUndefs.find(cfg) == mSettings.userUndefs.end()) { result[cfg] = getcode(tokens1, cfg, files, false); } } } std::string Preprocessor::removeSpaceNearNL(const std::string &str) { std::string tmp; char prev = '\n'; // treat start of file as newline for (std::size_t i = 0; i < str.size(); i++) { if (str[i] == ' ' && (prev == '\n' || i + 1 >= str.size() || // treat end of file as newline str[i+1] == '\n' ) ) { // Ignore space that has new line in either side of it } else { tmp.append(1, str[i]); prev = str[i]; } } return tmp; } void Preprocessor::preprocessWhitespaces(std::string &processedFile) { // Replace all tabs with spaces.. std::replace(processedFile.begin(), processedFile.end(), '\t', ' '); // Remove space characters that are after or before new line character processedFile = removeSpaceNearNL(processedFile); } void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list &resultConfigurations, const std::string &filename, const std::list &includePaths) { (void)includePaths; if (mFile0.empty()) mFile0 = filename; simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1(srcCodeStream, files, filename, &outputList); const std::set configs = getConfigs(tokens1); std::copy(configs.cbegin(), configs.cend(), std::back_inserter(resultConfigurations)); processedFile = tokens1.stringify(); } static void splitcfg(const std::string &cfg, std::list &defines, const std::string &defaultValue) { for (std::string::size_type defineStartPos = 0U; defineStartPos < cfg.size();) { const std::string::size_type defineEndPos = cfg.find(';', defineStartPos); std::string def = (defineEndPos == std::string::npos) ? cfg.substr(defineStartPos) : cfg.substr(defineStartPos, defineEndPos - defineStartPos); if (!defaultValue.empty() && def.find('=') == std::string::npos) def += '=' + defaultValue; defines.push_back(def); if (defineEndPos == std::string::npos) break; defineStartPos = defineEndPos + 1U; } } static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename) { simplecpp::DUI dui; splitcfg(mSettings.userDefines, dui.defines, "1"); if (!cfg.empty()) splitcfg(cfg, dui.defines, emptyString); for (const std::string &def : mSettings.library.defines) { const std::string::size_type pos = def.find_first_of(" ("); if (pos == std::string::npos) { dui.defines.push_back(def); continue; } std::string s = def; if (s[pos] == ' ') { s[pos] = '='; } else { s[s.find(')')+1] = '='; } dui.defines.push_back(s); } if (Path::isCPP(filename)) dui.defines.emplace_back("__cplusplus"); dui.undefined = mSettings.userUndefs; // -U dui.includePaths = mSettings.includePaths; // -I dui.includes = mSettings.userIncludes; // --include return dui; } static bool hasErrors(const simplecpp::OutputList &outputList) { for (simplecpp::OutputList::const_iterator it = outputList.begin(); it != outputList.end(); ++it) { switch (it->type) { case simplecpp::Output::ERROR: case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: return true; case simplecpp::Output::WARNING: case simplecpp::Output::MISSING_HEADER: case simplecpp::Output::PORTABILITY_BACKSLASH: break; }; } return false; } void Preprocessor::handleErrors(const simplecpp::OutputList& outputList, bool throwError) { const bool showerror = (!mSettings.userDefines.empty() && !mSettings.force); reportOutput(outputList, showerror); if (throwError) { for (const simplecpp::Output& output : outputList) { switch (output.type) { case simplecpp::Output::ERROR: case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: throw output; case simplecpp::Output::WARNING: case simplecpp::Output::MISSING_HEADER: case simplecpp::Output::PORTABILITY_BACKSLASH: break; }; } } } bool Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector &files) { const simplecpp::DUI dui = createDUI(mSettings, emptyString, files[0]); simplecpp::OutputList outputList; mTokenLists = simplecpp::load(rawtokens, files, dui, &outputList); handleErrors(outputList, false); return !hasErrors(outputList); } void Preprocessor::removeComments() { for (std::map::iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { if (it->second) it->second->removeComments(); } } void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const { tokens->sizeOfType["bool"] = mSettings.sizeof_bool; tokens->sizeOfType["short"] = mSettings.sizeof_short; tokens->sizeOfType["int"] = mSettings.sizeof_int; tokens->sizeOfType["long"] = mSettings.sizeof_long; tokens->sizeOfType["long long"] = mSettings.sizeof_long_long; tokens->sizeOfType["float"] = mSettings.sizeof_float; tokens->sizeOfType["double"] = mSettings.sizeof_double; tokens->sizeOfType["long double"] = mSettings.sizeof_long_double; tokens->sizeOfType["bool *"] = mSettings.sizeof_pointer; tokens->sizeOfType["short *"] = mSettings.sizeof_pointer; tokens->sizeOfType["int *"] = mSettings.sizeof_pointer; tokens->sizeOfType["long *"] = mSettings.sizeof_pointer; tokens->sizeOfType["long long *"] = mSettings.sizeof_pointer; tokens->sizeOfType["float *"] = mSettings.sizeof_pointer; tokens->sizeOfType["double *"] = mSettings.sizeof_pointer; tokens->sizeOfType["long double *"] = mSettings.sizeof_pointer; } simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool throwError) { const simplecpp::DUI dui = createDUI(mSettings, cfg, files[0]); simplecpp::OutputList outputList; std::list macroUsage; simplecpp::TokenList tokens2(files); simplecpp::preprocess(tokens2, tokens1, files, mTokenLists, dui, &outputList, ¯oUsage); handleErrors(outputList, throwError); tokens2.removeComments(); // ensure that guessed define macros without value are not used in the code if (!validateCfg(cfg, macroUsage)) return simplecpp::TokenList(files); return tokens2; } std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, const bool writeLocations) { simplecpp::TokenList tokens2 = preprocess(tokens1, cfg, files, false); unsigned int prevfile = 0; unsigned int line = 1; std::ostringstream ret; for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) { if (writeLocations && tok->location.fileIndex != prevfile) { ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n"; prevfile = tok->location.fileIndex; line = tok->location.line; } if (tok->previous && line >= tok->location.line) // #7912 ret << ' '; while (tok->location.line > line) { ret << '\n'; line++; } if (!tok->macro.empty()) ret << Preprocessor::macroChar; ret << tok->str(); } return ret.str(); } std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename) { simplecpp::OutputList outputList; std::vector files; std::istringstream istr(filedata); simplecpp::TokenList tokens1(istr, files, Path::simplifyPath(filename), &outputList); inlineSuppressions(tokens1); tokens1.removeComments(); removeComments(); setDirectives(tokens1); reportOutput(outputList, true); if (hasErrors(outputList)) return ""; std::string ret; try { ret = getcode(tokens1, cfg, files, filedata.find("#file") != std::string::npos); } catch (const simplecpp::Output &) { ret.clear(); } return ret; } void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror) { for (const simplecpp::Output &out : outputList) { switch (out.type) { case simplecpp::Output::ERROR: if (out.msg.compare(0,6,"#error")!=0 || showerror) error(out.location.file(), out.location.line, out.msg); break; case simplecpp::Output::WARNING: case simplecpp::Output::PORTABILITY_BACKSLASH: break; case simplecpp::Output::MISSING_HEADER: { const std::string::size_type pos1 = out.msg.find_first_of("<\""); const std::string::size_type pos2 = out.msg.find_first_of(">\"", pos1 + 1U); if (pos1 < pos2 && pos2 != std::string::npos) missingInclude(out.location.file(), out.location.line, out.msg.substr(pos1+1, pos2-pos1-1), out.msg[pos1] == '\"' ? UserHeader : SystemHeader); } break; case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: case simplecpp::Output::SYNTAX_ERROR: case simplecpp::Output::UNHANDLED_CHAR_ERROR: error(out.location.file(), out.location.line, out.msg); break; case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: error(emptyString, 0, out.msg); break; }; } } void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg) { std::list locationList; if (!filename.empty()) { const ErrorLogger::ErrorMessage::FileLocation loc(filename, linenr, 0); locationList.push_back(loc); } mErrorLogger->reportErr(ErrorLogger::ErrorMessage(locationList, mFile0, Severity::error, msg, "preprocessorErrorDirective", false)); } // Report that include is missing void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType) { const std::string fname = Path::fromNativeSeparators(filename); Suppressions::ErrorMessage errorMessage; errorMessage.errorId = "missingInclude"; errorMessage.setFileName(fname); errorMessage.lineNumber = linenr; if (mSettings.nomsg.isSuppressed(errorMessage)) return; errorMessage.errorId = "missingIncludeSystem"; if (headerType == SystemHeader && mSettings.nomsg.isSuppressed(errorMessage)) return; if (headerType == SystemHeader) missingSystemIncludeFlag = true; else missingIncludeFlag = true; if (mErrorLogger && mSettings.checkConfiguration) { std::list locationList; if (!filename.empty()) { ErrorLogger::ErrorMessage::FileLocation loc; loc.line = linenr; loc.setfile(Path::toNativeSeparators(filename)); locationList.push_back(loc); } ErrorLogger::ErrorMessage errmsg(locationList, mFile0, Severity::information, (headerType==SystemHeader) ? "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." : "Include file: \"" + header + "\" not found.", (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude", false); mErrorLogger->reportInfo(errmsg); } } bool Preprocessor::validateCfg(const std::string &cfg, const std::list ¯oUsageList) { bool ret = true; std::list defines; splitcfg(cfg, defines, emptyString); for (const std::string &define : defines) { if (define.find('=') != std::string::npos) continue; const std::string macroName(define.substr(0, define.find('('))); for (const simplecpp::MacroUsage &mu : macroUsageList) { if (mu.macroValueKnown) continue; if (mu.macroName != macroName) continue; bool directiveLocation = std::any_of(mDirectives.cbegin(), mDirectives.cend(), [=](const Directive &dir) { return mu.useLocation.file() == dir.file && mu.useLocation.line == dir.linenr; }); if (!directiveLocation) { if (mSettings.isEnabled(Settings::INFORMATION)) validateCfgError(mu.useLocation.file(), mu.useLocation.line, cfg, macroName); ret = false; } } } return ret; } void Preprocessor::validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o) { const std::string id = "ConfigurationNotChecked"; std::list locationList; const ErrorLogger::ErrorMessage::FileLocation loc(file, line, 0); locationList.push_back(loc); const ErrorLogger::ErrorMessage errmsg(locationList, mFile0, Severity::information, "Skipping configuration '" + cfg + "' since the value of '" + macro + "' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.", id, false); mErrorLogger->reportInfo(errmsg); } void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) { Settings settings2(*settings); Preprocessor preprocessor(settings2, errorLogger); settings2.checkConfiguration = true; preprocessor.missingInclude(emptyString, 1, emptyString, UserHeader); preprocessor.missingInclude(emptyString, 1, emptyString, SystemHeader); preprocessor.validateCfgError(emptyString, 1, "X", "X"); preprocessor.error(emptyString, 1, "#error message"); // #error .. } void Preprocessor::dump(std::ostream &out) const { // Create a xml directive dump. // The idea is not that this will be readable for humans. It's a // data dump that 3rd party tools could load and get useful info from. out << " " << std::endl; for (const Directive &dir : mDirectives) { out << " ' which // could result in invalid XML, so run it through toxml(). << "str=\"" << ErrorLogger::toxml(dir.str) << "\"/>" << std::endl; } out << " " << std::endl; } static const std::uint32_t crc32Table[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; static std::uint32_t crc32(const std::string &data) { std::uint32_t crc = ~0U; for (char c : data) { crc = crc32Table[(crc ^ (unsigned char)c) & 0xFF] ^ (crc >> 8); } return crc ^ ~0U; } unsigned int Preprocessor::calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const { std::ostringstream ostr; ostr << toolinfo << '\n'; for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) { if (!tok->comment) ostr << tok->str(); } for (std::map::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) { if (!tok->comment) ostr << tok->str(); } } return crc32(ostr.str()); } void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList) { Preprocessor::simplifyPragmaAsmPrivate(tokenList); for (std::map::iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) { Preprocessor::simplifyPragmaAsmPrivate(it->second); } } void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList) { // assembler code.. for (simplecpp::Token *tok = tokenList->front(); tok; tok = tok->next) { if (tok->op != '#') continue; if (sameline(tok, tok->previousSkipComments())) continue; const simplecpp::Token * const tok2 = tok->nextSkipComments(); if (!tok2 || !sameline(tok, tok2) || tok2->str() != "pragma") continue; const simplecpp::Token * const tok3 = tok2->nextSkipComments(); if (!tok3 || !sameline(tok, tok3) || tok3->str() != "asm") continue; const simplecpp::Token *endasm = tok3; while ((endasm = endasm->next) != nullptr) { if (endasm->op != '#' || sameline(endasm,endasm->previousSkipComments())) continue; const simplecpp::Token * const endasm2 = endasm->nextSkipComments(); if (!endasm2 || !sameline(endasm, endasm2) || endasm2->str() != "pragma") continue; const simplecpp::Token * const endasm3 = endasm2->nextSkipComments(); if (!endasm3 || !sameline(endasm2, endasm3) || endasm3->str() != "endasm") continue; while (sameline(endasm,endasm3)) endasm = endasm->next; break; } const simplecpp::Token * const tok4 = tok3->next; tok->setstr("asm"); const_cast(tok2)->setstr("("); const_cast(tok3)->setstr(")"); const_cast(tok4)->setstr(";"); while (tok4->next != endasm) tokenList->deleteToken(tok4->next); } } cppcheck-1.90/lib/preprocessor.h000066400000000000000000000201201357737443600167060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef preprocessorH #define preprocessorH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include #include #include #include #include class ErrorLogger; class Settings; /** * @brief A preprocessor directive * Each preprocessor directive (\#include, \#define, \#undef, \#if, \#ifdef, \#else, \#endif) * will be recorded as an instance of this class. * * file and linenr denote the location where where the directive is defined. * */ class CPPCHECKLIB Directive { public: /** name of (possibly included) file where directive is defined */ std::string file; /** line number in (possibly included) file where directive is defined */ unsigned int linenr; /** the actual directive text */ std::string str; /** record a directive (possibly filtering src) */ Directive(const std::string &_file, const int _linenr, const std::string &_str); }; /// @addtogroup Core /// @{ /** * @brief The cppcheck preprocessor. * The preprocessor has special functionality for extracting the various ifdef * configurations that exist in a source file. */ class CPPCHECKLIB Preprocessor { public: /** * Include file types. */ enum HeaderTypes { NoHeader = 0, UserHeader, SystemHeader }; /** character that is inserted in expanded macros */ static char macroChar; explicit Preprocessor(Settings& settings, ErrorLogger *errorLogger = nullptr); virtual ~Preprocessor(); static std::atomic missingIncludeFlag; static std::atomic missingSystemIncludeFlag; void inlineSuppressions(const simplecpp::TokenList &tokens); void setDirectives(const simplecpp::TokenList &tokens); /** list of all directives met while preprocessing file */ const std::list &getDirectives() const { return mDirectives; } std::set getConfigs(const simplecpp::TokenList &tokens) const; void handleErrors(const simplecpp::OutputList &outputList, bool throwError); bool loadFiles(const simplecpp::TokenList &rawtokens, std::vector &files); void removeComments(); void setPlatformInfo(simplecpp::TokenList *tokens) const; /** * Extract the code for each configuration * @param istr The (file/string) stream to read from. * @param result The map that will get the results * @param filename The name of the file to check e.g. "src/main.cpp" * @param includePaths List of paths where include files should be searched from, * single path can be e.g. in format "include/". * There must be a path separator at the end. Default parameter is empty list. * Note that if path from given filename is also extracted and that is used as * a last include path if include file was not found from earlier paths. */ void preprocess(std::istream &istr, std::map &result, const std::string &filename, const std::list &includePaths = std::list()); /** * Extract the code for each configuration. Use this with getcode() to get the * file data for each individual configuration. * * @param srcCodeStream The (file/string) stream to read from. * @param processedFile Give reference to empty string as a parameter, * function will fill processed file here. Use this also as a filedata parameter * to getcode() if you received more than once configurations. * @param resultConfigurations List of configurations. Pass these one by one * to getcode() with processedFile. * @param filename The name of the file to check e.g. "src/main.cpp" * @param includePaths List of paths where include files should be searched from, * single path can be e.g. in format "include/". * There must be a path separator at the end. Default parameter is empty list. * Note that if path from given filename is also extracted and that is used as * a last include path if include file was not found from earlier paths. */ void preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list &resultConfigurations, const std::string &filename, const std::list &includePaths); simplecpp::TokenList preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool throwError = false); std::string getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, const bool writeLocations); /** * Get preprocessed code for a given configuration * @param filedata file data including preprocessing 'if', 'define', etc * @param cfg configuration to read out * @param filename name of source file */ std::string getcode(const std::string &filedata, const std::string &cfg, const std::string &filename); /** * preprocess all whitespaces * @param processedFile The data to be processed */ static void preprocessWhitespaces(std::string &processedFile); /** * make sure empty configuration macros are not used in code. the given code must be a single configuration * @param cfg configuration * @param macroUsageList macro usage list * @return true => configuration is valid */ bool validateCfg(const std::string &cfg, const std::list ¯oUsageList); void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string ¯o); /** * Calculate CRC32 checksum. Using toolinfo, tokens1, filedata. * * @param tokens1 Sourcefile tokens * @param toolinfo Arbitrary extra toolinfo * @return CRC32 checksum */ unsigned int calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const; void simplifyPragmaAsm(simplecpp::TokenList *tokenList); private: static void simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList); /** * Remove space that has new line character on left or right side of it. * * @param str The string to be converted * @return The string where space characters have been removed. */ static std::string removeSpaceNearNL(const std::string &str); public: static void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings); void setFile0(const std::string &f) { mFile0 = f; } /** * dump all directives present in source file */ void dump(std::ostream &out) const; void reportOutput(const simplecpp::OutputList &outputList, bool showerror); private: void missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType); void error(const std::string &filename, unsigned int linenr, const std::string &msg); Settings& mSettings; ErrorLogger *mErrorLogger; /** list of all directives met while preprocessing file */ std::list mDirectives; std::map mTokenLists; /** filename for cpp/c file - useful when reporting errors */ std::string mFile0; }; /// @} //--------------------------------------------------------------------------- #endif // preprocessorH cppcheck-1.90/lib/programmemory.cpp000066400000000000000000000357621357737443600174350ustar00rootroot00000000000000 #include "programmemory.h" #include "token.h" #include "astutils.h" #include "symboldatabase.h" #include void ProgramMemory::setValue(nonneg int varid, const ValueFlow::Value &value) { values[varid] = value; } bool ProgramMemory::getIntValue(nonneg int varid, MathLib::bigint* result) const { const std::map::const_iterator it = values.find(varid); const bool found = it != values.end() && it->second.isIntValue(); if (found) *result = it->second.intvalue; return found; } void ProgramMemory::setIntValue(nonneg int varid, MathLib::bigint value) { values[varid] = ValueFlow::Value(value); } bool ProgramMemory::getTokValue(nonneg int varid, const Token** result) const { const std::map::const_iterator it = values.find(varid); const bool found = it != values.end() && it->second.isTokValue(); if (found) *result = it->second.tokvalue; return found; } bool ProgramMemory::hasValue(nonneg int varid) { return values.find(varid) != values.end(); } void ProgramMemory::swap(ProgramMemory &pm) { values.swap(pm.values); } void ProgramMemory::clear() { values.clear(); } bool ProgramMemory::empty() const { return values.empty(); } void ProgramMemory::replace(const ProgramMemory &pm) { for (auto&& p:pm.values) values[p.first] = p.second; } void ProgramMemory::insert(const ProgramMemory &pm) { for (auto&& p:pm.values) values.insert(p); } bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory) { if (!condition) return false; if (condition->str() == "&&") { return conditionIsFalse(condition->astOperand1(), programMemory) || conditionIsFalse(condition->astOperand2(), programMemory); } ProgramMemory progmem(programMemory); MathLib::bigint result = 0; bool error = false; execute(condition, &progmem, &result, &error); return !error && result == 0; } bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory) { if (!condition) return false; if (condition->str() == "||") { return conditionIsTrue(condition->astOperand1(), programMemory) || conditionIsTrue(condition->astOperand2(), programMemory); } ProgramMemory progmem(programMemory); bool error = false; MathLib::bigint result = 0; execute(condition, &progmem, &result, &error); return !error && result == 1; } static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then) { if (Token::Match(tok, "==|>=|<=|<|>|!=")) { if (then && !Token::Match(tok, "==|>=|<=")) return; if (!then && !Token::Match(tok, "<|>|!=")) return; ValueFlow::Value truevalue; ValueFlow::Value falsevalue; const Token* vartok = parseCompareInt(tok, truevalue, falsevalue); if (!vartok) return; if (vartok->varId() == 0) return; if (!truevalue.isIntValue()) return; if (isVariableChanged(tok->next(), endTok, vartok->varId(), false, settings, true)) return; pm.setIntValue(vartok->varId(), then ? truevalue.intvalue : falsevalue.intvalue); } else if (Token::Match(tok, "%var%")) { if (tok->varId() == 0) return; if (then && !astIsPointer(tok) && !astIsBool(tok)) return; if (isVariableChanged(tok->next(), endTok, tok->varId(), false, settings, true)) return; pm.setIntValue(tok->varId(), then); } else if (Token::simpleMatch(tok, "!")) { programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then); } else if (then && Token::simpleMatch(tok, "&&")) { programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); } else if (!then && Token::simpleMatch(tok, "||")) { programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); } } static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings* settings) { if (!scope) return; if (!scope->isLocal()) return; assert(scope != scope->nestedIn); fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings); if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse) { const Token * bodyStart = scope->bodyStart; if (scope->type == Scope::eElse) { if (!Token::simpleMatch(bodyStart->tokAt(-2), "} else {")) return; bodyStart = bodyStart->linkAt(-2); } const Token * condEndTok = bodyStart->previous(); if (!Token::simpleMatch(condEndTok, ") {")) return; const Token * condStartTok = condEndTok->link(); if (!condStartTok) return; if (!Token::Match(condStartTok->previous(), "if|while (")) return; const Token * condTok = condStartTok->astOperand2(); programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse); } } static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Token* tok, const Settings* settings) { fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings); } static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok, const ProgramMemory& state, std::unordered_map vars) { int indentlevel = 0; for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) { bool setvar = false; if (Token::Match(tok2, "[;{}] %var% = %var% ;")) { for (auto&& p:vars) { if (p.first != tok2->next()->varId()) continue; const Token *vartok = tok2->tokAt(3); pm.setValue(vartok->varId(), p.second); setvar = true; } } if (!setvar && (Token::Match(tok2, ";|{|}|%type% %var% =") || Token::Match(tok2, "[;{}] const| %type% %var% ("))) { const Token *vartok = tok2->next(); while (vartok->next()->isName()) vartok = vartok->next(); if (!pm.hasValue(vartok->varId())) { MathLib::bigint result = 0; bool error = false; execute(vartok->next()->astOperand2(), &pm, &result, &error); if (!error) pm.setIntValue(vartok->varId(), result); } } if (tok2->str() == "{") { if (indentlevel <= 0) break; --indentlevel; } if (tok2->str() == "}") { const Token *cond = tok2->link(); cond = Token::simpleMatch(cond->previous(), ") {") ? cond->linkAt(-1) : nullptr; if (cond && conditionIsFalse(cond->astOperand2(), state)) tok2 = cond->previous(); else if (cond && conditionIsTrue(cond->astOperand2(), state)) { ++indentlevel; continue; } else break; } } } ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value) { ProgramMemory programMemory; if (value.tokvalue) fillProgramMemoryFromConditions(programMemory, value.tokvalue, nullptr); if (value.condition) fillProgramMemoryFromConditions(programMemory, value.condition, nullptr); programMemory.setValue(varid, value); if (value.varId) programMemory.setIntValue(value.varId, value.varvalue); const ProgramMemory state = programMemory; fillProgramMemoryFromAssignments(programMemory, tok, state, {{varid, value}}); return programMemory; } void execute(const Token *expr, ProgramMemory * const programMemory, MathLib::bigint *result, bool *error) { if (!expr) *error = true; else if (expr->hasKnownIntValue()) { *result = expr->values().front().intvalue; } else if (expr->isNumber()) { *result = MathLib::toLongNumber(expr->str()); if (MathLib::isFloat(expr->str())) *error = true; } else if (expr->varId() > 0) { if (!programMemory->getIntValue(expr->varId(), result)) *error = true; } else if (expr->isComparisonOp()) { MathLib::bigint result1(0), result2(0); execute(expr->astOperand1(), programMemory, &result1, error); execute(expr->astOperand2(), programMemory, &result2, error); if (expr->str() == "<") *result = result1 < result2; else if (expr->str() == "<=") *result = result1 <= result2; else if (expr->str() == ">") *result = result1 > result2; else if (expr->str() == ">=") *result = result1 >= result2; else if (expr->str() == "==") *result = result1 == result2; else if (expr->str() == "!=") *result = result1 != result2; } else if (expr->isAssignmentOp()) { execute(expr->astOperand2(), programMemory, result, error); if (!expr->astOperand1() || !expr->astOperand1()->varId()) *error = true; if (*error) return; if (expr->str() == "=") { programMemory->setIntValue(expr->astOperand1()->varId(), *result); return; } long long intValue; if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) { *error = true; return; } if (expr->str() == "+=") programMemory->setIntValue(expr->astOperand1()->varId(), intValue + *result); else if (expr->str() == "-=") programMemory->setIntValue(expr->astOperand1()->varId(), intValue - *result); else if (expr->str() == "*=") programMemory->setIntValue(expr->astOperand1()->varId(), intValue * *result); else if (expr->str() == "/=" && *result != 0) programMemory->setIntValue(expr->astOperand1()->varId(), intValue / *result); else if (expr->str() == "%=" && *result != 0) programMemory->setIntValue(expr->astOperand1()->varId(), intValue % *result); else if (expr->str() == "&=") programMemory->setIntValue(expr->astOperand1()->varId(), intValue & *result); else if (expr->str() == "|=") programMemory->setIntValue(expr->astOperand1()->varId(), intValue | *result); else if (expr->str() == "^=") programMemory->setIntValue(expr->astOperand1()->varId(), intValue ^ *result); } else if (Token::Match(expr, "++|--")) { if (!expr->astOperand1() || expr->astOperand1()->varId() == 0U) *error = true; else { long long intValue; if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) *error = true; else { if (intValue == 0 && expr->str() == "--" && expr->astOperand1()->variable() && expr->astOperand1()->variable()->typeStartToken()->isUnsigned()) *error = true; // overflow *result = intValue + (expr->str() == "++" ? 1 : -1); programMemory->setIntValue(expr->astOperand1()->varId(), *result); } } } else if (expr->isArithmeticalOp() && expr->astOperand1() && expr->astOperand2()) { MathLib::bigint result1(0), result2(0); execute(expr->astOperand1(), programMemory, &result1, error); execute(expr->astOperand2(), programMemory, &result2, error); if (expr->str() == "+") *result = result1 + result2; else if (expr->str() == "-") *result = result1 - result2; else if (expr->str() == "*") { if (result2 && (result1 > std::numeric_limits::max()/result2)) *error = true; else *result = result1 * result2; } else if (result2 == 0) *error = true; else if (expr->str() == "/") *result = result1 / result2; else if (expr->str() == "%") *result = result1 % result2; else if (expr->str() == "<<") { if (result2 < 0 || result1 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB *error= true; } else { *result = result1 << result2; } } else if (expr->str() == ">>") { if (result2 < 0) { // don't perform UB *error=true; } else { *result = result1 >> result2; } } } else if (expr->str() == "&&") { bool error1 = false; execute(expr->astOperand1(), programMemory, result, &error1); if (!error1 && *result == 0) *result = 0; else { bool error2 = false; execute(expr->astOperand2(), programMemory, result, &error2); if (error1 && error2) *error = true; if (error2) *result = 1; else *result = !!*result; } } else if (expr->str() == "||") { execute(expr->astOperand1(), programMemory, result, error); if (*result == 0 && *error == false) execute(expr->astOperand2(), programMemory, result, error); } else if (expr->str() == "!") { execute(expr->astOperand1(), programMemory, result, error); *result = !(*result); } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { execute(expr->astOperand1(), programMemory, result, error); execute(expr->astOperand2(), programMemory, result, error); } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { const Token *tokvalue = nullptr; if (!programMemory->getTokValue(expr->astOperand1()->varId(), &tokvalue)) { auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(), expr->astOperand1()->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue)); if (tokvalue_it == expr->astOperand1()->values().end()) { *error = true; return; } tokvalue = tokvalue_it->tokvalue; } if (!tokvalue || !tokvalue->isLiteral()) { *error = true; return; } const std::string strValue = tokvalue->strValue(); MathLib::bigint index = 0; execute(expr->astOperand2(), programMemory, &index, error); if (index >= 0 && index < strValue.size()) *result = strValue[index]; else if (index == strValue.size()) *result = 0; else *error = true; } else *error = true; } cppcheck-1.90/lib/programmemory.h000066400000000000000000000027541357737443600170750ustar00rootroot00000000000000#ifndef GUARD_PROGRAMMEMORY_H #define GUARD_PROGRAMMEMORY_H #include "config.h" #include "utils.h" #include "valueflow.h" #include "mathlib.h" #include struct ProgramMemory { std::map values; void setValue(nonneg int varid, const ValueFlow::Value &value); bool getIntValue(nonneg int varid, MathLib::bigint* result) const; void setIntValue(nonneg int varid, MathLib::bigint value); bool getTokValue(nonneg int varid, const Token** result) const; bool hasValue(nonneg int varid); void swap(ProgramMemory &pm); void clear(); bool empty() const; void replace(const ProgramMemory &pm); void insert(const ProgramMemory &pm); }; void execute(const Token *expr, ProgramMemory * const programMemory, MathLib::bigint *result, bool *error); /** * Is condition always false when variable has given value? * \param condition top ast token in condition * \param programMemory program memory */ bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory); /** * Is condition always true when variable has given value? * \param condition top ast token in condition * \param programMemory program memory */ bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory); /** * Get program memory by looking backwards from given token. */ ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value); #endif cppcheck-1.90/lib/settings.cpp000066400000000000000000000113511357737443600163610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "settings.h" #include "valueflow.h" std::atomic Settings::mTerminated; const char Settings::SafeChecks::XmlRootName[] = "safe-checks"; const char Settings::SafeChecks::XmlClasses[] = "class-public"; const char Settings::SafeChecks::XmlExternalFunctions[] = "external-functions"; const char Settings::SafeChecks::XmlInternalFunctions[] = "internal-functions"; const char Settings::SafeChecks::XmlExternalVariables[] = "external-variables"; Settings::Settings() : mEnabled(0), checkConfiguration(false), checkLibrary(false), checkHeaders(true), checkUnusedTemplates(false), debugSimplified(false), debugnormal(false), debugwarnings(false), debugtemplate(false), dump(false), enforcedLang(None), exceptionHandling(false), exitCode(0), experimental(false), force(false), inconclusive(false), verification(false), debugVerification(false), inlineSuppressions(false), jobs(1), jointSuppressionReport(false), loadAverage(0), maxConfigs(12), checkAllConfigurations(true), maxCtuDepth(2), preprocessOnly(false), quiet(false), relativePaths(false), reportProgress(false), showtime(SHOWTIME_MODES::SHOWTIME_NONE), verbose(false), xml(false), xml_version(2) { } std::string Settings::addEnabled(const std::string &str) { // Enable parameters may be comma separated... if (str.find(',') != std::string::npos) { std::string::size_type prevPos = 0; std::string::size_type pos = 0; while ((pos = str.find(',', pos)) != std::string::npos) { if (pos == prevPos) return std::string("cppcheck: --enable parameter is empty"); const std::string errmsg(addEnabled(str.substr(prevPos, pos - prevPos))); if (!errmsg.empty()) return errmsg; ++pos; prevPos = pos; } if (prevPos >= str.length()) return std::string("cppcheck: --enable parameter is empty"); return addEnabled(str.substr(prevPos)); } if (str == "all") { mEnabled |= WARNING | STYLE | PERFORMANCE | PORTABILITY | INFORMATION | UNUSED_FUNCTION | MISSING_INCLUDE; } else if (str == "warning") { mEnabled |= WARNING; } else if (str == "style") { mEnabled |= STYLE; } else if (str == "performance") { mEnabled |= PERFORMANCE; } else if (str == "portability") { mEnabled |= PORTABILITY; } else if (str == "information") { mEnabled |= INFORMATION | MISSING_INCLUDE; } else if (str == "unusedFunction") { mEnabled |= UNUSED_FUNCTION; } else if (str == "missingInclude") { mEnabled |= MISSING_INCLUDE; } #ifdef CHECK_INTERNAL else if (str == "internal") { mEnabled |= INTERNAL; } #endif else { if (str.empty()) return std::string("cppcheck: --enable parameter is empty"); else return std::string("cppcheck: there is no --enable parameter with the name '" + str + "'"); } return std::string(); } bool Settings::isEnabled(Severity::SeverityType severity) const { switch (severity) { case Severity::none: return true; case Severity::error: return true; case Severity::warning: return isEnabled(WARNING); case Severity::style: return isEnabled(STYLE); case Severity::performance: return isEnabled(PERFORMANCE); case Severity::portability: return isEnabled(PORTABILITY); case Severity::information: return isEnabled(INFORMATION); case Severity::debug: return false; default: return false; } } bool Settings::isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck) const { if (!isEnabled(Settings::WARNING) && (value->condition || value->defaultArg)) return false; if (!inconclusive && (inconclusiveCheck || value->isInconclusive())) return false; return true; } cppcheck-1.90/lib/settings.h000066400000000000000000000245521357737443600160350ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef settingsH #define settingsH //--------------------------------------------------------------------------- #include "config.h" #include "errorlogger.h" #include "importproject.h" #include "library.h" #include "platform.h" #include "standards.h" #include "suppressions.h" #include "timer.h" #include #include #include #include #include namespace ValueFlow { class Value; } /// @addtogroup Core /// @{ /** * @brief This is just a container for general settings so that we don't need * to pass individual values to functions or constructors now or in the * future when we might have even more detailed settings. */ class CPPCHECKLIB Settings : public cppcheck::Platform { public: enum EnabledGroup { WARNING = 0x1, STYLE = 0x2, PERFORMANCE = 0x4, PORTABILITY = 0x8, INFORMATION = 0x10, UNUSED_FUNCTION = 0x20, MISSING_INCLUDE = 0x40, INTERNAL = 0x80 }; private: /** @brief enable extra checks by id */ int mEnabled; /** @brief terminate checking */ static std::atomic mTerminated; public: Settings(); std::list addons; /** @brief Paths used as base for conversion to relative paths. */ std::vector basePaths; /** @brief --cppcheck-build-dir */ std::string buildDir; /** Is the 'configuration checking' wanted? */ bool checkConfiguration; /** Check for incomplete info in library files? */ bool checkLibrary; /** * Check code in the headers, this is on by default but can * be turned off to save CPU */ bool checkHeaders; /** Check unused templates */ bool checkUnusedTemplates; /** @brief include paths excluded from checking the configuration */ std::set configExcludePaths; /** @brief Is --debug-simplified given? */ bool debugSimplified; /** @brief Is --debug-normal given? */ bool debugnormal; /** @brief Is --debug-warnings given? */ bool debugwarnings; /** @brief Is --debug-template given? */ bool debugtemplate; /** @brief Is --dump given? */ bool dump; std::string dumpFile; enum Language { None, C, CPP }; /** @brief Name of the language that is enforced. Empty per default. */ Language enforcedLang; /** @brief Is --exception-handling given */ bool exceptionHandling; // argv[0] std::string exename; /** @brief If errors are found, this value is returned from main(). Default value is 0. */ int exitCode; /** * When this flag is false (default) then experimental * heuristics and checks are disabled. * * It should not be possible to enable this from any client. */ bool experimental; /** @brief Force checking the files with "too many" configurations (--force). */ bool force; /** @brief List of include paths, e.g. "my/includes/" which should be used for finding include files inside source files. (-I) */ std::list includePaths; /** @brief Inconclusive checks */ bool inconclusive; /** Do not only check how interface is used. Also check that interface is safe. */ class CPPCHECKLIB SafeChecks { public: SafeChecks() : classes(false), externalFunctions(false), internalFunctions(false), externalVariables(false) {} static const char XmlRootName[]; static const char XmlClasses[]; static const char XmlExternalFunctions[]; static const char XmlInternalFunctions[]; static const char XmlExternalVariables[]; /** * Public interface of classes * - public function parameters can have any value * - public functions can be called in any order * - public variables can have any value */ bool classes; /** * External functions * - external functions can be called in any order * - function parameters can have any values */ bool externalFunctions; /** * Experimental: assume that internal functions can be used in any way * This is only available in the GUI. */ bool internalFunctions; /** * Global variables that can be modified outside the TU. * - Such variable can have "any" value */ bool externalVariables; }; SafeChecks safeChecks; /** @brief Enable verification analysis */ bool verification; bool debugVerification; /** @brief check unknown function return values */ std::set checkUnknownFunctionReturn; /** @brief Is --inline-suppr given? */ bool inlineSuppressions; /** @brief How many processes/threads should do checking at the same time. Default is 1. (-j N) */ unsigned int jobs; /** @brief Collect unmatched suppressions in one run. * This delays the reporting until all files are checked. * It is needed by checks that analyse the whole code base. */ bool jointSuppressionReport; /** @brief --library= */ std::list libraries; /** Library */ Library library; /** @brief Load average value */ unsigned int loadAverage; /** @brief Maximum number of configurations to check before bailing. Default is 12. (--max-configs=N) */ unsigned int maxConfigs; /** @brief --check all configurations */ bool checkAllConfigurations; /** @brief --max-ctu-depth */ int maxCtuDepth; /** @brief suppress exitcode */ Suppressions nofail; /** @brief suppress message (--suppressions) */ Suppressions nomsg; /** @brief write results (--output-file=<file>) */ std::string outputFile; /** @brief plist output (--plist-output=<dir>) */ std::string plistOutput; /** @brief Using -E for debugging purposes */ bool preprocessOnly; ImportProject project; /** @brief Is --quiet given? */ bool quiet; /** @brief Use relative paths in output. */ bool relativePaths; /** @brief --report-progress */ bool reportProgress; /** Rule */ class CPPCHECKLIB Rule { public: Rule() : tokenlist("simple") // use simple tokenlist , id("rule") // default id , severity(Severity::style) { // default severity } std::string tokenlist; std::string pattern; std::string id; std::string summary; Severity::SeverityType severity; }; /** * @brief Extra rules */ std::list rules; /** @brief show timing information (--showtime=file|summary|top5) */ SHOWTIME_MODES showtime; /** Struct contains standards settings */ Standards standards; /** @brief The output format in which the errors are printed in text mode, e.g. "{severity} {file}:{line} {message} {id}" */ std::string templateFormat; /** @brief The output format in which the error locations are printed in * text mode, e.g. "{file}:{line} {info}" */ std::string templateLocation; /** @brief defines given by the user */ std::string userDefines; /** @brief undefines given by the user */ std::set userUndefs; /** @brief forced includes given by the user */ std::list userIncludes; /** @brief Is --verbose given? */ bool verbose; /** @brief write XML results (--xml) */ bool xml; /** @brief XML version (--xml-version=..) */ int xml_version; /** * @brief return true if a included file is to be excluded in Preprocessor::getConfigs * @return true for the file to be excluded. */ bool configurationExcluded(const std::string &file) const { for (const std::string & configExcludePath : configExcludePaths) { if (file.length()>=configExcludePath.length() && file.compare(0,configExcludePath.length(),configExcludePath)==0) { return true; } } return false; } /** * @brief Enable extra checks by id. See isEnabled() * @param str single id or list of id values to be enabled * or empty string to enable all. e.g. "style,possibleError" * @return error message. empty upon success */ std::string addEnabled(const std::string &str); /** * @brief Disables all severities, except from error. */ void clearEnabled() { mEnabled = 0; } /** * @brief Returns true if given id is in the list of * enabled extra checks (--enable) * @param group group to be enabled * @return true if the check is enabled. */ bool isEnabled(EnabledGroup group) const { return (mEnabled & group) == group; } /** * @brief Returns true if given severity is enabled * @return true if the check is enabled. */ bool isEnabled(Severity::SeverityType severity) const; /** * @brief Returns true if given value can be shown * @return true if the value can be shown */ bool isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck=false) const; /** Is posix library specified? */ bool posix() const { return std::find(libraries.begin(), libraries.end(), "posix") != libraries.end(); } /** @brief Request termination of checking */ static void terminate(bool t = true) { Settings::mTerminated = t; } /** @brief termination requested? */ static bool terminated() { return Settings::mTerminated; } }; /// @} //--------------------------------------------------------------------------- #endif // settingsH cppcheck-1.90/lib/standards.h000066400000000000000000000060331357737443600161520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef standardsH #define standardsH //--------------------------------------------------------------------------- #include /// @addtogroup Core /// @{ /** * @brief This is just a container for standards settings. * This struct contains all possible standards that cppcheck recognize. */ struct Standards { /** C code C89/C99/C11 standard */ enum cstd_t { C89, C99, C11, CLatest=C11 } c; /** C++ code standard */ enum cppstd_t { CPP03, CPP11, CPP14, CPP17, CPP20, CPPLatest=CPP20 } cpp; /** This constructor clear all the variables **/ Standards() : c(C11), cpp(CPPLatest) {} bool setC(const std::string& str) { if (str == "c89" || str == "C89") { c = C89; return true; } if (str == "c99" || str == "C99") { c = C99; return true; } if (str == "c11" || str == "C11") { c = C11; return true; } return false; } const std::string getC() const { switch (c) { case C89: return "c89"; case C99: return "c99"; case C11: return "c11"; } return ""; } bool setCPP(const std::string& str) { if (str == "c++03" || str == "C++03") { cpp = CPP03; return true; } if (str == "c++11" || str == "C++11") { cpp = CPP11; return true; } if (str == "c++14" || str == "C++14") { cpp = CPP14; return true; } if (str == "c++17" || str == "C++17") { cpp = CPP17; return true; } if (str == "c++20" || str == "C++20") { cpp = CPP20; return true; } return false; } const std::string getCPP() const { switch (cpp) { case CPP03: return "c++03"; case CPP11: return "c++11"; case CPP14: return "c++14"; case CPP17: return "c++17"; case CPP20: return "c++20"; } return ""; } }; /// @} //--------------------------------------------------------------------------- #endif // standardsH cppcheck-1.90/lib/suppressions.cpp000066400000000000000000000330351357737443600173010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "suppressions.h" #include "errorlogger.h" #include "mathlib.h" #include "path.h" #include #include #include // std::isdigit, std::isalnum, etc #include #include #include class ErrorLogger; static bool isValidGlobPattern(const std::string &pattern) { for (std::string::const_iterator i = pattern.begin(); i != pattern.end(); ++i) { if (*i == '*' || *i == '?') { std::string::const_iterator j = i + 1; if (j != pattern.end() && (*j == '*' || *j == '?')) { return false; } } } return true; } static bool isAcceptedErrorIdChar(char c) { switch (c) { case '_': case '-': case '.': return true; default: return std::isalnum(c); } } std::string Suppressions::parseFile(std::istream &istr) { // Change '\r' to '\n' in the istr std::string filedata; std::string line; while (std::getline(istr, line)) filedata += line + "\n"; std::replace(filedata.begin(), filedata.end(), '\r', '\n'); // Parse filedata.. std::istringstream istr2(filedata); while (std::getline(istr2, line)) { // Skip empty lines if (line.empty()) continue; // Skip comments if (line.length() > 1 && line[0] == '#') continue; if (line.length() >= 2 && line[0] == '/' && line[1] == '/') continue; const std::string errmsg(addSuppressionLine(line)); if (!errmsg.empty()) return errmsg; } return ""; } std::string Suppressions::parseXmlFile(const char *filename) { tinyxml2::XMLDocument doc; const tinyxml2::XMLError error = doc.LoadFile(filename); if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) return "File not found"; if (error != tinyxml2::XML_SUCCESS) return "Failed to parse XML file"; const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); for (const tinyxml2::XMLElement * e = rootnode->FirstChildElement(); e; e = e->NextSiblingElement()) { if (std::strcmp(e->Name(), "suppress") != 0) return "Invalid suppression xml file format, expected element but got a <" + std::string(e->Name()) + '>'; Suppression s; for (const tinyxml2::XMLElement * e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) { const char *text = e2->GetText() ? e2->GetText() : ""; if (std::strcmp(e2->Name(), "id") == 0) s.errorId = text; else if (std::strcmp(e2->Name(), "fileName") == 0) s.fileName = text; else if (std::strcmp(e2->Name(), "lineNumber") == 0) s.lineNumber = std::atoi(text); else if (std::strcmp(e2->Name(), "symbolName") == 0) s.symbolName = text; else return "Unknown suppression element <" + std::string(e2->Name()) + ">, expected ///"; } const std::string err = addSuppression(s); if (!err.empty()) return err; } return ""; } std::string Suppressions::addSuppressionLine(const std::string &line) { std::istringstream lineStream(line); Suppressions::Suppression suppression; if (std::getline(lineStream, suppression.errorId, ':')) { if (std::getline(lineStream, suppression.fileName)) { // If there is not a dot after the last colon in "file" then // the colon is a separator and the contents after the colon // is a line number.. // Get position of last colon const std::string::size_type pos = suppression.fileName.rfind(':'); // if a colon is found and there is no dot after it.. if (pos != std::string::npos && suppression.fileName.find('.', pos) == std::string::npos) { // Try to parse out the line number try { std::istringstream istr1(suppression.fileName.substr(pos+1)); istr1 >> suppression.lineNumber; } catch (...) { suppression.lineNumber = Suppressions::Suppression::NO_LINE; } if (suppression.lineNumber != Suppressions::Suppression::NO_LINE) { suppression.fileName.erase(pos); } } } } suppression.fileName = Path::simplifyPath(suppression.fileName); return addSuppression(suppression); } std::string Suppressions::addSuppression(const Suppressions::Suppression &suppression) { // Check that errorId is valid.. if (suppression.errorId.empty()) { return "Failed to add suppression. No id."; } if (suppression.errorId != "*") { for (std::string::size_type pos = 0; pos < suppression.errorId.length(); ++pos) { if (suppression.errorId[pos] < 0 || !isAcceptedErrorIdChar(suppression.errorId[pos])) { return "Failed to add suppression. Invalid id \"" + suppression.errorId + "\""; } if (pos == 0 && std::isdigit(suppression.errorId[pos])) { return "Failed to add suppression. Invalid id \"" + suppression.errorId + "\""; } } } if (!isValidGlobPattern(suppression.errorId)) return "Failed to add suppression. Invalid glob pattern '" + suppression.errorId + "'."; if (!isValidGlobPattern(suppression.fileName)) return "Failed to add suppression. Invalid glob pattern '" + suppression.fileName + "'."; mSuppressions.push_back(suppression); return ""; } void Suppressions::ErrorMessage::setFileName(const std::string &s) { mFileName = Path::simplifyPath(s); } bool Suppressions::Suppression::parseComment(std::string comment, std::string *errorMessage) { if (comment.size() < 2) return false; if (comment.find(';') != std::string::npos) comment.erase(comment.find(';')); if (comment.find("//", 2) != std::string::npos) comment.erase(comment.find("//",2)); if (comment.compare(comment.size() - 2, 2, "*/") == 0) comment.erase(comment.size() - 2, 2); std::istringstream iss(comment.substr(2)); std::string word; iss >> word; if (word != "cppcheck-suppress") return false; iss >> errorId; if (!iss) return false; while (iss) { iss >> word; if (!iss) break; if (word.find_first_not_of("+-*/%#;") == std::string::npos) break; if (word.compare(0,11,"symbolName=")==0) symbolName = word.substr(11); else if (errorMessage && errorMessage->empty()) *errorMessage = "Bad suppression attribute '" + word + "'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym"; } return true; } bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &errmsg) const { if (!errorId.empty() && !matchglob(errorId, errmsg.errorId)) return false; if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName())) return false; if (lineNumber != NO_LINE && lineNumber != errmsg.lineNumber) return false; if (!symbolName.empty()) { for (std::string::size_type pos = 0; pos < errmsg.symbolNames.size();) { const std::string::size_type pos2 = errmsg.symbolNames.find('\n',pos); std::string symname; if (pos2 == std::string::npos) { symname = errmsg.symbolNames.substr(pos); pos = pos2; } else { symname = errmsg.symbolNames.substr(pos,pos2-pos); pos = pos2+1; } if (matchglob(symbolName, symname)) return true; } return false; } return true; } bool Suppressions::Suppression::isMatch(const Suppressions::ErrorMessage &errmsg) { if (!isSuppressed(errmsg)) return false; matched = true; return true; } std::string Suppressions::Suppression::getText() const { std::string ret; if (!errorId.empty()) ret = errorId; if (!fileName.empty()) ret += " fileName=" + fileName; if (lineNumber != NO_LINE) ret += " lineNumber=" + MathLib::toString(lineNumber); if (!symbolName.empty()) ret += " symbolName=" + symbolName; if (ret.compare(0,1," ")==0) return ret.substr(1); return ret; } bool Suppressions::isSuppressed(const Suppressions::ErrorMessage &errmsg) { const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression"); for (Suppression &s : mSuppressions) { if (unmatchedSuppression && s.errorId != errmsg.errorId) continue; if (s.isMatch(errmsg)) return true; } return false; } bool Suppressions::isSuppressedLocal(const Suppressions::ErrorMessage &errmsg) { const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression"); for (Suppression &s : mSuppressions) { if (!s.isLocal()) continue; if (unmatchedSuppression && s.errorId != errmsg.errorId) continue; if (s.isMatch(errmsg)) return true; } return false; } void Suppressions::dump(std::ostream & out) const { out << " " << std::endl; for (const Suppression &suppression : mSuppressions) { out << " " << std::endl; } out << " " << std::endl; } #include std::list Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const { std::list result; for (const Suppression &s : mSuppressions) { if (s.matched) continue; if (!unusedFunctionChecking && s.errorId == "unusedFunction") continue; if (file.empty() || !s.isLocal() || s.fileName != file) continue; result.push_back(s); } return result; } std::list Suppressions::getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const { std::list result; for (const Suppression &s : mSuppressions) { if (s.matched) continue; if (!unusedFunctionChecking && s.errorId == "unusedFunction") continue; if (s.isLocal()) continue; result.push_back(s); } return result; } bool Suppressions::matchglob(const std::string &pattern, const std::string &name) { const char *p = pattern.c_str(); const char *n = name.c_str(); std::stack > backtrack; for (;;) { bool matching = true; while (*p != '\0' && matching) { switch (*p) { case '*': // Step forward until we match the next character after * while (*n != '\0' && *n != p[1]) { n++; } if (*n != '\0') { // If this isn't the last possibility, save it for later backtrack.push(std::make_pair(p, n)); } break; case '?': // Any character matches unless we're at the end of the name if (*n != '\0') { n++; } else { matching = false; } break; default: // Non-wildcard characters match literally if (*n == *p) { n++; } else if (*n == '\\' && *p == '/') { n++; } else if (*n == '/' && *p == '\\') { n++; } else { matching = false; } break; } p++; } // If we haven't failed matching and we've reached the end of the name, then success if (matching && *n == '\0') { return true; } // If there are no other paths to try, then fail if (backtrack.empty()) { return false; } // Restore pointers from backtrack stack p = backtrack.top().first; n = backtrack.top().second; backtrack.pop(); // Advance name pointer by one because the current position didn't work n++; } } cppcheck-1.90/lib/suppressions.h000066400000000000000000000132741357737443600167510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2018 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef suppressionsH #define suppressionsH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include /// @addtogroup Core /// @{ /** @brief class for handling suppressions */ class CPPCHECKLIB Suppressions { public: struct CPPCHECKLIB ErrorMessage { std::string errorId; void setFileName(const std::string &s); const std::string &getFileName() const { return mFileName; } int lineNumber; bool inconclusive; std::string symbolNames; private: std::string mFileName; }; struct CPPCHECKLIB Suppression { Suppression() : lineNumber(NO_LINE), matched(false) {} Suppression(const Suppression &other) { *this = other; } Suppression(const std::string &id, const std::string &file, int line=NO_LINE) : errorId(id), fileName(file), lineNumber(line), matched(false) {} Suppression & operator=(const Suppression &other) { errorId = other.errorId; fileName = other.fileName; lineNumber = other.lineNumber; symbolName = other.symbolName; matched = other.matched; return *this; } bool operator<(const Suppression &other) const { if (errorId != other.errorId) return errorId < other.errorId; if (lineNumber < other.lineNumber) return true; if (fileName != other.fileName) return fileName < other.fileName; if (symbolName != other.symbolName) return symbolName < other.symbolName; return false; } /** * Parse inline suppression in comment * @param comment the full comment text * @param errorMessage output parameter for error message (wrong suppression attribute) * @return true if it is a inline comment. */ bool parseComment(std::string comment, std::string *errorMessage); bool isSuppressed(const ErrorMessage &errmsg) const; bool isMatch(const ErrorMessage &errmsg); std::string getText() const; bool isLocal() const { return !fileName.empty() && fileName.find_first_of("?*") == std::string::npos; } std::string errorId; std::string fileName; int lineNumber; std::string symbolName; bool matched; enum { NO_LINE = -1 }; }; /** * @brief Don't show errors listed in the file. * @param istr Open file stream where errors can be read. * @return error message. empty upon success */ std::string parseFile(std::istream &istr); /** * @brief Don't show errors listed in the file. * @param filename file name * @return error message. empty upon success */ std::string parseXmlFile(const char *filename); /** * @brief Don't show the given error. * @param line Description of error to suppress (in id:file:line format). * @return error message. empty upon success */ std::string addSuppressionLine(const std::string &line); /** * @brief Don't show this error. File and/or line are optional. In which case * the errorId alone is used for filtering. * @param suppression suppression details * @return error message. empty upon success */ std::string addSuppression(const Suppression &suppression); /** * @brief Returns true if this message should not be shown to the user. * @param errmsg error message * @return true if this error is suppressed. */ bool isSuppressed(const ErrorMessage &errmsg); /** * @brief Returns true if this message should not be shown to the user, only uses local suppressions. * @param errmsg error message * @return true if this error is suppressed. */ bool isSuppressedLocal(const ErrorMessage &errmsg); /** * @brief Create an xml dump of suppressions * @param out stream to write XML to */ void dump(std::ostream &out) const; /** * @brief Returns list of unmatched local (per-file) suppressions. * @return list of unmatched suppressions */ std::list getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const; /** * @brief Returns list of unmatched global (glob pattern) suppressions. * @return list of unmatched suppressions */ std::list getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const; static bool matchglob(const std::string &pattern, const std::string &name); private: /** @brief List of error which the user doesn't want to see. */ std::list mSuppressions; }; /// @} //--------------------------------------------------------------------------- #endif // suppressionsH cppcheck-1.90/lib/symboldatabase.cpp000066400000000000000000007313511357737443600175240ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "symboldatabase.h" #include "astutils.h" #include "errorlogger.h" #include "platform.h" #include "settings.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include //--------------------------------------------------------------------------- SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger) { mIsCpp = isCPP(); if (mSettings->defaultSign == 's' || mSettings->defaultSign == 'S') mDefaultSignedness = ValueType::SIGNED; else if (mSettings->defaultSign == 'u' || mSettings->defaultSign == 'U') mDefaultSignedness = ValueType::UNSIGNED; else mDefaultSignedness = ValueType::UNKNOWN_SIGN; createSymbolDatabaseFindAllScopes(); createSymbolDatabaseClassInfo(); createSymbolDatabaseVariableInfo(); createSymbolDatabaseCopyAndMoveConstructors(); createSymbolDatabaseFunctionScopes(); createSymbolDatabaseClassAndStructScopes(); createSymbolDatabaseFunctionReturnTypes(); createSymbolDatabaseNeedInitialization(); createSymbolDatabaseVariableSymbolTable(); createSymbolDatabaseSetScopePointers(); createSymbolDatabaseSetVariablePointers(); setValueTypeInTokenList(false); createSymbolDatabaseSetFunctionPointers(true); createSymbolDatabaseSetTypePointers(); createSymbolDatabaseEnums(); createSymbolDatabaseEscapeFunctions(); createSymbolDatabaseIncompleteVars(); } static const Token* skipScopeIdentifiers(const Token* tok) { if (tok && tok->str() == "::") { tok = tok->next(); } while (Token::Match(tok, "%name% ::") || (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->linkAt(1), "> ::"))) { if (tok->strAt(1) == "::") tok = tok->tokAt(2); else tok = tok->linkAt(1)->tokAt(2); } return tok; } static bool isExecutableScope(const Token* tok) { if (!Token::simpleMatch(tok, "{")) return false; const Token * tok2 = tok->link()->previous(); if (Token::simpleMatch(tok2, "; }")) return true; if (Token::Match(tok2, "{|} }")) { const Token* startTok = tok2->str() == "{" ? tok2 : tok2->link(); if (Token::Match(startTok->previous(), "do|try|else {")) return true; if (Token::simpleMatch(startTok->previous(), ") {")) return !findLambdaStartToken(tok2); if (tok->str() == "{") return false; else return isExecutableScope(startTok); } return false; } void SymbolDatabase::createSymbolDatabaseFindAllScopes() { // create global scope scopeList.emplace_back(this, nullptr, nullptr); // pointer to current scope Scope *scope = &scopeList.back(); // Store current access in each scope (depends on evaluation progress) std::map access; // find all scopes for (const Token *tok = mTokenizer->tokens(); tok; tok = tok ? tok->next() : nullptr) { // #5593 suggested to add here: if (mErrorLogger) mErrorLogger->reportProgress(mTokenizer->list.getSourceFilePath(), "SymbolDatabase", tok->progressValue()); // Locate next class if ((mTokenizer->isCPP() && ((Token::Match(tok, "class|struct|union|namespace ::| %name% {|:|::|<") && !Token::Match(tok->previous(), "new|friend|const|enum|typedef|mutable|volatile|using|)|(|<")) || (Token::Match(tok, "enum class| %name% {") || Token::Match(tok, "enum class| %name% : %name% {")))) || (mTokenizer->isC() && Token::Match(tok, "struct|union|enum %name% {"))) { const Token *tok2 = tok->tokAt(2); if (tok->strAt(1) == "::") tok2 = tok2->next(); else if (mTokenizer->isCPP() && tok->strAt(1) == "class") tok2 = tok2->next(); while (Token::Match(tok2, ":: %name%")) tok2 = tok2->tokAt(2); while (Token::Match(tok2, "%name% :: %name%")) tok2 = tok2->tokAt(2); // skip over template args while (tok2 && tok2->str() == "<" && tok2->link()) { tok2 = tok2->link()->next(); while (Token::Match(tok2, ":: %name%")) tok2 = tok2->tokAt(2); } // skip over final if (mTokenizer->isCPP() && Token::simpleMatch(tok2, "final")) tok2 = tok2->next(); // make sure we have valid code if (!Token::Match(tok2, "{|:")) { // check for qualified variable if (tok2 && tok2->next()) { if (tok2->next()->str() == ";") tok = tok2->next(); else if (Token::simpleMatch(tok2->next(), "= {") && Token::simpleMatch(tok2->linkAt(2), "} ;")) tok = tok2->linkAt(2)->next(); else if (Token::Match(tok2->next(), "(|{") && tok2->next()->link()->strAt(1) == ";") tok = tok2->next()->link()->next(); // skip variable declaration else if (Token::Match(tok2, "*|&|>")) continue; else if (Token::Match(tok2, "%name% (") && mTokenizer->isFunctionHead(tok2->next(), "{;")) continue; else if (Token::Match(tok2, "%name% [")) continue; // skip template else if (Token::simpleMatch(tok2, ";") && Token::Match(tok->previous(), "template|> class|struct")) { tok = tok2; continue; } // forward declaration else if (Token::simpleMatch(tok2, ";") && Token::Match(tok, "class|struct|union")) { // TODO: see if it can be used tok = tok2; continue; } // skip constructor else if (Token::simpleMatch(tok2, "(") && Token::simpleMatch(tok2->link(), ") ;")) { tok = tok2->link()->next(); continue; } else throw InternalError(tok2, "SymbolDatabase bailout; unhandled code", InternalError::SYNTAX); continue; } break; // bail } const Token * name = tok->next(); if (name->str() == "class" && name->strAt(-1) == "enum") name = name->next(); Scope *new_scope = findScope(name, scope); if (new_scope) { // only create base list for classes and structures if (new_scope->isClassOrStruct()) { // goto initial '{' if (!new_scope->definedType) mTokenizer->syntaxError(nullptr); // #6808 tok2 = new_scope->definedType->initBaseInfo(tok, tok2); // make sure we have valid code if (!tok2) { break; } } // definition may be different than declaration if (mTokenizer->isCPP() && tok->str() == "class") { access[new_scope] = AccessControl::Private; new_scope->type = Scope::eClass; } else if (tok->str() == "struct") { access[new_scope] = AccessControl::Public; new_scope->type = Scope::eStruct; } new_scope->classDef = tok; new_scope->bodyStart = tok2; new_scope->bodyEnd = tok2->link(); // make sure we have valid code if (!new_scope->bodyEnd) { mTokenizer->syntaxError(tok); } scope = new_scope; tok = tok2; } else { scopeList.emplace_back(this, tok, scope); new_scope = &scopeList.back(); if (tok->str() == "class") access[new_scope] = AccessControl::Private; else if (tok->str() == "struct" || tok->str() == "union") access[new_scope] = AccessControl::Public; // fill typeList... if (new_scope->isClassOrStructOrUnion() || new_scope->type == Scope::eEnum) { Type* new_type = findType(name, scope); if (!new_type) { typeList.emplace_back(new_scope->classDef, new_scope, scope); new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; } else new_type->classScope = new_scope; new_scope->definedType = new_type; } // only create base list for classes and structures if (new_scope->isClassOrStruct()) { // goto initial '{' tok2 = new_scope->definedType->initBaseInfo(tok, tok2); // make sure we have valid code if (!tok2) { mTokenizer->syntaxError(tok); } } else if (new_scope->type == Scope::eEnum) { if (tok2->str() == ":") tok2 = tok2->tokAt(2); } new_scope->bodyStart = tok2; new_scope->bodyEnd = tok2->link(); // make sure we have valid code if (!new_scope->bodyEnd) { mTokenizer->syntaxError(tok); } if (new_scope->type == Scope::eEnum) { tok2 = new_scope->addEnum(tok, mTokenizer->isCPP()); scope->nestedList.push_back(new_scope); if (!tok2) mTokenizer->syntaxError(tok); } else { // make the new scope the current scope scope->nestedList.push_back(new_scope); scope = new_scope; } tok = tok2; } } // Namespace and unknown macro (#3854) else if (mTokenizer->isCPP() && Token::Match(tok, "namespace %name% %type% (") && tok->tokAt(2)->isUpperCaseName() && Token::simpleMatch(tok->linkAt(3), ") {")) { scopeList.emplace_back(this, tok, scope); Scope *new_scope = &scopeList.back(); access[new_scope] = AccessControl::Public; const Token *tok2 = tok->linkAt(3)->next(); new_scope->bodyStart = tok2; new_scope->bodyEnd = tok2->link(); // make sure we have valid code if (!new_scope->bodyEnd) { scopeList.pop_back(); break; } // make the new scope the current scope scope->nestedList.push_back(new_scope); scope = &scopeList.back(); tok = tok2; } // forward declaration else if (Token::Match(tok, "class|struct|union %name% ;") && tok->strAt(-1) != "friend") { if (!findType(tok->next(), scope)) { // fill typeList.. typeList.emplace_back(tok, nullptr, scope); Type* new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; } tok = tok->tokAt(2); } // using namespace else if (mTokenizer->isCPP() && Token::Match(tok, "using namespace ::| %type% ;|::")) { Scope::UsingInfo using_info; using_info.start = tok; // save location using_info.scope = findNamespace(tok->tokAt(2), scope); scope->usingList.push_back(using_info); // check for global namespace if (tok->strAt(2) == "::") tok = tok->tokAt(4); else tok = tok->tokAt(3); // skip over qualification while (Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); } // using type alias else if (mTokenizer->isCPP() && Token::Match(tok, "using %name% =")) { if (tok->strAt(-1) != ">" && !findType(tok->next(), scope)) { // fill typeList.. typeList.emplace_back(tok, nullptr, scope); Type* new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; } tok = tok->tokAt(3); while (tok && tok->str() != ";") tok = tok->next(); } // unnamed struct and union else if (Token::Match(tok, "struct|union {") && Token::Match(tok->next()->link(), "} *|&| %name% ;|[|=")) { scopeList.emplace_back(this, tok, scope); Scope *new_scope = &scopeList.back(); access[new_scope] = AccessControl::Public; const Token* varNameTok = tok->next()->link()->next(); if (varNameTok->str() == "*") { varNameTok = varNameTok->next(); } else if (varNameTok->str() == "&") { varNameTok = varNameTok->next(); } typeList.emplace_back(tok, new_scope, scope); { Type* new_type = &typeList.back(); new_scope->definedType = new_type; scope->definedTypesMap[new_type->name()] = new_type; } scope->addVariable(varNameTok, tok, tok, access[scope], new_scope->definedType, scope, mSettings); const Token *tok2 = tok->next(); new_scope->bodyStart = tok2; new_scope->bodyEnd = tok2->link(); // make sure we have valid code if (!new_scope->bodyEnd) { scopeList.pop_back(); break; } // make the new scope the current scope scope->nestedList.push_back(new_scope); scope = new_scope; tok = tok2; } // anonymous struct, union and namespace else if ((Token::Match(tok, "struct|union {") && Token::simpleMatch(tok->next()->link(), "} ;")) || Token::simpleMatch(tok, "namespace {")) { scopeList.emplace_back(this, tok, scope); Scope *new_scope = &scopeList.back(); access[new_scope] = AccessControl::Public; const Token *tok2 = tok->next(); new_scope->bodyStart = tok2; new_scope->bodyEnd = tok2->link(); typeList.emplace_back(tok, new_scope, scope); { Type* new_type = &typeList.back(); new_scope->definedType = new_type; scope->definedTypesMap[new_type->name()] = new_type; } // make sure we have valid code if (!new_scope->bodyEnd) { scopeList.pop_back(); break; } // make the new scope the current scope scope->nestedList.push_back(new_scope); scope = new_scope; tok = tok2; } // forward declared enum else if (Token::Match(tok, "enum class| %name% ;") || Token::Match(tok, "enum class| %name% : %name% ;")) { typeList.emplace_back(tok, nullptr, scope); Type* new_type = &typeList.back(); scope->definedTypesMap[new_type->name()] = new_type; tok = tok->tokAt(2); } // check for end of scope else if (tok == scope->bodyEnd) { access.erase(scope); scope = const_cast(scope->nestedIn); continue; } // check if in class or structure or union else if (scope->isClassOrStructOrUnion()) { const Token *funcStart = nullptr; const Token *argStart = nullptr; const Token *declEnd = nullptr; // What section are we in.. if (tok->str() == "private:") access[scope] = AccessControl::Private; else if (tok->str() == "protected:") access[scope] = AccessControl::Protected; else if (tok->str() == "public:" || tok->str() == "__published:") access[scope] = AccessControl::Public; else if (Token::Match(tok, "public|protected|private %name% :")) { if (tok->str() == "private") access[scope] = AccessControl::Private; else if (tok->str() == "protected") access[scope] = AccessControl::Protected; else access[scope] = AccessControl::Public; tok = tok->tokAt(2); } // class function? else if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) { if (tok->previous()->str() != "::" || tok->strAt(-2) == scope->className) { Function function(mTokenizer, tok, scope, funcStart, argStart); // save the access type function.access = access[scope]; const Token *end = function.argDef->link(); // count the number of constructors if (function.isConstructor()) scope->numConstructors++; // assume implementation is inline (definition and implementation same) function.token = function.tokenDef; function.arg = function.argDef; // out of line function if (const Token *endTok = mTokenizer->isFunctionHead(end, ";")) { tok = endTok; scope->addFunction(function); } // inline function else { // find start of function '{' bool foundInitList = false; while (end && end->str() != "{" && end->str() != ";") { if (end->link() && Token::Match(end, "(|<")) { end = end->link(); } else if (foundInitList && Token::Match(end, "%name%|> {") && Token::Match(end->linkAt(1), "} ,|{")) { end = end->linkAt(1); } else { if (end->str() == ":") foundInitList = true; end = end->next(); } } if (!end || end->str() == ";") continue; scope->addFunction(function); Function* funcptr = &scope->functionList.back(); const Token *tok2 = funcStart; addNewFunction(&scope, &tok2); if (scope) { scope->functionOf = function.nestedIn; scope->function = funcptr; scope->function->functionScope = scope; } tok = tok2; } } // nested class or friend function? else { /** @todo check entire qualification for match */ const Scope * const nested = scope->findInNestedListRecursive(tok->strAt(-2)); if (nested) addClassFunction(&scope, &tok, argStart); else { /** @todo handle friend functions */ } } } // friend class declaration? else if (mTokenizer->isCPP() && Token::Match(tok, "friend class| ::| %any% ;|::")) { Type::FriendInfo friendInfo; // save the name start friendInfo.nameStart = tok->strAt(1) == "class" ? tok->tokAt(2) : tok->next(); friendInfo.nameEnd = friendInfo.nameStart; // skip leading "::" if (friendInfo.nameEnd->str() == "::") friendInfo.nameEnd = friendInfo.nameEnd->next(); // skip qualification "name ::" while (friendInfo.nameEnd && friendInfo.nameEnd->strAt(1) == "::") friendInfo.nameEnd = friendInfo.nameEnd->tokAt(2); // fill this in after parsing is complete friendInfo.type = nullptr; if (!scope->definedType) mTokenizer->syntaxError(tok); scope->definedType->friendList.push_back(friendInfo); } } else if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) { const Token *funcStart = nullptr; const Token *argStart = nullptr; const Token *declEnd = nullptr; // function? if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) { // has body? if (declEnd && declEnd->str() == "{") { tok = funcStart; // class function if (tok->previous() && tok->previous()->str() == "::") addClassFunction(&scope, &tok, argStart); // class destructor else if (tok->previous() && tok->previous()->str() == "~" && tok->strAt(-2) == "::") addClassFunction(&scope, &tok, argStart); // regular function else { const Function* const function = addGlobalFunction(scope, tok, argStart, funcStart); if (!function) mTokenizer->syntaxError(tok); } // syntax error? if (!scope) mTokenizer->syntaxError(tok); } // function prototype? else if (declEnd && declEnd->str() == ";") { if (tok->previous() && tok->previous()->str() == "::" && Token::Match(declEnd->previous(), "default|delete")) { addClassFunction(&scope, &tok, argStart); continue; } bool newFunc = true; // Is this function already in the database? for (std::multimap::const_iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) { if (Function::argsMatch(scope, i->second->argDef, argStart, emptyString, 0)) { newFunc = false; break; } } // save function prototype in database if (newFunc) { addGlobalFunctionDecl(scope, tok, argStart, funcStart); } tok = declEnd; continue; } } } else if (scope->isExecutable()) { if (Token::Match(tok, "else|try|do {")) { const Token* tok1 = tok->next(); if (tok->str() == "else") scopeList.emplace_back(this, tok, scope, Scope::eElse, tok1); else if (tok->str() == "do") scopeList.emplace_back(this, tok, scope, Scope::eDo, tok1); else //if (tok->str() == "try") scopeList.emplace_back(this, tok, scope, Scope::eTry, tok1); tok = tok1; scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); } else if (Token::Match(tok, "if|for|while|catch|switch (") && Token::simpleMatch(tok->next()->link(), ") {")) { const Token *scopeStartTok = tok->next()->link()->next(); if (tok->str() == "if") scopeList.emplace_back(this, tok, scope, Scope::eIf, scopeStartTok); else if (tok->str() == "for") { scopeList.emplace_back(this, tok, scope, Scope::eFor, scopeStartTok); } else if (tok->str() == "while") scopeList.emplace_back(this, tok, scope, Scope::eWhile, scopeStartTok); else if (tok->str() == "catch") { scopeList.emplace_back(this, tok, scope, Scope::eCatch, scopeStartTok); } else // if (tok->str() == "switch") scopeList.emplace_back(this, tok, scope, Scope::eSwitch, scopeStartTok); scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); if (scope->type == Scope::eFor) scope->checkVariable(tok->tokAt(2), AccessControl::Local, mSettings); // check for variable declaration and add it to new scope if found else if (scope->type == Scope::eCatch) scope->checkVariable(tok->tokAt(2), AccessControl::Throw, mSettings); // check for variable declaration and add it to new scope if found tok = scopeStartTok; } else if (Token::Match(tok, "%var% {")) { tok = tok->linkAt(1); } else if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { const Token *lambdaStartToken = lambdaEndToken->link(); const Token * argStart = lambdaStartToken->astParent(); const Token * funcStart = Token::simpleMatch(argStart, "[") ? argStart : argStart->astParent(); const Function * function = addGlobalFunction(scope, tok, argStart, funcStart); if (!function) mTokenizer->syntaxError(tok); tok = lambdaStartToken; } else if (tok->str() == "{") { if (isExecutableScope(tok)) { scopeList.emplace_back(this, tok, scope, Scope::eUnconditional, tok); scope->nestedList.push_back(&scopeList.back()); scope = &scopeList.back(); } else { tok = tok->link(); } } // syntax error? if (!scope) mTokenizer->syntaxError(tok); } } } void SymbolDatabase::createSymbolDatabaseClassInfo() { if (mTokenizer->isC()) return; // fill in using info for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { for (std::list::iterator i = it->usingList.begin(); i != it->usingList.end(); ++i) { // only find if not already found if (i->scope == nullptr) { // check scope for match const Scope * const scope = findScope(i->start->tokAt(2), &(*it)); if (scope) { // set found scope i->scope = scope; break; } } } } // fill in base class info for (std::list::iterator it = typeList.begin(); it != typeList.end(); ++it) { // finish filling in base class info for (Type::BaseInfo & i : it->derivedFrom) { const Type* found = findType(i.nameTok, it->enclosingScope); if (found && found->findDependency(&(*it))) { // circular dependency //mTokenizer->syntaxError(nullptr); } else { i.type = found; } } } // fill in friend info for (std::list::iterator it = typeList.begin(); it != typeList.end(); ++it) { for (Type::FriendInfo &friendInfo : it->friendList) { friendInfo.type = findType(friendInfo.nameStart, it->enclosingScope); } } } void SymbolDatabase::createSymbolDatabaseVariableInfo() { // fill in variable info for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { // find variables it->getVariableList(mSettings); } // fill in function arguments for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { std::list::iterator func; for (func = it->functionList.begin(); func != it->functionList.end(); ++func) { // add arguments func->addArguments(this, &*it); } } } void SymbolDatabase::createSymbolDatabaseCopyAndMoveConstructors() { // fill in class and struct copy/move constructors for (std::list::iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) { if (!scope->isClassOrStruct()) continue; std::list::iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (!func->isConstructor() || func->minArgCount() != 1) continue; const Variable* firstArg = func->getArgumentVar(0); if (firstArg->type() == scope->definedType) { if (firstArg->isRValueReference()) func->type = Function::eMoveConstructor; else if (firstArg->isReference() && !firstArg->isPointer()) func->type = Function::eCopyConstructor; } if (func->type == Function::eCopyConstructor || func->type == Function::eMoveConstructor) scope->numCopyOrMoveConstructors++; } } } void SymbolDatabase::createSymbolDatabaseFunctionScopes() { // fill in function scopes for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->type == Scope::eFunction) functionScopes.push_back(&*it); } } void SymbolDatabase::createSymbolDatabaseClassAndStructScopes() { // fill in class and struct scopes for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->isClassOrStruct()) classAndStructScopes.push_back(&*it); } } void SymbolDatabase::createSymbolDatabaseFunctionReturnTypes() { // fill in function return types for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { std::list::iterator func; for (func = it->functionList.begin(); func != it->functionList.end(); ++func) { // add return types if (func->retDef) { const Token *type = func->retDef; while (Token::Match(type, "static|const|struct|union|enum")) type = type->next(); if (type) { func->retType = findVariableTypeInBase(&*it, type); if (!func->retType) func->retType = findTypeInNested(type, func->nestedIn); } } } } } void SymbolDatabase::createSymbolDatabaseNeedInitialization() { if (mTokenizer->isC()) { // For C code it is easy, as there are no constructors and no default values for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { Scope *scope = &(*it); if (scope->definedType) scope->definedType->needInitialization = Type::NeedInitialization::True; } } else { // For C++, it is more difficult: Determine if user defined type needs initialization... unsigned int unknowns = 0; // stop checking when there are no unknowns unsigned int retry = 0; // bail if we don't resolve all the variable types for some reason do { unknowns = 0; for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { Scope *scope = &(*it); if (!scope->definedType) { mBlankTypes.emplace_back(); scope->definedType = &mBlankTypes.back(); } if (scope->isClassOrStruct() && scope->definedType->needInitialization == Type::NeedInitialization::Unknown) { // check for default constructor bool hasDefaultConstructor = false; std::list::const_iterator func; for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { if (func->type == Function::eConstructor) { // check for no arguments: func ( ) if (func->argCount() == 0) { hasDefaultConstructor = true; break; } /** check for arguments with default values */ else if (func->argCount() == func->initializedArgCount()) { hasDefaultConstructor = true; break; } } } // User defined types with user defined default constructor doesn't need initialization. // We assume the default constructor initializes everything. // Another check will figure out if the constructor actually initializes everything. if (hasDefaultConstructor) scope->definedType->needInitialization = Type::NeedInitialization::False; // check each member variable to see if it needs initialization else { bool needInitialization = false; bool unknown = false; std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end() && !needInitialization; ++var) { if (var->isClass()) { if (var->type()) { // does this type need initialization? if (var->type()->needInitialization == Type::NeedInitialization::True) needInitialization = true; else if (var->type()->needInitialization == Type::NeedInitialization::Unknown) { if (!(var->valueType() && var->valueType()->type == ValueType::CONTAINER)) unknown = true; } } } else if (!var->hasDefault()) needInitialization = true; } if (needInitialization) scope->definedType->needInitialization = Type::NeedInitialization::True; else if (!unknown) scope->definedType->needInitialization = Type::NeedInitialization::False; else { if (scope->definedType->needInitialization == Type::NeedInitialization::Unknown) unknowns++; } } } else if (scope->type == Scope::eUnion && scope->definedType->needInitialization == Type::NeedInitialization::Unknown) scope->definedType->needInitialization = Type::NeedInitialization::True; } retry++; } while (unknowns && retry < 100); // this shouldn't happen so output a debug warning if (retry == 100 && mSettings->debugwarnings) { for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { const Scope *scope = &(*it); if (scope->isClassOrStruct() && scope->definedType->needInitialization == Type::NeedInitialization::Unknown) debugMessage(scope->classDef, "SymbolDatabase::SymbolDatabase couldn't resolve all user defined types."); } } } } void SymbolDatabase::createSymbolDatabaseVariableSymbolTable() { // create variable symbol table mVariableList.resize(mTokenizer->varIdCount() + 1); std::fill_n(mVariableList.begin(), mVariableList.size(), (const Variable*)nullptr); // check all scopes for variables for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { Scope *scope = &(*it); // add all variables for (std::list::iterator var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { const unsigned int varId = var->declarationId(); if (varId) mVariableList[varId] = &(*var); // fix up variables without type if (!var->type() && !var->typeStartToken()->isStandardType()) { const Type *type = findType(var->typeStartToken(), scope); if (type) var->type(type); } } // add all function parameters for (std::list::iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { for (std::list::iterator arg = func->argumentList.begin(); arg != func->argumentList.end(); ++arg) { // check for named parameters if (arg->nameToken() && arg->declarationId()) { const unsigned int declarationId = arg->declarationId(); if (declarationId > 0U) mVariableList[declarationId] = &(*arg); // fix up parameters without type if (!arg->type() && !arg->typeStartToken()->isStandardType()) { const Type *type = findTypeInNested(arg->typeStartToken(), scope); if (type) arg->type(type); } } } } } // fill in missing variables if possible const std::size_t functions = functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope *func = functionScopes[i]; for (const Token *tok = func->bodyStart->next(); tok && tok != func->bodyEnd; tok = tok->next()) { // check for member variable if (tok->varId() && tok->next() && (tok->next()->str() == "." || (tok->next()->str() == "[" && tok->linkAt(1)->strAt(1) == "."))) { const Token *tok1 = tok->next()->str() == "." ? tok->tokAt(2) : tok->linkAt(1)->tokAt(2); if (tok1 && tok1->varId() && mVariableList[tok1->varId()] == nullptr) { const Variable *var = mVariableList[tok->varId()]; if (var && var->typeScope()) { // find the member variable of this variable const Variable *var1 = var->typeScope()->getVariable(tok1->str()); if (var1) { // add this variable to the look up table mVariableList[tok1->varId()] = var1; } } } } } } } void SymbolDatabase::createSymbolDatabaseSetScopePointers() { // Set scope pointers for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { Token* start = const_cast(it->bodyStart); Token* end = const_cast(it->bodyEnd); if (it->type == Scope::eGlobal) { start = const_cast(mTokenizer->list.front()); end = const_cast(mTokenizer->list.back()); } assert(start); assert(end); end->scope(&*it); for (Token* tok = start; tok != end; tok = tok->next()) { if (start != end && tok->str() == "{") { bool isEndOfScope = false; for (std::list::const_iterator innerScope = it->nestedList.begin(); innerScope != it->nestedList.end(); ++innerScope) { if (tok == (*innerScope)->bodyStart) { // Is begin of inner scope tok = tok->link(); if (tok->next() == end || !tok->next()) { isEndOfScope = true; break; } tok = tok->next(); break; } } if (isEndOfScope) break; } tok->scope(&*it); } } } void SymbolDatabase::createSymbolDatabaseSetFunctionPointers(bool firstPass) { if (firstPass) { // Set function definition and declaration pointers for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { for (std::list::const_iterator func = it->functionList.begin(); func != it->functionList.end(); ++func) { if (func->tokenDef) const_cast(func->tokenDef)->function(&*func); if (func->token) const_cast(func->token)->function(&*func); } } } // Set function call pointers for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { if (!tok->function() && tok->varId() == 0 && Token::Match(tok, "%name% (") && !isReservedName(tok->str())) { const Function *function = findFunction(tok); if (function) const_cast(tok)->function(function); } } // Set C++ 11 delegate constructor function call pointers for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { for (std::list::const_iterator func = it->functionList.begin(); func != it->functionList.end(); ++func) { // look for initializer list if (func->isConstructor() && func->functionScope && func->functionScope->functionOf && func->arg) { const Token * tok = func->arg->link()->next(); if (tok->str() == "noexcept") { const Token * closingParenTok = tok->linkAt(1); if (!closingParenTok || !closingParenTok->next()) { continue; } tok = closingParenTok->next(); } if (tok->str() != ":") { continue; } tok = tok->next(); while (tok && tok != func->functionScope->bodyStart) { if (Token::Match(tok, "%name% {|(")) { if (tok->str() == func->tokenDef->str()) { const Function *function = func->functionScope->functionOf->findFunction(tok); if (function) const_cast(tok)->function(function); break; } tok = tok->linkAt(1); } tok = tok->next(); } } } } } void SymbolDatabase::createSymbolDatabaseSetTypePointers() { std::set typenames; for (const Type &t : typeList) { typenames.insert(t.name()); } // Set type pointers for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { if (!tok->isName() || tok->varId() || tok->function() || tok->type() || tok->enumerator()) continue; if (typenames.find(tok->str()) == typenames.end()) continue; const Type *type = findVariableType(tok->scope(), tok); if (type) const_cast(tok)->type(type); } } void SymbolDatabase::fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar) { VarIdMap::iterator varId = varIds.find(vartok->varId()); if (varId == varIds.end()) { MemberIdMap memberId; if (membertok->varId() == 0) { memberId[membervar->nameToken()->varId()] = const_cast(mTokenizer)->newVarId(); mVariableList.push_back(membervar); } else mVariableList[membertok->varId()] = membervar; varIds.insert(std::make_pair(vartok->varId(), memberId)); varId = varIds.find(vartok->varId()); } MemberIdMap::iterator memberId = varId->second.find(membervar->nameToken()->varId()); if (memberId == varId->second.end()) { if (membertok->varId() == 0) { varId->second.insert(std::make_pair(membervar->nameToken()->varId(), const_cast(mTokenizer)->newVarId())); mVariableList.push_back(membervar); memberId = varId->second.find(membervar->nameToken()->varId()); } else mVariableList[membertok->varId()] = membervar; } if (membertok->varId() == 0) membertok->varId(memberId->second); } void SymbolDatabase::createSymbolDatabaseSetVariablePointers() { VarIdMap varIds; // Set variable pointers for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { if (tok->varId()) const_cast(tok)->variable(getVariableFromVarId(tok->varId())); // Set Token::variable pointer for array member variable // Since it doesn't point at a fixed location it doesn't have varid if (tok->variable() != nullptr && (tok->variable()->typeScope() || tok->variable()->isSmartPointer() || (tok->valueType() && tok->valueType()->type == ValueType::CONTAINER)) && Token::Match(tok, "%name% [|.")) { Token *tok2 = tok->next(); // Locate "]" while (tok2 && tok2->str() == "[") tok2 = tok2->link()->next(); Token *membertok = nullptr; if (Token::Match(tok2, ". %name%")) membertok = tok2->next(); else if (Token::Match(tok2, ") . %name%") && tok->strAt(-1) == "(") membertok = tok2->tokAt(2); if (membertok) { const Variable *var = tok->variable(); if (var && var->typeScope()) { const Variable *membervar = var->typeScope()->getVariable(membertok->str()); if (membervar) { membertok->variable(membervar); if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) fixVarId(varIds, tok, const_cast(membertok), membervar); } } else if (const ::Type *type = var ? var->smartPointerType() : nullptr) { const Scope *classScope = type->classScope; const Variable *membervar = classScope ? classScope->getVariable(membertok->str()) : nullptr; if (membervar) { membertok->variable(membervar); if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) fixVarId(varIds, tok, const_cast(membertok), membervar); } } else if (var && tok->valueType() && tok->valueType()->type == ValueType::CONTAINER) { if (Token::Match(var->typeStartToken(), "std :: %type% < %type% *| *| >")) { const Type * type = var->typeStartToken()->tokAt(4)->type(); if (type && type->classScope && type->classScope->definedType) { const Variable *membervar = type->classScope->getVariable(membertok->str()); if (membervar) { membertok->variable(membervar); if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) fixVarId(varIds, tok, const_cast(membertok), membervar); } } } } } } // check for function returning record type // func(...).var // func(...)[...].var else if (tok->function() && tok->next()->str() == "(" && (Token::Match(tok->next()->link(), ") . %name% !!(") || (Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %name% !!(")))) { const Type *type = tok->function()->retType; if (type) { Token *membertok; if (tok->next()->link()->next()->str() == ".") membertok = tok->next()->link()->next()->next(); else membertok = tok->next()->link()->next()->link()->next()->next(); const Variable *membervar = membertok->variable(); if (!membervar) { if (type->classScope) { membervar = type->classScope->getVariable(membertok->str()); if (membervar) { membertok->variable(membervar); if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) { if (tok->function()->retDef) fixVarId(varIds, tok->function()->retDef, const_cast(membertok), membervar); } } } } } } } } void SymbolDatabase::createSymbolDatabaseEnums() { // fill in enumerators in enum for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->type != Scope::eEnum) continue; // add enumerators to enumerator tokens for (Enumerator & i : it->enumeratorList) const_cast(i.name)->enumerator(&i); } // fill in enumerator values for (std::list::iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->type != Scope::eEnum) continue; MathLib::bigint value = 0; for (Enumerator & enumerator : it->enumeratorList) { // look for initialization tokens that can be converted to enumerators and convert them if (enumerator.start) { if (!enumerator.end) mTokenizer->syntaxError(enumerator.start); for (const Token * tok3 = enumerator.start; tok3 && tok3 != enumerator.end->next(); tok3 = tok3->next()) { if (tok3->tokType() == Token::eName) { const Enumerator * e = findEnumerator(tok3); if (e) const_cast(tok3)->enumerator(e); } } // look for possible constant folding expressions // rhs of operator: Token *rhs = enumerator.start->previous()->astOperand2(); // constant folding of expression: ValueFlow::valueFlowConstantFoldAST(rhs, mSettings); // get constant folded value: if (rhs && rhs->hasKnownIntValue()) { enumerator.value = rhs->values().front().intvalue; enumerator.value_known = true; value = enumerator.value + 1; } } // not initialized so use default value else { enumerator.value = value++; enumerator.value_known = true; } } } // find enumerators for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { if (tok->tokType() != Token::eName) continue; const Enumerator * enumerator = findEnumerator(tok); if (enumerator) const_cast(tok)->enumerator(enumerator); } } void SymbolDatabase::createSymbolDatabaseIncompleteVars() { const std::set cpp20keywords = { "alignas", "alignof", "axiom", "co_await", "co_return", "co_yield", "concept", "synchronized", "consteval", "reflexpr", "requires", }; const std::set cppkeywords = { "asm", "auto", "catch", "char", "class", "const", "constexpr", "decltype", "default", "do", "enum", "explicit", "export", "extern", "final", "friend", "inline", "mutable", "namespace", "new", "noexcept", "nullptr", "override", "private", "protected", "public", "register", "sizeof", "static", "static_assert", "struct", "template", "this", "thread_local", "throw", "try", "typedef", "typeid", "typename", "union", "using", "virtual", "void", "volatile", "NULL", }; for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { const Scope * scope = tok->scope(); if (!scope) continue; if (!scope->isExecutable()) continue; if (!Token::Match(tok, "%name%")) continue; if (!tok->isNameOnly()) continue; if (Token::Match(tok, "%var%")) continue; if (tok->type()) continue; if (Token::Match(tok->next(), "::|.|(|:|%var%")) continue; if (Token::Match(tok->next(), "&|&&|* )|%var%")) continue; if (Token::simpleMatch(tok->next(), ")") && Token::simpleMatch(tok->next()->link()->previous(), "catch (")) continue; // Very likely a typelist if (Token::Match(tok->tokAt(-2), "%type% ,")) continue; // Inside template brackets if (Token::Match(tok->next(), "<|>") && tok->next()->link()) continue; if (Token::simpleMatch(tok->previous(), "<") && tok->previous()->link()) continue; // Skip goto labels if (Token::simpleMatch(tok->previous(), "goto")) continue; if (cppkeywords.count(tok->str()) > 0) continue; if (mSettings->standards.cpp >= Standards::CPP20 && cpp20keywords.count(tok->str()) > 0) continue; const_cast(tok)->isIncompleteVar(true); } } void SymbolDatabase::createSymbolDatabaseEscapeFunctions() { for (Scope & scope : scopeList) { if (scope.type != Scope::eFunction) continue; Function * function = scope.function; if (!function) continue; function->isEscapeFunction(isReturnScope(scope.bodyEnd, &mSettings->library, true)); } } void SymbolDatabase::setArrayDimensionsUsingValueFlow() { // set all unknown array dimensions for (const Variable *var : mVariableList) { // check each array variable if (!var || !var->isArray()) continue; // check each array dimension for (const Dimension &const_dimension : var->dimensions()) { Dimension &dimension = const_cast(const_dimension); if (dimension.num != 0 || !dimension.tok) continue; dimension.known = false; // check for a single token dimension if (dimension.tok->hasKnownIntValue()) { dimension.known = true; dimension.num = dimension.tok->getKnownIntValue(); continue; } else if (dimension.tok->valueType() && dimension.tok->valueType()->pointer == 0) { int bits = 0; switch (dimension.tok->valueType()->type) { case ValueType::Type::CHAR: bits = mSettings->char_bit; break; case ValueType::Type::SHORT: bits = mSettings->short_bit; break; case ValueType::Type::INT: bits = mSettings->int_bit; break; case ValueType::Type::LONG: bits = mSettings->long_bit; break; case ValueType::Type::LONGLONG: bits = mSettings->long_long_bit; break; default: break; }; if (bits > 0 && bits <= 62) { if (dimension.tok->valueType()->sign == ValueType::Sign::UNSIGNED) dimension.num = 1LL << bits; else dimension.num = 1LL << (bits - 1); } } } } } SymbolDatabase::~SymbolDatabase() { // Clear scope, type, function and variable pointers for (const Token* tok = mTokenizer->list.front(); tok; tok = tok->next()) { const_cast(tok)->scope(nullptr); const_cast(tok)->type(nullptr); const_cast(tok)->function(nullptr); const_cast(tok)->variable(nullptr); const_cast(tok)->enumerator(nullptr); const_cast(tok)->setValueType(nullptr); } } bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart, const Token** declEnd) const { if (tok->varId()) return false; // function returning function pointer? '... ( ... %name% ( ... ))( ... ) {' // function returning reference to array '... ( & %name% ( ... ))[ ... ] {' // TODO: Activate this again if (false && tok->str() == "(" && tok->strAt(1) != "*" && (tok->link()->previous()->str() == ")" || Token::simpleMatch(tok->link()->tokAt(-2), ") const"))) { const Token* tok2 = tok->link()->next(); if (tok2 && tok2->str() == "(" && Token::Match(tok2->link()->next(), "{|;|const|=")) { const Token* argStartTok; if (tok->link()->previous()->str() == "const") argStartTok = tok->link()->linkAt(-2); else argStartTok = tok->link()->linkAt(-1); *funcStart = argStartTok->previous(); *argStart = argStartTok; *declEnd = Token::findmatch(tok2->link()->next(), "{|;"); return true; } else if (tok2 && tok2->str() == "[") { while (tok2 && tok2->str() == "[") tok2 = tok2->link()->next(); if (Token::Match(tok2, "{|;|const|=")) { const Token* argStartTok; if (tok->link()->previous()->str() == "const") argStartTok = tok->link()->linkAt(-2); else argStartTok = tok->link()->linkAt(-1); *funcStart = argStartTok->previous(); *argStart = argStartTok; *declEnd = Token::findmatch(tok2, "{|;"); return true; } } } // regular function? else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) && tok->previous() && (Token::Match(tok->previous(), "%name%|>|&|*|::|~") || // Either a return type or scope qualifier in front of tok outerScope->isClassOrStructOrUnion())) { // or a ctor/dtor const Token* tok1 = tok->previous(); const Token* tok2 = tok->next()->link()->next(); if (!mTokenizer->isFunctionHead(tok->next(), ";:{")) return false; // skip over destructor "~" if (tok1->str() == "~") tok1 = tok1->previous(); // skip over qualification while (Token::simpleMatch(tok1, "::")) { tok1 = tok1->previous(); if (Token::Match(tok1, "%name%")) tok1 = tok1->previous(); else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%")) tok1 = tok1->link()->tokAt(-2); } // skip over const, noexcept, throw, override, final and volatile specifiers while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile|&|&&")) { tok2 = tok2->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link()->next(); } // skip over trailing return type if (tok2 && tok2->str() == ".") { for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, ";|{|=|override|final")) break; if (tok2->link() && Token::Match(tok2, "<|[|(")) tok2 = tok2->link(); } } // done if constructor or destructor if (!Token::Match(tok1, "{|}|;|public:|protected:|private:") && tok1) { // skip over pointers and references while (Token::Match(tok1, "%type%|*|&") && !endsWith(tok1->str(), ':') && (!isReservedName(tok1->str()) || tok1->str() == "const")) tok1 = tok1->previous(); // skip over template if (tok1 && tok1->str() == ">") { if (tok1->link()) tok1 = tok1->link()->previous(); else return false; } // function can't have number or variable as return type if (tok1 && (tok1->isNumber() || tok1->varId())) return false; // skip over return type if (Token::Match(tok1, "%name%")) { if (tok1->str() == "return") return false; tok1 = tok1->previous(); } // skip over qualification while (Token::simpleMatch(tok1, "::")) { tok1 = tok1->previous(); if (Token::Match(tok1, "%name%")) tok1 = tok1->previous(); else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%")) tok1 = tok1->link()->tokAt(-2); } // skip over modifiers and other stuff while (Token::Match(tok1, "const|static|extern|template|virtual|struct|class|enum|%name%")) { // friend type func(); is not a function if (isCPP() && tok1->str() == "friend" && tok2->str() == ";") return false; tok1 = tok1->previous(); } // should be at a sequence point if this is a function if (!Token::Match(tok1, ">|{|}|;|public:|protected:|private:") && tok1) return false; } if (tok2 && (Token::Match(tok2, ";|{|=") || (tok2->isUpperCaseName() && Token::Match(tok2, "%name% ;|{")) || (tok2->isUpperCaseName() && Token::Match(tok2, "%name% (") && tok2->next()->link()->strAt(1) == "{") || Token::Match(tok2, ": ::| %name% (|::|<|{") || Token::Match(tok2, "&|&&| ;|{") || Token::Match(tok2, "= delete|default ;"))) { *funcStart = tok; *argStart = tok->next(); *declEnd = Token::findmatch(tok2, "{|;"); return true; } } // UNKNOWN_MACRO(a,b) { ... } else if (outerScope->type == Scope::eGlobal && Token::Match(tok, "%name% (") && tok->isUpperCaseName() && Token::simpleMatch(tok->linkAt(1), ") {") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { *funcStart = tok; *argStart = tok->next(); *declEnd = tok->linkAt(1)->next(); return true; } // template constructor? else if (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->next()->link(), "> (")) { const Token* tok2 = tok->next()->link()->next()->link(); if (Token::Match(tok2, ") const| ;|{|=") || Token::Match(tok2, ") : ::| %name% (|::|<|{") || Token::Match(tok2, ") const| noexcept {|;|(")) { *funcStart = tok; *argStart = tok2->link(); *declEnd = Token::findmatch(tok2->next(), "{|;"); return true; } } // regular C function with missing return or invalid C++ ? else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) && Token::simpleMatch(tok->linkAt(1), ") {") && (!tok->previous() || Token::Match(tok->previous(), ";|}"))) { if (mTokenizer->isC()) { debugMessage(tok, "SymbolDatabase::isFunction found C function '" + tok->str() + "' without a return type."); *funcStart = tok; *argStart = tok->next(); *declEnd = tok->linkAt(1)->next(); return true; } mTokenizer->syntaxError(tok); } return false; } void SymbolDatabase::validateExecutableScopes() const { const std::size_t functions = functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope* const scope = functionScopes[i]; const Function* const function = scope->function; if (scope->isExecutable() && !function) { const std::list callstack(1, scope->classDef); const std::string msg = std::string("Executable scope '") + scope->classDef->str() + "' with unknown function."; const ErrorLogger::ErrorMessage errmsg(callstack, &mTokenizer->list, Severity::debug, "symbolDatabaseWarning", msg, false); mErrorLogger->reportErr(errmsg); } } } namespace { const Function* getFunctionForArgumentvariable(const Variable * const var, const std::vector& functionScopes) { const std::size_t functions = functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope* const scope = functionScopes[i]; const Function* const function = scope->function; if (function) { for (std::size_t arg=0; arg < function->argCount(); ++arg) { if (var==function->getArgumentVar(arg)) return function; } } } return nullptr; } } void SymbolDatabase::validateVariables() const { for (std::vector::const_iterator iter = mVariableList.begin(); iter!=mVariableList.end(); ++iter) { const Variable * const var = *iter; if (var) { if (!var->scope()) { const Function* function = getFunctionForArgumentvariable(var, functionScopes); if (!var->isArgument() || (function && function->hasBody())) { throw InternalError(var->nameToken(), "Analysis failed (variable without scope). If the code is valid then please report this failure.", InternalError::INTERNAL); //std::cout << "!!!Variable found without scope: " << var->nameToken()->str() << std::endl; } } } } } void SymbolDatabase::validate() const { if (mSettings->debugwarnings) { validateExecutableScopes(); } //validateVariables(); } Variable::~Variable() { delete mValueType; } bool Variable::isPointerArray() const { return isArray() && nameToken() && nameToken()->previous() && (nameToken()->previous()->str() == "*"); } const Token * Variable::declEndToken() const { Token const * declEnd = typeStartToken(); while (declEnd && !Token::Match(declEnd, "[;,)={]")) { if (declEnd->link() && Token::Match(declEnd,"(|[")) declEnd = declEnd->link(); declEnd = declEnd->next(); } return declEnd; } void Variable::evaluate(const Settings* settings) { const Library * const lib = settings ? &settings->library : nullptr; if (mNameToken) setFlag(fIsArray, arrayDimensions(settings)); if (mTypeStartToken) setValueType(ValueType::parseDecl(mTypeStartToken,settings)); const Token* tok = mTypeStartToken; while (tok && tok->previous() && tok->previous()->isName()) tok = tok->previous(); const Token* end = mTypeEndToken; if (end) end = end->next(); while (tok != end) { if (tok->str() == "static") setFlag(fIsStatic, true); else if (tok->str() == "extern") setFlag(fIsExtern, true); else if (tok->str() == "volatile") setFlag(fIsVolatile, true); else if (tok->str() == "mutable") setFlag(fIsMutable, true); else if (tok->str() == "const") setFlag(fIsConst, true); else if (tok->str() == "*") { setFlag(fIsPointer, !isArray() || Token::Match(tok->previous(), "( * %name% )")); setFlag(fIsConst, false); // Points to const, isn't necessarily const itself } else if (tok->str() == "&") { if (isReference()) setFlag(fIsRValueRef, true); setFlag(fIsReference, true); } else if (tok->str() == "&&") { // Before simplification, && isn't split up setFlag(fIsRValueRef, true); setFlag(fIsReference, true); // Set also fIsReference } if (tok->str() == "<" && tok->link()) tok = tok->link(); else tok = tok->next(); } while (Token::Match(mTypeStartToken, "static|const|volatile %any%")) mTypeStartToken = mTypeStartToken->next(); while (mTypeEndToken && mTypeEndToken->previous() && Token::Match(mTypeEndToken, "const|volatile")) mTypeEndToken = mTypeEndToken->previous(); if (mTypeStartToken) { std::string strtype = mTypeStartToken->str(); for (const Token *typeToken = mTypeStartToken; Token::Match(typeToken, "%type% :: %type%"); typeToken = typeToken->tokAt(2)) strtype += "::" + typeToken->strAt(2); setFlag(fIsClass, !lib->podtype(strtype) && !mTypeStartToken->isStandardType() && !isEnumType() && !isPointer() && !isReference() && strtype != "..."); setFlag(fIsStlType, Token::simpleMatch(mTypeStartToken, "std ::")); setFlag(fIsStlString, isStlType() && (Token::Match(mTypeStartToken->tokAt(2), "string|wstring|u16string|u32string !!::") || (Token::simpleMatch(mTypeStartToken->tokAt(2), "basic_string <") && !Token::simpleMatch(mTypeStartToken->linkAt(3), "> ::")))); setFlag(fIsSmartPointer, lib->isSmartPointer(mTypeStartToken)); } if (mAccess == AccessControl::Argument) { tok = mNameToken; if (!tok) { // Argument without name tok = mTypeEndToken; // back up to start of array dimensions while (tok && tok->str() == "]") tok = tok->link()->previous(); // add array dimensions if present if (tok && tok->next()->str() == "[") setFlag(fIsArray, arrayDimensions(settings)); } if (!tok) return; tok = tok->next(); while (tok->str() == "[") tok = tok->link(); setFlag(fHasDefault, tok->str() == "="); } // check for C++11 member initialization if (mScope && mScope->isClassOrStruct()) { // type var = x or // type var = {x} // type var = x; gets simplified to: type var ; var = x ; Token const * declEnd = declEndToken(); if ((Token::Match(declEnd, "; %name% =") && declEnd->strAt(1) == mNameToken->str()) || Token::Match(declEnd, "=|{")) setFlag(fHasDefault, true); } if (mTypeStartToken) { if (Token::Match(mTypeStartToken, "float|double")) setFlag(fIsFloatType, true); } } void Variable::setValueType(const ValueType &valueType) { delete mValueType; mValueType = new ValueType(valueType); if ((mValueType->pointer > 0) && (!isArray() || Token::Match(mNameToken->previous(), "( * %name% )"))) setFlag(fIsPointer, true); setFlag(fIsConst, mValueType->constness & (1U << mValueType->pointer)); if (mValueType->smartPointerType) setFlag(fIsSmartPointer, true); } const Type *Variable::smartPointerType() const { if (!isSmartPointer()) return nullptr; if (mValueType->smartPointerType) return mValueType->smartPointerType; // TODO: Cache result const Token *ptrType = typeStartToken(); while (Token::Match(ptrType, "%name%|::")) ptrType = ptrType->next(); if (Token::Match(ptrType, "< %name% >")) return ptrType->scope()->findType(ptrType->next()->str()); return nullptr; } std::string Variable::getTypeName() const { std::string ret; // TODO: For known types, generate the full type name for (const Token *typeTok = mTypeStartToken; Token::Match(typeTok, "%name%|::") && typeTok->varId() == 0; typeTok = typeTok->next()) ret += typeTok->str(); return ret; } Function::Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *scope, const Token *tokDef, const Token *tokArgDef) : tokenDef(tokDef), argDef(tokArgDef), token(nullptr), arg(nullptr), retDef(nullptr), retType(nullptr), functionScope(nullptr), nestedIn(scope), initArgCount(0), type(eFunction), access(AccessControl::Public), noexceptArg(nullptr), throwArg(nullptr), templateDef(nullptr), mFlags(0) { // operator function if (tokenDef->isOperatorKeyword()) { isOperator(true); // 'operator =' is special if (tokenDef->str() == "operator=") type = Function::eOperatorEqual; } else if (tokenDef->str() == "[") { type = Function::eLambda; } // class constructor/destructor else if (tokenDef->str() == scope->className && scope->type != Scope::ScopeType::eNamespace) { // destructor if (tokenDef->previous()->str() == "~") type = Function::eDestructor; // constructor of any kind else type = Function::eConstructor; isExplicit(tokenDef->previous()->str() == "explicit"); } const Token *tok1 = tok; // look for end of previous statement while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{|public:|protected:|private:")) { tok1 = tok1->previous(); // extern function if (tok1->str() == "extern") { isExtern(true); } // virtual function else if (tok1->str() == "virtual") { hasVirtualSpecifier(true); } // static function else if (tok1->str() == "static") { isStatic(true); if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) isStaticLocal(true); } // friend function else if (tok1->str() == "friend") { isFriend(true); } // Function template else if (tok1->link() && tok1->str() == ">" && Token::simpleMatch(tok1->link()->previous(), "template <")) { templateDef = tok1->link()->previous(); break; } } // find the return type if (!isConstructor() && !isDestructor() && !isLambda()) { // @todo auto type deduction should be checked // @todo attributes and exception specification can also precede trailing return type if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type hasTrailingReturnType(true); if (argDef->link()->strAt(1) == ".") retDef = argDef->link()->tokAt(2); else if (argDef->link()->strAt(2) == ".") retDef = argDef->link()->tokAt(3); else if (argDef->link()->strAt(3) == ".") retDef = argDef->link()->tokAt(4); } else { if (tok1->str() == ">") tok1 = tok1->next(); while (Token::Match(tok1, "extern|virtual|static|friend|struct|union|enum")) tok1 = tok1->next(); retDef = tok1; } } const Token *end = argDef->link(); // parse function attributes.. tok = end->next(); while (tok) { if (tok->str() == "const") isConst(true); else if (tok->str() == "&") hasLvalRefQualifier(true); else if (tok->str() == "&&") hasRvalRefQualifier(true); else if (tok->str() == "override") setFlag(fHasOverrideSpecifier, true); else if (tok->str() == "final") setFlag(fHasFinalSpecifier, true); else if (tok->str() == "volatile") isVolatile(true); else if (tok->str() == "noexcept") { isNoExcept(!Token::simpleMatch(tok->next(), "( false )")); if (tok->next()->str() == "(") tok = tok->linkAt(1); } else if (Token::simpleMatch(tok, "throw (")) { isThrow(true); if (tok->strAt(2) != ")") throwArg = tok->next(); tok = tok->linkAt(1); } else if (Token::Match(tok, "= 0|default|delete ;")) { const std::string& modifier = tok->strAt(1); isPure(modifier == "0"); isDefault(modifier == "default"); isDelete(modifier == "delete"); } else if (tok->str() == ".") { // trailing return type // skip over return type while (tok && !Token::Match(tok->next(), ";|{|override|final")) tok = tok->next(); } else break; if (tok) tok = tok->next(); } if (mTokenizer->isFunctionHead(end, ":{")) { // assume implementation is inline (definition and implementation same) token = tokenDef; arg = argDef; isInline(true); hasBody(true); } } static std::string qualifiedName(const Scope *scope) { std::string name = scope->className; while (scope->nestedIn) { if (!scope->nestedIn->className.empty()) name = (scope->nestedIn->className + " :: ") + name; scope = scope->nestedIn; } return name; } static bool usingNamespace(const Scope *scope, const Token *first, const Token *second, int &offset) { offset = 0; std::string name = first->str(); while (Token::Match(first, "%type% :: %type%")) { if (offset) name += (" :: " + first->str()); offset += 2; first = first->tokAt(2); if (first->str() == second->str()) { break; } } if (offset) { while (scope) { for (const auto & info : scope->usingList) { if (info.scope) { if (name == qualifiedName(info.scope)) return true; } // no scope so get name from using else { const Token *start = info.start->tokAt(2); std::string nsName; while (start && start->str() != ";") { if (!nsName.empty()) nsName += " "; nsName += start->str(); start = start->next(); } if (nsName == name) return true; } } scope = scope->nestedIn; } } return false; } bool Function::argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) { const bool isCPP = scope->check->isCPP(); if (!isCPP) // C does not support overloads return true; int arg_path_length = path_length; int offset = 0; while (first->str() == second->str() && first->isLong() == second->isLong() && first->isUnsigned() == second->isUnsigned()) { // skip optional type information if (Token::Match(first->next(), "struct|enum|union|class")) first = first->next(); if (Token::Match(second->next(), "struct|enum|union|class")) second = second->next(); // skip const on type passed by value if (Token::Match(first->next(), "const %type% %name%|,|)") && !Token::Match(first->next(), "const %type% %name%| [")) first = first->next(); if (Token::Match(second->next(), "const %type% %name%|,|)") && !Token::Match(second->next(), "const %type% %name%| [")) second = second->next(); // at end of argument list if (first->str() == ")") { return true; } // skip default value assignment else if (first->next()->str() == "=") { first = first->nextArgument(); if (first) first = first->tokAt(-2); if (second->next()->str() == "=") { second = second->nextArgument(); if (second) second = second->tokAt(-2); if (!first || !second) { // End of argument list (first or second) return !first && !second; } } else if (!first) { // End of argument list (first) return !second->nextArgument(); // End of argument list (second) } } else if (second->next()->str() == "=") { second = second->nextArgument(); if (second) second = second->tokAt(-2); if (!second) { // End of argument list (second) return false; } } // definition missing variable name else if ((first->next()->str() == "," && second->next()->str() != ",") || (Token::Match(first, "!!( )") && second->next()->str() != ")")) { second = second->next(); // skip default value assignment if (second->next()->str() == "=") { do { second = second->next(); } while (!Token::Match(second->next(), ",|)")); } } else if (first->next()->str() == "[" && second->next()->str() != "[") second = second->next(); // function missing variable name else if ((second->next()->str() == "," && first->next()->str() != ",") || (Token::Match(second, "!!( )") && first->next()->str() != ")")) { first = first->next(); // skip default value assignment if (first->next()->str() == "=") { do { first = first->next(); } while (!Token::Match(first->next(), ",|)")); } } else if (second->next()->str() == "[" && first->next()->str() != "[") first = first->next(); // argument list has different number of arguments else if (second->str() == ")") break; // ckeck for type * x == type x[] else if (Token::Match(first->next(), "* %name%| ,|)|=") && Token::Match(second->next(), "%name%| [ ] ,|)")) { do { first = first->next(); } while (!Token::Match(first->next(), ",|)")); do { second = second->next(); } while (!Token::Match(second->next(), ",|)")); } // const after * else if (first->next()->str() == "*" && first->strAt(2) != "const" && second->next()->str() == "*" && second->strAt(2) == "const") { first = first->next(); second = second->tokAt(2); } // variable names are different else if ((Token::Match(first->next(), "%name% ,|)|=|[") && Token::Match(second->next(), "%name% ,|)|[")) && (first->next()->str() != second->next()->str())) { // skip variable names first = first->next(); second = second->next(); // skip default value assignment if (first->next()->str() == "=") { do { first = first->next(); } while (!Token::Match(first->next(), ",|)")); } } // using namespace else if (usingNamespace(scope, first->next(), second->next(), offset)) first = first->tokAt(offset); // variable with class path else if (arg_path_length && Token::Match(first->next(), "%name%") && first->strAt(1) != "const") { std::string param = path; if (Token::simpleMatch(second->next(), param.c_str())) { second = second->tokAt(int(arg_path_length)); arg_path_length = 0; } // nested or base class variable else if (arg_path_length <= 2 && Token::Match(first->next(), "%name%") && (Token::Match(second->next(), "%name% :: %name%") || (Token::Match(second->next(), "%name% <") && Token::Match(second->linkAt(1), "> :: %name%"))) && ((second->next()->str() == scope->className) || (scope->definedType && scope->definedType->isDerivedFrom(second->next()->str()))) && (first->next()->str() == second->strAt(3))) { if (Token::Match(second->next(), "%name% <")) second = second->linkAt(1)->next(); else second = second->tokAt(2); } // remove class name else if (arg_path_length > 2 && first->strAt(1) != second->strAt(1)) { std::string short_path = path; unsigned int short_path_length = arg_path_length; // remove last " :: " short_path.resize(short_path.size() - 4); short_path_length--; // remove last name std::string::size_type lastSpace = short_path.find_last_of(' '); if (lastSpace != std::string::npos) { short_path.resize(lastSpace+1); short_path_length--; if (short_path[short_path.size() - 1] == '>') { short_path.resize(short_path.size() - 3); while (short_path[short_path.size() - 1] == '<') { lastSpace = short_path.find_last_of(' '); short_path.resize(lastSpace+1); short_path_length--; } } } param = short_path; if (Token::simpleMatch(second->next(), param.c_str())) { second = second->tokAt(int(short_path_length)); arg_path_length = 0; } } } first = first->next(); second = second->next(); // reset path length if (first->str() == "," || second->str() == ",") arg_path_length = path_length; } return false; } bool Function::returnsReference(const Function* function, bool unknown) { if (!function) return false; if (function->type != Function::eFunction) return false; const Token* defEnd = function->returnDefEnd(); if (defEnd->strAt(-1) == "&") return true; // Check for unknown types, which could be a reference const Token* start = function->retDef; while (Token::Match(start, "const|volatile")) start = start->next(); if (start->tokAt(1) == defEnd && !start->type() && !start->isStandardType()) return unknown; // TODO: Try to deduce the type of the expression if (Token::Match(start, "decltype|typeof")) return unknown; return false; } const Token * Function::constructorMemberInitialization() const { if (!isConstructor() || !functionScope || !functionScope->bodyStart) return nullptr; if (Token::Match(token, "%name% (") && Token::simpleMatch(token->linkAt(1), ") :")) return token->linkAt(1)->next(); return nullptr; } bool Function::isSafe(const Settings *settings) const { if (settings->safeChecks.externalFunctions) { if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() != 0) return true; if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() != 0 || !isStatic())) return true; } if (settings->safeChecks.internalFunctions) { if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() == 0) return true; if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() == 0 || isStatic())) return true; } if (settings->safeChecks.classes && access == AccessControl::Public && (nestedIn->type == Scope::ScopeType::eClass || nestedIn->type == Scope::ScopeType::eStruct)) return true; return false; } Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart) { Function* function = nullptr; // Lambda functions are always unique if (tok->str() != "[") { for (std::multimap::iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) { const Function *f = i->second; if (f->hasBody()) continue; if (Function::argsMatch(scope, f->argDef, argStart, emptyString, 0)) { function = const_cast(i->second); break; } } } if (!function) function = addGlobalFunctionDecl(scope, tok, argStart, funcStart); function->arg = argStart; function->token = funcStart; function->hasBody(true); addNewFunction(&scope, &tok); if (scope) { scope->function = function; function->functionScope = scope; return function; } return nullptr; } Function* SymbolDatabase::addGlobalFunctionDecl(Scope*& scope, const Token *tok, const Token *argStart, const Token* funcStart) { Function function(mTokenizer, tok, scope, funcStart, argStart); scope->addFunction(function); return &scope->functionList.back(); } void SymbolDatabase::addClassFunction(Scope **scope, const Token **tok, const Token *argStart) { const bool destructor((*tok)->previous()->str() == "~"); const bool has_const(argStart->link()->strAt(1) == "const"); const bool lval(argStart->link()->strAt(has_const ? 2 : 1) == "&"); const bool rval(argStart->link()->strAt(has_const ? 2 : 1) == "&&"); int count = 0; std::string path; unsigned int path_length = 0; const Token *tok1 = (*tok); if (destructor) tok1 = tok1->previous(); // back up to head of path while (tok1 && tok1->previous() && tok1->previous()->str() == "::" && tok1->tokAt(-2) && (tok1->tokAt(-2)->isName() || (tok1->strAt(-2) == ">" && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->previous(), "%name%")))) { count++; const Token * tok2 = tok1->tokAt(-2); if (tok2->str() == ">") tok2 = tok2->link()->previous(); if (tok2) { do { path = tok1->previous()->str() + " " + path; tok1 = tok1->previous(); path_length++; } while (tok1 != tok2); } else return; // syntax error ? } // syntax error? if (!tok1) return; std::list::iterator it1; // search for match for (it1 = scopeList.begin(); it1 != scopeList.end(); ++it1) { Scope *scope1 = &(*it1); bool match = false; // check in namespace if using found if (*scope == scope1 && !scope1->usingList.empty()) { std::list::const_iterator it2; for (it2 = scope1->usingList.begin(); it2 != scope1->usingList.end(); ++it2) { if (it2->scope) { Function * func = findFunctionInScope(tok1, it2->scope, path, path_length); if (func) { if (!func->hasBody()) { const Token *closeParen = (*tok)->next()->link(); if (closeParen) { if (Token::Match(closeParen, ") noexcept| = default ;") || (Token::simpleMatch(closeParen, ") noexcept (") && Token::simpleMatch(closeParen->linkAt(2), ") = default ;"))) { func->isDefault(true); return; } } func->hasBody(true); func->token = *tok; func->arg = argStart; addNewFunction(scope, tok); if (*scope) { (*scope)->functionOf = func->nestedIn; (*scope)->function = func; (*scope)->function->functionScope = *scope; } return; } } } } } if (scope1->className == tok1->str() && (scope1->type != Scope::eFunction)) { // do the scopes match (same scope) or do their names match (multiple namespaces) if ((*scope == scope1->nestedIn) || (*scope && (*scope)->className == scope1->nestedIn->className && !(*scope)->className.empty() && (*scope)->type == scope1->nestedIn->type)) { // nested scopes => check that they match { const Scope *s1 = *scope; const Scope *s2 = scope1->nestedIn; while (s1 && s2) { if (s1->className != s2->className) break; s1 = s1->nestedIn; s2 = s2->nestedIn; } // Not matching scopes if (s1 || s2) continue; } Scope *scope2 = scope1; while (scope2 && count > 1) { count--; if (tok1->strAt(1) == "<") tok1 = tok1->linkAt(1)->tokAt(2); else tok1 = tok1->tokAt(2); scope2 = scope2->findRecordInNestedList(tok1->str()); } if (count == 1 && scope2) { match = true; scope1 = scope2; } } } if (match) { for (std::multimap::iterator it = scope1->functionMap.find((*tok)->str()); it != scope1->functionMap.end() && it->first == (*tok)->str(); ++it) { Function * func = const_cast(it->second); if (!func->hasBody()) { if (Function::argsMatch(scope1, func->argDef, (*tok)->next(), path, path_length)) { if (func->type == Function::eDestructor && destructor) { func->hasBody(true); } else if (func->type != Function::eDestructor && !destructor) { // normal function? const Token *closeParen = (*tok)->next()->link(); if (closeParen) { if (Token::Match(closeParen, ") noexcept| = default ;") || (Token::simpleMatch(closeParen, ") noexcept (") && Token::simpleMatch(closeParen->linkAt(2), ") = default ;"))) { func->isDefault(true); return; } const bool hasConstKeyword = closeParen->next()->str() == "const"; if ((func->isConst() == hasConstKeyword) && (func->hasLvalRefQualifier() == lval) && (func->hasRvalRefQualifier() == rval)) { func->hasBody(true); } } } if (func->hasBody()) { func->token = *tok; func->arg = argStart; addNewFunction(scope, tok); if (*scope) { (*scope)->functionOf = scope1; (*scope)->function = func; (*scope)->function->functionScope = *scope; } return; } } } } } } // class function of unknown class addNewFunction(scope, tok); } void SymbolDatabase::addNewFunction(Scope **scope, const Token **tok) { const Token *tok1 = *tok; scopeList.emplace_back(this, tok1, *scope); Scope *newScope = &scopeList.back(); // find start of function '{' bool foundInitList = false; while (tok1 && tok1->str() != "{" && tok1->str() != ";") { if (tok1->link() && Token::Match(tok1, "(|<")) { tok1 = tok1->link(); } else if (foundInitList && Token::Match(tok1, "%name%|> {") && Token::Match(tok1->linkAt(1), "} ,|{")) { tok1 = tok1->linkAt(1); } else { if (tok1->str() == ":") foundInitList = true; tok1 = tok1->next(); } } if (tok1 && tok1->str() == "{") { newScope->bodyStart = tok1; newScope->bodyEnd = tok1->link(); // syntax error? if (!newScope->bodyEnd) { scopeList.pop_back(); while (tok1->next()) tok1 = tok1->next(); *scope = nullptr; *tok = tok1; return; } (*scope)->nestedList.push_back(newScope); *scope = newScope; *tok = tok1; } else { scopeList.pop_back(); *scope = nullptr; *tok = nullptr; } } const Token *Type::initBaseInfo(const Token *tok, const Token *tok1) { // goto initial '{' const Token *tok2 = tok1; while (tok2 && tok2->str() != "{") { // skip unsupported templates if (tok2->str() == "<") tok2 = tok2->link(); // check for base classes else if (Token::Match(tok2, ":|,")) { tok2 = tok2->next(); // check for invalid code if (!tok2 || !tok2->next()) return nullptr; Type::BaseInfo base; if (tok2->str() == "virtual") { base.isVirtual = true; tok2 = tok2->next(); } if (tok2->str() == "public") { base.access = AccessControl::Public; tok2 = tok2->next(); } else if (tok2->str() == "protected") { base.access = AccessControl::Protected; tok2 = tok2->next(); } else if (tok2->str() == "private") { base.access = AccessControl::Private; tok2 = tok2->next(); } else { if (tok->str() == "class") base.access = AccessControl::Private; else if (tok->str() == "struct") base.access = AccessControl::Public; } if (!tok2) return nullptr; if (tok2->str() == "virtual") { base.isVirtual = true; tok2 = tok2->next(); } if (!tok2) return nullptr; base.nameTok = tok2; // handle global namespace if (tok2->str() == "::") { tok2 = tok2->next(); } // handle derived base classes while (Token::Match(tok2, "%name% ::")) { tok2 = tok2->tokAt(2); } if (!tok2) return nullptr; base.name = tok2->str(); tok2 = tok2->next(); // add unhandled templates if (tok2 && tok2->link() && tok2->str() == "<") { for (const Token* const end = tok2->link()->next(); tok2 != end; tok2 = tok2->next()) { base.name += tok2->str(); } } // save pattern for base class name derivedFrom.push_back(base); } else tok2 = tok2->next(); } return tok2; } const std::string& Type::name() const { const Token* next = classDef->next(); if (classScope && classScope->enumClass && isEnumType()) return next->strAt(1); else if (next->str() == "class") return next->strAt(1); else if (next->isName()) return next->str(); return emptyString; } void SymbolDatabase::debugMessage(const Token *tok, const std::string &msg) const { if (tok && mSettings->debugwarnings) { const std::list locationList(1, tok); const ErrorLogger::ErrorMessage errmsg(locationList, &mTokenizer->list, Severity::debug, "debug", msg, false); if (mErrorLogger) mErrorLogger->reportErr(errmsg); } } const Function* Type::getFunction(const std::string& funcName) const { if (classScope) { const std::multimap::const_iterator it = classScope->functionMap.find(funcName); if (it != classScope->functionMap.end()) return it->second; } for (const Type::BaseInfo & i : derivedFrom) { if (i.type) { const Function* const func = i.type->getFunction(funcName); if (func) return func; } } return nullptr; } bool Type::hasCircularDependencies(std::set* ancestors) const { std::set knownAncestors; if (!ancestors) { ancestors=&knownAncestors; } for (std::vector::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) { if (!parent->type) continue; else if (this==parent->type) return true; else if (ancestors->find(*parent)!=ancestors->end()) return true; else { ancestors->insert(*parent); if (parent->type->hasCircularDependencies(ancestors)) return true; } } return false; } bool Type::findDependency(const Type* ancestor) const { if (this==ancestor) return true; for (std::vector::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) { if (parent->type && parent->type->findDependency(ancestor)) return true; } return false; } bool Type::isDerivedFrom(const std::string & ancestor) const { for (std::vector::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) { if (parent->name == ancestor) return true; if (parent->type && parent->type->isDerivedFrom(ancestor)) return true; } return false; } bool Variable::arrayDimensions(const Settings* settings) { const Library::Container* container = settings->library.detectContainer(mTypeStartToken); if (container && container->arrayLike_indexOp && container->size_templateArgNo > 0) { const Token* tok = Token::findsimplematch(mTypeStartToken, "<"); if (tok) { Dimension dimension_; tok = tok->next(); for (int i = 0; i < container->size_templateArgNo && tok; i++) { tok = tok->nextTemplateArgument(); } if (tok) { while (!tok->astParent() && !Token::Match(tok->next(), "[,<>]")) tok = tok->next(); while (tok->astParent() && !Token::Match(tok->astParent(), "[,<>]")) tok = tok->astParent(); dimension_.tok = tok; ValueFlow::valueFlowConstantFoldAST(const_cast(dimension_.tok), settings); if (tok->hasKnownIntValue()) { dimension_.num = tok->getKnownIntValue(); dimension_.known = true; } } mDimensions.push_back(dimension_); return true; } } const Token *dim = mNameToken; if (!dim) { // Argument without name dim = mTypeEndToken; // back up to start of array dimensions while (dim && dim->str() == "]") dim = dim->link()->previous(); } if (dim) dim = dim->next(); if (dim && dim->str() == ")") dim = dim->next(); bool arr = false; while (dim && dim->next() && dim->str() == "[") { Dimension dimension_; dimension_.known = false; // check for empty array dimension [] if (dim->next()->str() != "]") { dimension_.tok = dim->astOperand2(); ValueFlow::valueFlowConstantFoldAST(const_cast(dimension_.tok), settings); if (dimension_.tok && dimension_.tok->hasKnownIntValue()) { dimension_.num = dimension_.tok->getKnownIntValue(); dimension_.known = true; } } mDimensions.push_back(dimension_); dim = dim->link()->next(); arr = true; } return arr; } static std::ostream & operator << (std::ostream & s, Scope::ScopeType type) { s << (type == Scope::eGlobal ? "Global" : type == Scope::eClass ? "Class" : type == Scope::eStruct ? "Struct" : type == Scope::eUnion ? "Union" : type == Scope::eNamespace ? "Namespace" : type == Scope::eFunction ? "Function" : type == Scope::eIf ? "If" : type == Scope::eElse ? "Else" : type == Scope::eFor ? "For" : type == Scope::eWhile ? "While" : type == Scope::eDo ? "Do" : type == Scope::eSwitch ? "Switch" : type == Scope::eTry ? "Try" : type == Scope::eCatch ? "Catch" : type == Scope::eUnconditional ? "Unconditional" : type == Scope::eLambda ? "Lambda" : type == Scope::eEnum ? "Enum" : "Unknown"); return s; } static std::string accessControlToString(const AccessControl& access) { switch (access) { case AccessControl::Public: return "Public"; case AccessControl::Protected: return "Protected"; case AccessControl::Private: return "Private"; case AccessControl::Global: return "Global"; case AccessControl::Namespace: return "Namespace"; case AccessControl::Argument: return "Argument"; case AccessControl::Local: return "Local"; case AccessControl::Throw: return "Throw"; } return "Unknown"; } static std::string tokenToString(const Token* tok, const Tokenizer* tokenizer) { std::ostringstream oss; if (tok) { oss << tok->str() << " "; oss << tokenizer->list.fileLine(tok) << " "; } oss << tok; return oss.str(); } static std::string scopeToString(const Scope* scope, const Tokenizer* tokenizer) { std::ostringstream oss; if (scope) { oss << scope->type << " "; if (scope->classDef) oss << tokenizer->list.fileLine(scope->classDef) << " "; } oss << scope; return oss.str(); } static std::string tokenType(const Token * tok) { std::ostringstream oss; if (tok) { if (tok->isUnsigned()) oss << "unsigned "; else if (tok->isSigned()) oss << "signed "; if (tok->isComplex()) oss << "_Complex "; if (tok->isLong()) oss << "long "; oss << tok->str(); } return oss.str(); } void SymbolDatabase::printVariable(const Variable *var, const char *indent) const { std::cout << indent << "mNameToken: " << tokenToString(var->nameToken(), mTokenizer) << std::endl; if (var->nameToken()) { std::cout << indent << " declarationId: " << var->declarationId() << std::endl; } std::cout << indent << "mTypeStartToken: " << tokenToString(var->typeStartToken(), mTokenizer) << std::endl; std::cout << indent << "mTypeEndToken: " << tokenToString(var->typeEndToken(), mTokenizer) << std::endl; const Token * autoTok = nullptr; std::cout << indent << " "; for (const Token * tok = var->typeStartToken(); tok != var->typeEndToken()->next(); tok = tok->next()) { std::cout << " " << tokenType(tok); if (tok->str() == "auto") autoTok = tok; } std::cout << std::endl; if (autoTok) { const ValueType * valueType = autoTok->valueType(); std::cout << indent << " auto valueType: " << valueType << std::endl; if (var->typeStartToken()->valueType()) { std::cout << indent << " " << valueType->str() << std::endl; } } std::cout << indent << "mIndex: " << var->index() << std::endl; std::cout << indent << "mAccess: " << accessControlToString(var->accessControl()) << std::endl; std::cout << indent << "mFlags: " << std::endl; std::cout << indent << " isMutable: " << var->isMutable() << std::endl; std::cout << indent << " isStatic: " << var->isStatic() << std::endl; std::cout << indent << " isExtern: " << var->isExtern() << std::endl; std::cout << indent << " isLocal: " << var->isLocal() << std::endl; std::cout << indent << " isConst: " << var->isConst() << std::endl; std::cout << indent << " isClass: " << var->isClass() << std::endl; std::cout << indent << " isArray: " << var->isArray() << std::endl; std::cout << indent << " isPointer: " << var->isPointer() << std::endl; std::cout << indent << " isReference: " << var->isReference() << std::endl; std::cout << indent << " isRValueRef: " << var->isRValueReference() << std::endl; std::cout << indent << " hasDefault: " << var->hasDefault() << std::endl; std::cout << indent << " isStlType: " << var->isStlType() << std::endl; std::cout << indent << "mType: "; if (var->type()) { std::cout << var->type()->type() << " " << var->type()->name(); std::cout << " " << mTokenizer->list.fileLine(var->type()->classDef); std::cout << " " << var->type() << std::endl; } else std::cout << "none" << std::endl; if (var->nameToken()) { const ValueType * valueType = var->nameToken()->valueType(); std::cout << indent << "valueType: " << valueType << std::endl; if (valueType) { std::cout << indent << " " << valueType->str() << std::endl; } } std::cout << indent << "mScope: " << scopeToString(var->scope(), mTokenizer) << std::endl; std::cout << indent << "mDimensions:"; for (std::size_t i = 0; i < var->dimensions().size(); i++) { std::cout << " " << var->dimension(i); if (!var->dimensions()[i].known) std::cout << "?"; } std::cout << std::endl; } void SymbolDatabase::printOut(const char *title) const { std::cout << std::setiosflags(std::ios::boolalpha); if (title) std::cout << "\n### " << title << " ###\n"; for (std::list::const_iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) { std::cout << "Scope: " << &*scope << " " << scope->type << std::endl; std::cout << " className: " << scope->className << std::endl; std::cout << " classDef: " << tokenToString(scope->classDef, mTokenizer) << std::endl; std::cout << " bodyStart: " << tokenToString(scope->bodyStart, mTokenizer) << std::endl; std::cout << " bodyEnd: " << tokenToString(scope->bodyEnd, mTokenizer) << std::endl; std::list::const_iterator func; // find the function body if not implemented inline for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { std::cout << " Function: " << &*func << std::endl; std::cout << " name: " << tokenToString(func->tokenDef, mTokenizer) << std::endl; std::cout << " type: " << (func->type == Function::eConstructor? "Constructor" : func->type == Function::eCopyConstructor ? "CopyConstructor" : func->type == Function::eMoveConstructor ? "MoveConstructor" : func->type == Function::eOperatorEqual ? "OperatorEqual" : func->type == Function::eDestructor ? "Destructor" : func->type == Function::eFunction ? "Function" : func->type == Function::eLambda ? "Lambda" : "Unknown") << std::endl; std::cout << " access: " << accessControlToString(func->access) << std::endl; std::cout << " hasBody: " << func->hasBody() << std::endl; std::cout << " isInline: " << func->isInline() << std::endl; std::cout << " isConst: " << func->isConst() << std::endl; std::cout << " hasVirtualSpecifier: " << func->hasVirtualSpecifier() << std::endl; std::cout << " isPure: " << func->isPure() << std::endl; std::cout << " isStatic: " << func->isStatic() << std::endl; std::cout << " isStaticLocal: " << func->isStaticLocal() << std::endl; std::cout << " isExtern: " << func->isExtern() << std::endl; std::cout << " isFriend: " << func->isFriend() << std::endl; std::cout << " isExplicit: " << func->isExplicit() << std::endl; std::cout << " isDefault: " << func->isDefault() << std::endl; std::cout << " isDelete: " << func->isDelete() << std::endl; std::cout << " hasOverrideSpecifier: " << func->hasOverrideSpecifier() << std::endl; std::cout << " hasFinalSpecifier: " << func->hasFinalSpecifier() << std::endl; std::cout << " isNoExcept: " << func->isNoExcept() << std::endl; std::cout << " isThrow: " << func->isThrow() << std::endl; std::cout << " isOperator: " << func->isOperator() << std::endl; std::cout << " hasLvalRefQual: " << func->hasLvalRefQualifier() << std::endl; std::cout << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl; std::cout << " isVariadic: " << func->isVariadic() << std::endl; std::cout << " isVolatile: " << func->isVolatile() << std::endl; std::cout << " hasTrailingReturnType: " << func->hasTrailingReturnType() << std::endl; std::cout << " attributes:"; if (func->isAttributeConst()) std::cout << " const "; if (func->isAttributePure()) std::cout << " pure "; if (func->isAttributeNoreturn()) std::cout << " noreturn "; if (func->isAttributeNothrow()) std::cout << " nothrow "; if (func->isAttributeConstructor()) std::cout << " constructor "; if (func->isAttributeDestructor()) std::cout << " destructor "; if (func->isAttributeNodiscard()) std::cout << " nodiscard "; std::cout << std::endl; std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl; std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl; std::cout << " tokenDef: " << tokenToString(func->tokenDef, mTokenizer) << std::endl; std::cout << " argDef: " << tokenToString(func->argDef, mTokenizer) << std::endl; if (!func->isConstructor() && !func->isDestructor()) std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl; if (func->retDef) { std::cout << " "; for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;|override|final"); tok = tok->next()) std::cout << " " << tokenType(tok); std::cout << std::endl; } std::cout << " retType: " << func->retType << std::endl; if (func->tokenDef->next()->valueType()) { const ValueType * valueType = func->tokenDef->next()->valueType(); std::cout << " valueType: " << valueType << std::endl; if (valueType) { std::cout << " " << valueType->str() << std::endl; } } if (func->hasBody()) { std::cout << " token: " << tokenToString(func->token, mTokenizer) << std::endl; std::cout << " arg: " << tokenToString(func->arg, mTokenizer) << std::endl; } std::cout << " nestedIn: " << scopeToString(func->nestedIn, mTokenizer) << std::endl; std::cout << " functionScope: " << scopeToString(func->functionScope, mTokenizer) << std::endl; std::list::const_iterator var; for (var = func->argumentList.begin(); var != func->argumentList.end(); ++var) { std::cout << " Variable: " << &*var << std::endl; printVariable(&*var, " "); } } std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { std::cout << " Variable: " << &*var << std::endl; printVariable(&*var, " "); } if (scope->type == Scope::eEnum) { std::cout << " enumType: "; if (scope->enumType) scope->enumType->stringify(std::cout, false, true, false); else std::cout << "int"; std::cout << std::endl; std::cout << " enumClass: " << scope->enumClass << std::endl; for (std::vector::const_iterator enumerator = scope->enumeratorList.begin(); enumerator != scope->enumeratorList.end(); ++enumerator) { std::cout << " Enumerator: " << enumerator->name->str() << " = "; if (enumerator->value_known) { std::cout << enumerator->value; } if (enumerator->start) { const Token * tok = enumerator->start; std::cout << (enumerator->value_known ? " " : "") << "[" << tok->str(); while (tok && tok != enumerator->end) { if (tok->next()) std::cout << " " << tok->next()->str(); tok = tok->next(); } std::cout << "]"; } std::cout << std::endl; } } std::cout << " nestedIn: " << scope->nestedIn; if (scope->nestedIn) { std::cout << " " << scope->nestedIn->type << " " << scope->nestedIn->className; } std::cout << std::endl; std::cout << " definedType: " << scope->definedType << std::endl; std::cout << " nestedList[" << scope->nestedList.size() << "] = ("; std::list::const_iterator nsi; std::size_t count = scope->nestedList.size(); for (nsi = scope->nestedList.begin(); nsi != scope->nestedList.end(); ++nsi) { std::cout << " " << (*nsi) << " " << (*nsi)->type << " " << (*nsi)->className; if (count-- > 1) std::cout << ","; } std::cout << " )" << std::endl; std::list::const_iterator use; for (use = scope->usingList.begin(); use != scope->usingList.end(); ++use) { std::cout << " using: " << use->scope << " " << use->start->strAt(2); const Token *tok1 = use->start->tokAt(3); while (tok1 && tok1->str() == "::") { std::cout << "::" << tok1->strAt(1); tok1 = tok1->tokAt(2); } std::cout << " " << mTokenizer->list.fileLine(use->start) << std::endl; } std::cout << " functionOf: " << scopeToString(scope->functionOf, mTokenizer) << std::endl; std::cout << " function: " << scope->function; if (scope->function) std::cout << " " << scope->function->name(); std::cout << std::endl; } for (std::list::const_iterator type = typeList.begin(); type != typeList.end(); ++type) { std::cout << "Type: " << &(*type) << std::endl; std::cout << " name: " << type->name() << std::endl; std::cout << " classDef: " << tokenToString(type->classDef, mTokenizer) << std::endl; std::cout << " classScope: " << type->classScope << std::endl; std::cout << " enclosingScope: " << type->enclosingScope; if (type->enclosingScope) { std::cout << " " << type->enclosingScope->type << " " << type->enclosingScope->className; } std::cout << std::endl; std::cout << " needInitialization: " << (type->needInitialization == Type::NeedInitialization::Unknown ? "Unknown" : type->needInitialization == Type::NeedInitialization::True ? "True" : type->needInitialization == Type::NeedInitialization::False ? "False" : "Invalid") << std::endl; std::cout << " derivedFrom[" << type->derivedFrom.size() << "] = ("; std::size_t count = type->derivedFrom.size(); for (const Type::BaseInfo & i : type->derivedFrom) { if (i.isVirtual) std::cout << "Virtual "; std::cout << (i.access == AccessControl::Public ? " Public" : i.access == AccessControl::Protected ? " Protected" : i.access == AccessControl::Private ? " Private" : " Unknown"); if (i.type) std::cout << " " << i.type; else std::cout << " Unknown"; std::cout << " " << i.name; if (count-- > 1) std::cout << ","; } std::cout << " )" << std::endl; std::cout << " friendList[" << type->friendList.size() << "] = ("; for (size_t i = 0; i < type->friendList.size(); i++) { if (type->friendList[i].type) std::cout << type->friendList[i].type; else std::cout << " Unknown"; std::cout << ' '; if (type->friendList[i].nameEnd) std::cout << type->friendList[i].nameEnd->str(); if (i+1 < type->friendList.size()) std::cout << ','; } std::cout << " )" << std::endl; } for (std::size_t i = 1; i < mVariableList.size(); i++) { std::cout << "mVariableList[" << i << "]: " << mVariableList[i]; if (mVariableList[i]) { std::cout << " " << mVariableList[i]->name() << " " << mTokenizer->list.fileLine(mVariableList[i]->nameToken()); } std::cout << std::endl; } std::cout << std::resetiosflags(std::ios::boolalpha); } void SymbolDatabase::printXml(std::ostream &out) const { out << std::setiosflags(std::ios::boolalpha); std::set variables; // Scopes.. out << " " << std::endl; for (std::list::const_iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) { out << " type << "\""; if (!scope->className.empty()) out << " className=\"" << ErrorLogger::toxml(scope->className) << "\""; if (scope->bodyStart) out << " bodyStart=\"" << scope->bodyStart << '\"'; if (scope->bodyEnd) out << " bodyEnd=\"" << scope->bodyEnd << '\"'; if (scope->nestedIn) out << " nestedIn=\"" << scope->nestedIn << "\""; if (scope->function) out << " function=\"" << scope->function << "\""; if (scope->functionList.empty() && scope->varlist.empty()) out << "/>" << std::endl; else { out << '>' << std::endl; if (!scope->functionList.empty()) { out << " " << std::endl; for (std::list::const_iterator function = scope->functionList.begin(); function != scope->functionList.end(); ++function) { out << " tokenDef << "\" name=\"" << ErrorLogger::toxml(function->name()) << '\"'; out << " type=\"" << (function->type == Function::eConstructor? "Constructor" : function->type == Function::eCopyConstructor ? "CopyConstructor" : function->type == Function::eMoveConstructor ? "MoveConstructor" : function->type == Function::eOperatorEqual ? "OperatorEqual" : function->type == Function::eDestructor ? "Destructor" : function->type == Function::eFunction ? "Function" : function->type == Function::eLambda ? "Lambda" : "Unknown") << '\"'; if (function->nestedIn->definedType) { if (function->hasVirtualSpecifier()) out << " hasVirtualSpecifier=\"true\""; else if (function->isImplicitlyVirtual()) out << " isImplicitlyVirtual=\"true\""; } if (function->isStatic()) out << " isStatic=\"true\""; if (function->argCount() == 0U) out << "/>" << std::endl; else { out << ">" << std::endl; for (unsigned int argnr = 0; argnr < function->argCount(); ++argnr) { const Variable *arg = function->getArgumentVar(argnr); out << " " << std::endl; variables.insert(arg); } out << " " << std::endl; } } out << " " << std::endl; } if (!scope->varlist.empty()) { out << " " << std::endl; for (std::list::const_iterator var = scope->varlist.begin(); var != scope->varlist.end(); ++var) out << " " << std::endl; out << " " << std::endl; } out << " " << std::endl; } } out << " " << std::endl; // Variables.. for (const Variable *var : mVariableList) variables.insert(var); out << " " << std::endl; for (const Variable *var : variables) { if (!var) continue; out << " nameToken() << '\"'; out << " typeStartToken=\"" << var->typeStartToken() << '\"'; out << " typeEndToken=\"" << var->typeEndToken() << '\"'; out << " access=\"" << accessControlToString(var->mAccess) << '\"'; out << " scope=\"" << var->scope() << '\"'; out << " constness=\"" << var->valueType()->constness << '\"'; out << " isArgument=\"" << var->isArgument() << '\"'; out << " isArray=\"" << var->isArray() << '\"'; out << " isClass=\"" << var->isClass() << '\"'; out << " isConst=\"" << var->isConst() << '\"'; out << " isExtern=\"" << var->isExtern() << '\"'; out << " isLocal=\"" << var->isLocal() << '\"'; out << " isPointer=\"" << var->isPointer() << '\"'; out << " isReference=\"" << var->isReference() << '\"'; out << " isStatic=\"" << var->isStatic() << '\"'; out << "/>" << std::endl; } out << " " << std::endl; out << std::resetiosflags(std::ios::boolalpha); } //--------------------------------------------------------------------------- static const Type* findVariableTypeIncludingUsedNamespaces(const SymbolDatabase* symbolDatabase, const Scope* scope, const Token* typeTok) { const Type* argType = symbolDatabase->findVariableType(scope, typeTok); if (argType) return argType; // look for variable type in any using namespace in this scope or above while (scope) { for (const Scope::UsingInfo &ui : scope->usingList) { if (ui.scope) { argType = symbolDatabase->findVariableType(ui.scope, typeTok); if (argType) return argType; } } scope = scope->nestedIn; } return nullptr; } //--------------------------------------------------------------------------- void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope) { // check for non-empty argument list "( ... )" const Token * start = arg ? arg : argDef; if (!Token::simpleMatch(start, "(")) return; if (!(start && start->link() != start->next() && !Token::simpleMatch(start, "( void )"))) return; unsigned int count = 0; for (const Token* tok = start->next(); tok; tok = tok->next()) { if (Token::Match(tok, ",|)")) return; // Syntax error const Token* startTok = tok; const Token* endTok = nullptr; const Token* nameTok = nullptr; do { if (tok != startTok && !nameTok && Token::Match(tok, "( & %var% ) [")) { nameTok = tok->tokAt(2); endTok = nameTok->previous(); tok = tok->link(); } else if (tok->varId() != 0) { nameTok = tok; endTok = tok->previous(); } else if (tok->str() == "[") { // skip array dimension(s) tok = tok->link(); while (tok->next()->str() == "[") tok = tok->next()->link(); } else if (tok->str() == "<") { tok = tok->link(); if (!tok) // something is wrong so just bail out return; } tok = tok->next(); if (!tok) // something is wrong so just bail return; } while (tok->str() != "," && tok->str() != ")" && tok->str() != "="); const Token *typeTok = startTok; // skip over stuff to get to type while (Token::Match(typeTok, "const|volatile|enum|struct|::")) typeTok = typeTok->next(); if (Token::Match(typeTok, ",|)")) { // #8333 symbolDatabase->mTokenizer->syntaxError(typeTok); return; } // skip over qualification while (Token::Match(typeTok, "%type% ::")) typeTok = typeTok->tokAt(2); // check for argument with no name or missing varid if (!endTok) { if (tok->previous()->isName() && !Token::Match(tok->tokAt(-1), "const|volatile")) { if (tok->previous() != typeTok) { nameTok = tok->previous(); endTok = nameTok->previous(); if (hasBody()) symbolDatabase->debugMessage(nameTok, "Function::addArguments found argument \'" + nameTok->str() + "\' with varid 0."); } else endTok = typeTok; } else endTok = tok->previous(); } const ::Type *argType = nullptr; if (!typeTok->isStandardType()) { argType = findVariableTypeIncludingUsedNamespaces(symbolDatabase, scope, typeTok); // save type const_cast(typeTok)->type(argType); } // skip default values if (tok->str() == "=") { do { if (tok->link() && Token::Match(tok, "[{[(<]")) tok = tok->link(); tok = tok->next(); } while (tok->str() != "," && tok->str() != ")"); } // skip over stuff before type while (Token::Match(startTok, "enum|struct|const|volatile")) startTok = startTok->next(); argumentList.emplace_back(nameTok, startTok, endTok, count++, AccessControl::Argument, argType, functionScope, symbolDatabase->mSettings); if (tok->str() == ")") { // check for a variadic function if (Token::simpleMatch(startTok, "...")) isVariadic(true); break; } } // count default arguments for (const Token* tok = argDef->next(); tok && tok != argDef->link(); tok = tok->next()) { if (tok->str() == "=") initArgCount++; } } bool Function::isImplicitlyVirtual(bool defaultVal) const { if (hasVirtualSpecifier()) //If it has the virtual specifier it's definitely virtual return true; if (hasOverrideSpecifier()) //If it has the override specifier then it's either virtual or not going to compile return true; bool foundAllBaseClasses = true; if (getOverriddenFunction(&foundAllBaseClasses)) //If it overrides a base class's method then it's virtual return true; if (foundAllBaseClasses) //If we've seen all the base classes and none of the above were true then it must not be virtual return false; return defaultVal; //If we can't see all the bases classes then we can't say conclusively } const Function *Function::getOverriddenFunction(bool *foundAllBaseClasses) const { if (foundAllBaseClasses) *foundAllBaseClasses = true; if (!nestedIn->isClassOrStruct()) return nullptr; return getOverriddenFunctionRecursive(nestedIn->definedType, foundAllBaseClasses); } const Function * Function::getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const { // check each base class for (const ::Type::BaseInfo & i : baseType->derivedFrom) { const ::Type* derivedFromType = i.type; // check if base class exists in database if (!derivedFromType || !derivedFromType->classScope) { if (foundAllBaseClasses) *foundAllBaseClasses = false; continue; } const Scope *parent = derivedFromType->classScope; // check if function defined in base class for (std::multimap::const_iterator it = parent->functionMap.find(tokenDef->str()); it != parent->functionMap.end() && it->first == tokenDef->str(); ++it) { const Function * func = it->second; if (func->hasVirtualSpecifier()) { // Base is virtual and of same name const Token *temp1 = func->tokenDef->previous(); const Token *temp2 = tokenDef->previous(); bool match = true; // check for matching return parameters while (temp1->str() != "virtual") { if (temp1->str() != temp2->str() && !(temp1->str() == derivedFromType->name() && temp2->str() == baseType->name())) { match = false; break; } temp1 = temp1->previous(); temp2 = temp2->previous(); } // check for matching function parameters match = match && argsMatch(baseType->classScope, func->argDef, argDef, emptyString, 0); // check for matching cv-ref qualifiers match = match && isConst() == func->isConst() && isVolatile() == func->isVolatile() && hasRvalRefQualifier() == func->hasRvalRefQualifier() && hasLvalRefQualifier() == func->hasLvalRefQualifier(); // it's a match if (match) { return func; } } } if (!derivedFromType->derivedFrom.empty() && !derivedFromType->hasCircularDependencies()) { // avoid endless recursion, see #5289 Crash: Stack overflow in isImplicitlyVirtual_rec when checking SVN and // #5590 with a loop within the class hierarchy. const Function *func = getOverriddenFunctionRecursive(derivedFromType, foundAllBaseClasses); if (func) { return func; } } } return nullptr; } const Variable* Function::getArgumentVar(nonneg int num) const { for (std::list::const_iterator i = argumentList.begin(); i != argumentList.end(); ++i) { if (i->index() == num) return (&*i); else if (i->index() > num) return nullptr; } return nullptr; } //--------------------------------------------------------------------------- Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_) : check(check_), classDef(classDef_), bodyStart(start_), bodyEnd(start_->link()), nestedIn(nestedIn_), numConstructors(0), numCopyOrMoveConstructors(0), type(type_), definedType(nullptr), functionOf(nullptr), function(nullptr), enumType(nullptr), enumClass(false) { } Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_) : check(check_), classDef(classDef_), bodyStart(nullptr), bodyEnd(nullptr), nestedIn(nestedIn_), numConstructors(0), numCopyOrMoveConstructors(0), definedType(nullptr), functionOf(nullptr), function(nullptr), enumType(nullptr), enumClass(false) { const Token *nameTok = classDef; if (!classDef) { type = Scope::eGlobal; } else if (classDef->str() == "class" && check && check->isCPP()) { type = Scope::eClass; nameTok = nameTok->next(); } else if (classDef->str() == "struct") { type = Scope::eStruct; nameTok = nameTok->next(); } else if (classDef->str() == "union") { type = Scope::eUnion; nameTok = nameTok->next(); } else if (classDef->str() == "namespace") { type = Scope::eNamespace; nameTok = nameTok->next(); } else if (classDef->str() == "enum") { type = Scope::eEnum; nameTok = nameTok->next(); if (nameTok->str() == "class") { enumClass = true; nameTok = nameTok->next(); } } else if (classDef->str() == "[") { type = Scope::eLambda; } else { type = Scope::eFunction; } // skip over qualification if present nameTok = skipScopeIdentifiers(nameTok); if (nameTok && ((type == Scope::eEnum && Token::Match(nameTok, ":|{")) || nameTok->str() != "{")) // anonymous and unnamed structs/unions don't have a name className = nameTok->str(); } bool Scope::hasDefaultConstructor() const { if (numConstructors) { std::list::const_iterator func; for (func = functionList.begin(); func != functionList.end(); ++func) { if (func->type == Function::eConstructor && func->argCount() == 0) return true; } } return false; } AccessControl Scope::defaultAccess() const { switch (type) { case eGlobal: return AccessControl::Global; case eClass: return AccessControl::Private; case eStruct: return AccessControl::Public; case eUnion: return AccessControl::Public; case eNamespace: return AccessControl::Namespace; default: return AccessControl::Local; } } // Get variable list.. void Scope::getVariableList(const Settings* settings) { const Token *start; if (bodyStart) start = bodyStart->next(); // global scope else if (className.empty()) start = check->mTokenizer->tokens(); // forward declaration else return; // Variable declared in condition: if (auto x = bar()) if (Token::Match(classDef, "if|while ( %type%") && Token::simpleMatch(classDef->next()->astOperand2(), "=")) { checkVariable(classDef->tokAt(2), defaultAccess(), settings); } AccessControl varaccess = defaultAccess(); for (const Token *tok = start; tok && tok != bodyEnd; tok = tok->next()) { // syntax error? if (tok->next() == nullptr) break; // Is it a function? else if (tok->str() == "{") { tok = tok->link(); continue; } // Is it a nested class or structure? else if (Token::Match(tok, "class|struct|union|namespace %type% :|{")) { tok = tok->tokAt(2); while (tok && tok->str() != "{") tok = tok->next(); if (tok) { // skip implementation tok = tok->link(); continue; } else break; } else if (Token::Match(tok, "struct|union {")) { if (Token::Match(tok->next()->link(), "} %name% ;|[")) { tok = tok->next()->link()->tokAt(2); continue; } else if (Token::simpleMatch(tok->next()->link(), "} ;")) { tok = tok->next(); continue; } } // Borland C++: Skip all variables in the __published section. // These are automatically initialized. else if (tok->str() == "__published:") { for (; tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); if (Token::Match(tok->next(), "private:|protected:|public:")) break; } if (tok) continue; else break; } // "private:" "public:" "protected:" etc else if (tok->str() == "public:") { varaccess = AccessControl::Public; continue; } else if (tok->str() == "protected:") { varaccess = AccessControl::Protected; continue; } else if (tok->str() == "private:") { varaccess = AccessControl::Private; continue; } // Is it a forward declaration? else if (Token::Match(tok, "class|struct|union %name% ;")) { tok = tok->tokAt(2); continue; } // Borland C++: Ignore properties.. else if (tok->str() == "__property") continue; // skip return, goto and delete else if (Token::Match(tok, "return|delete|goto")) { while (tok->next() && tok->next()->str() != ";" && tok->next()->str() != "}" /* ticket #4994 */) { tok = tok->next(); } continue; } // skip case/default if (Token::Match(tok, "case|default")) { while (tok->next() && !Token::Match(tok->next(), "[:;{}]")) tok = tok->next(); continue; } // Search for start of statement.. else if (tok->previous() && !Token::Match(tok->previous(), ";|{|}|public:|protected:|private:")) continue; else if (tok->str() == ";") continue; tok = checkVariable(tok, varaccess, settings); if (!tok) break; } } const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess, const Settings* settings) { // Is it a throw..? if (Token::Match(tok, "throw %any% (") && Token::simpleMatch(tok->linkAt(2), ") ;")) { return tok->linkAt(2); } if ((Token::Match(tok, "throw %any% :: %any% (") && Token::simpleMatch(tok->linkAt(4), ") ;"))) { return tok->linkAt(4); } // friend? if (Token::Match(tok, "friend %type%") && tok->next()->varId() == 0) { const Token *next = Token::findmatch(tok->tokAt(2), ";|{"); if (next && next->str() == "{") next = next->link(); return next; } // skip const|volatile|static|mutable|extern while (Token::Match(tok, "const|volatile|static|mutable|extern")) { tok = tok->next(); } // the start of the type tokens does not include the above modifiers const Token *typestart = tok; if (Token::Match(tok, "class|struct|union|enum")) { tok = tok->next(); } // This is the start of a statement const Token *vartok = nullptr; const Token *typetok = nullptr; if (tok && isVariableDeclaration(tok, vartok, typetok)) { // If the vartok was set in the if-blocks above, create a entry for this variable.. tok = vartok->next(); while (Token::Match(tok, "[|{")) tok = tok->link()->next(); if (vartok->varId() == 0) { if (!vartok->isBoolean()) check->debugMessage(vartok, "Scope::checkVariable found variable \'" + vartok->str() + "\' with varid 0."); return tok; } const Type *vType = nullptr; if (typetok) { vType = findVariableTypeIncludingUsedNamespaces(check, this, typetok); const_cast(typetok)->type(vType); } // skip "enum" or "struct" if (Token::Match(typestart, "enum|struct")) typestart = typestart->next(); addVariable(vartok, typestart, vartok->previous(), varaccess, vType, this, settings); } return tok; } const Variable *Scope::getVariable(const std::string &varname) const { std::list::const_iterator iter; for (iter = varlist.begin(); iter != varlist.end(); ++iter) { if (iter->name() == varname) return &*iter; } return nullptr; } static const Token* skipPointers(const Token* tok) { while (Token::Match(tok, "*|&|&&") || (Token::Match(tok, "( [*&]") && Token::Match(tok->link()->next(), "(|["))) { tok = tok->next(); if (tok->strAt(-1) == "(" && Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); } return tok; } static const Token* skipPointersAndQualifiers(const Token* tok) { tok = skipPointers(tok); while (Token::Match(tok, "const|volatile")) { tok = tok->next(); tok = skipPointers(tok); } return tok; } bool Scope::isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const { const bool isCPP = check && check->mTokenizer->isCPP(); if (isCPP && Token::Match(tok, "throw|new")) return false; const bool isCPP11 = isCPP && check->mSettings->standards.cpp >= Standards::CPP11; if (isCPP11 && tok->str() == "using") return false; const Token* localTypeTok = skipScopeIdentifiers(tok); const Token* localVarTok = nullptr; if (Token::Match(localTypeTok, "%type% <")) { if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) return false; const Token* closeTok = localTypeTok->next()->link(); if (closeTok) { localVarTok = skipPointers(closeTok->next()); if (Token::Match(localVarTok, ":: %type% %name% [;=({]")) { if (localVarTok->strAt(3) != "(" || Token::Match(localVarTok->linkAt(3), "[)}] ;")) { localTypeTok = localVarTok->next(); localVarTok = localVarTok->tokAt(2); } } } } else if (Token::Match(localTypeTok, "%type%")) { localVarTok = skipPointersAndQualifiers(localTypeTok->next()); } if (!localVarTok) return false; if (localVarTok->str() == "const") localVarTok = localVarTok->next(); if (Token::Match(localVarTok, "%name% ;|=") || (localVarTok && localVarTok->varId() && localVarTok->strAt(1) == ":")) { vartok = localVarTok; typetok = localTypeTok; } else if (Token::Match(localVarTok, "%name% )|[") && localVarTok->str() != "operator") { vartok = localVarTok; typetok = localTypeTok; } else if (localVarTok && localVarTok->varId() && Token::Match(localVarTok, "%name% (|{") && Token::Match(localVarTok->next()->link(), ")|} ;")) { vartok = localVarTok; typetok = localTypeTok; } else if (type == eCatch && Token::Match(localVarTok, "%name% )")) { vartok = localVarTok; typetok = localTypeTok; } return nullptr != vartok; } const Token * Scope::addEnum(const Token * tok, bool isCpp) { const Token * tok2 = tok->next(); // skip over class if present if (isCpp && tok2->str() == "class") tok2 = tok2->next(); // skip over name tok2 = tok2->next(); // save type if present if (tok2->str() == ":") { tok2 = tok2->next(); enumType = tok2; tok2 = tok2->next(); } // add enumerators if (tok2->str() == "{") { const Token * end = tok2->link(); tok2 = tok2->next(); while (Token::Match(tok2, "%name% =|,|}") || (Token::Match(tok2, "%name% (") && Token::Match(tok2->linkAt(1), ") ,|}"))) { Enumerator enumerator(this); // save enumerator name enumerator.name = tok2; // skip over name tok2 = tok2->next(); if (tok2->str() == "=") { // skip over "=" tok2 = tok2->next(); if (tok2->str() == "}") return nullptr; enumerator.start = tok2; while (!Token::Match(tok2, ",|}")) { if (tok2->link()) tok2 = tok2->link(); enumerator.end = tok2; tok2 = tok2->next(); } } else if (tok2->str() == "(") { // skip over unknown macro tok2 = tok2->link()->next(); } if (tok2->str() == ",") { enumeratorList.push_back(enumerator); tok2 = tok2->next(); } else if (tok2->str() == "}") { enumeratorList.push_back(enumerator); break; } } if (tok2 == end) { tok2 = tok2->next(); if (tok2 && tok2->str() != ";") tok2 = nullptr; } else tok2 = nullptr; } else tok2 = nullptr; return tok2; } const Enumerator * SymbolDatabase::findEnumerator(const Token * tok) const { const Scope * scope = tok->scope(); const std::string &tokStr = tok->str(); if (mTokensThatAreNotEnumeratorValues.find(tokStr) != mTokensThatAreNotEnumeratorValues.end()) { return nullptr; } // check for qualified name if (tok->strAt(-1) == "::") { // find first scope const Token *tok1 = tok; while (Token::Match(tok1->tokAt(-2), "%name% ::")) tok1 = tok1->tokAt(-2); if (tok1->strAt(-1) == "::") scope = &scopeList.front(); else { // FIXME search base class here // find first scope while (scope && scope->nestedIn) { const Scope * temp = scope->nestedIn->findRecordInNestedList(tok1->str()); if (temp) { scope = temp; break; } scope = scope->nestedIn; } } if (scope) { tok1 = tok1->tokAt(2); while (scope && Token::Match(tok1, "%name% ::")) { scope = scope->findRecordInNestedList(tok1->str()); tok1 = tok1->tokAt(2); } if (scope) { const Enumerator * enumerator = scope->findEnumerator(tokStr); if (enumerator) // enum class return enumerator; // enum else { for (std::list::const_iterator it = scope->nestedList.begin(), end = scope->nestedList.end(); it != end; ++it) { enumerator = (*it)->findEnumerator(tokStr); if (enumerator) return enumerator; } } } } } else { const Enumerator * enumerator = scope->findEnumerator(tokStr); if (enumerator) return enumerator; for (std::list::const_iterator s = scope->nestedList.begin(); s != scope->nestedList.end(); ++s) { enumerator = (*s)->findEnumerator(tokStr); if (enumerator) return enumerator; } if (scope->definedType) { const std::vector & derivedFrom = scope->definedType->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Type *derivedFromType = i.type; if (derivedFromType && derivedFromType ->classScope) { enumerator = derivedFromType->classScope->findEnumerator(tokStr); if (enumerator) return enumerator; } } } while (scope->nestedIn) { if (scope->type == Scope::eFunction && scope->functionOf) scope = scope->functionOf; else scope = scope->nestedIn; enumerator = scope->findEnumerator(tokStr); if (enumerator) return enumerator; for (std::list::const_iterator s = scope->nestedList.begin(); s != scope->nestedList.end(); ++s) { enumerator = (*s)->findEnumerator(tokStr); if (enumerator) return enumerator; } } } mTokensThatAreNotEnumeratorValues.insert(tokStr); return nullptr; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findVariableTypeInBase(const Scope* scope, const Token* typeTok) const { if (scope && scope->definedType && !scope->definedType->derivedFrom.empty()) { const std::vector &derivedFrom = scope->definedType->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Type *base = i.type; if (base && base->classScope) { const Type * type = base->classScope->findType(typeTok->str()); if (type) return type; type = findVariableTypeInBase(base->classScope, typeTok); if (type) return type; } } } return nullptr; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *typeTok) const { const Scope *scope = start; // check if type does not have a namespace if (typeTok->strAt(-1) != "::" && typeTok->strAt(1) != "::") { // check if type same as scope if (start->isClassOrStruct() && typeTok->str() == start->className) return start->definedType; while (scope) { // look for type in this scope const Type * type = scope->findType(typeTok->str()); if (type) return type; // look for type in base classes if possible if (scope->isClassOrStruct()) { type = findVariableTypeInBase(scope, typeTok); if (type) return type; } // check if in member function class to see if it's present in class if (scope->type == Scope::eFunction && scope->functionOf) { const Scope *scope1 = scope->functionOf; type = scope1->findType(typeTok->str()); if (type) return type; type = findVariableTypeInBase(scope1, typeTok); if (type) return type; } scope = scope->nestedIn; } } // check for a qualified name and use it when given else if (typeTok->strAt(-1) == "::") { // check if type is not part of qualification if (typeTok->strAt(1) == "::") return nullptr; // find start of qualified function name const Token *tok1 = typeTok; while (Token::Match(tok1->tokAt(-2), "%type% ::") || (Token::simpleMatch(tok1->tokAt(-2), "> ::") && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->tokAt(-1), "%type%"))) { if (tok1->strAt(-1) == "::") tok1 = tok1->tokAt(-2); else tok1 = tok1->linkAt(-2)->tokAt(-1); } // check for global scope if (tok1->strAt(-1) == "::") { scope = &scopeList.front(); scope = scope->findRecordInNestedList(tok1->str()); } // find start of qualification else { while (scope) { if (scope->className == tok1->str()) break; else { const Scope *scope1 = scope->findRecordInNestedList(tok1->str()); if (scope1) { scope = scope1; break; } else if (scope->type == Scope::eFunction && scope->functionOf) scope = scope->functionOf; else scope = scope->nestedIn; } } } if (scope) { // follow qualification while (scope && (Token::Match(tok1, "%type% ::") || (Token::Match(tok1, "%type% <") && Token::simpleMatch(tok1->linkAt(1), "> ::")))) { if (tok1->strAt(1) == "::") tok1 = tok1->tokAt(2); else tok1 = tok1->linkAt(1)->tokAt(2); const Scope * temp = scope->findRecordInNestedList(tok1->str()); if (!temp) { // look in base classes const Type * type = findVariableTypeInBase(scope, tok1); if (type) return type; } scope = temp; } if (scope && scope->definedType) return scope->definedType; } } return nullptr; } bool Scope::hasInlineOrLambdaFunction() const { for (const Scope *s : nestedList) { // Inline function if (s->type == Scope::eUnconditional && Token::simpleMatch(s->bodyStart->previous(), ") {")) return true; // Lambda function if (s->type == Scope::eLambda) return true; } return false; } void Scope::findFunctionInBase(const std::string & name, nonneg int args, std::vector & matches) const { if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { const std::vector &derivedFrom = definedType->derivedFrom; for (const Type::BaseInfo & i : derivedFrom) { const Type *base = i.type; if (base && base->classScope) { if (base->classScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already continue; for (std::multimap::const_iterator it = base->classScope->functionMap.find(name); it != base->classScope->functionMap.end() && it->first == name; ++it) { const Function *func = it->second; if (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount())) { matches.push_back(func); } } base->classScope->findFunctionInBase(name, args, matches); } } } } //--------------------------------------------------------------------------- static void checkVariableCallMatch(const Variable* callarg, const Variable* funcarg, size_t& same, size_t& fallback1, size_t& fallback2) { if (callarg) { ValueType::MatchResult res = ValueType::matchParameter(callarg->valueType(), callarg, funcarg); if (res == ValueType::MatchResult::SAME) { same++; return; } if (res == ValueType::MatchResult::FALLBACK1) { fallback1++; return; } if (res == ValueType::MatchResult::FALLBACK2) { fallback2++; return; } if (res == ValueType::MatchResult::NOMATCH) return; bool ptrequals = callarg->isArrayOrPointer() == funcarg->isArrayOrPointer(); bool constEquals = !callarg->isArrayOrPointer() || ((callarg->typeStartToken()->strAt(-1) == "const") == (funcarg->typeStartToken()->strAt(-1) == "const")); if (ptrequals && constEquals && callarg->typeStartToken()->str() == funcarg->typeStartToken()->str() && callarg->typeStartToken()->isUnsigned() == funcarg->typeStartToken()->isUnsigned() && callarg->typeStartToken()->isLong() == funcarg->typeStartToken()->isLong()) { same++; } else if (callarg->isArrayOrPointer()) { if (ptrequals && constEquals && funcarg->typeStartToken()->str() == "void") fallback1++; else if (constEquals && funcarg->isStlStringType() && Token::Match(callarg->typeStartToken(), "char|wchar_t")) fallback2++; } else if (ptrequals) { const bool takesInt = Token::Match(funcarg->typeStartToken(), "char|short|int|long"); const bool takesFloat = Token::Match(funcarg->typeStartToken(), "float|double"); const bool passesInt = Token::Match(callarg->typeStartToken(), "char|short|int|long"); const bool passesFloat = Token::Match(callarg->typeStartToken(), "float|double"); if ((takesInt && passesInt) || (takesFloat && passesFloat)) fallback1++; else if ((takesInt && passesFloat) || (takesFloat && passesInt)) fallback2++; } } } static std::string getTypeString(const Token *typeToken) { if (!typeToken) return ""; while (Token::Match(typeToken, "%name%|*|&|::")) { if (typeToken->str() == "::") { std::string ret; while (Token::Match(typeToken, ":: %name%")) { ret += "::" + typeToken->strAt(1); typeToken = typeToken->tokAt(2); } if (typeToken->str() == "<") { for (const Token *tok = typeToken; tok != typeToken->link(); tok = tok->next()) ret += tok->str(); ret += ">"; } return ret; } if (Token::Match(typeToken, "%name% const| %var%|*|&")) { return typeToken->str(); } typeToken = typeToken->next(); } return ""; } const Function* Scope::findFunction(const Token *tok, bool requireConst) const { // make sure this is a function call const Token *end = tok->linkAt(1); if (!end) return nullptr; const std::vector arguments = getArguments(tok); std::vector matches; // find all the possible functions that could match const std::size_t args = arguments.size(); auto addMatchingFunctions = [&](const Scope *scope) { for (std::multimap::const_iterator it = scope->functionMap.find(tok->str()); it != scope->functionMap.cend() && it->first == tok->str(); ++it) { const Function *func = it->second; if (args == func->argCount() || (func->isVariadic() && args >= (func->argCount() - 1)) || (args < func->argCount() && args >= func->minArgCount())) { matches.push_back(func); } } }; addMatchingFunctions(this); // check in anonumous namespaces for (const Scope *nestedScope : nestedList) { if (nestedScope->type == eNamespace && nestedScope->className.empty()) addMatchingFunctions(nestedScope); } // check in base classes findFunctionInBase(tok->str(), args, matches); const Function* fallback1Func = nullptr; const Function* fallback2Func = nullptr; // check each function against the arguments in the function call for a match for (std::size_t i = 0; i < matches.size();) { bool constFallback = false; const Function * func = matches[i]; size_t same = 0; if (requireConst && !func->isConst()) { i++; continue; } if (!requireConst || !func->isConst()) { // get the function this call is in const Scope * scope = tok->scope(); // check if this function is a member function if (scope && scope->functionOf && scope->functionOf->isClassOrStruct() && scope->function) { // check if isConst mismatches if (scope->function->isConst() != func->isConst()) { if (scope->function->isConst()) { ++i; continue; } constFallback = true; } } } size_t fallback1 = 0; size_t fallback2 = 0; bool erased = false; for (std::size_t j = 0; j < args; ++j) { // don't check variadic arguments if (func->isVariadic() && j > (func->argCount() - 1)) { break; } const Variable *funcarg = func->getArgumentVar(j); if (!arguments[j]->valueType()) { const Token *vartok = arguments[j]; int pointer = 0; while (vartok && (vartok->isUnaryOp("&") || vartok->isUnaryOp("*"))) { pointer += vartok->isUnaryOp("&") ? 1 : -1; vartok = vartok->astOperand1(); } if (vartok && vartok->variable()) { const Token *callArgTypeToken = vartok->variable()->typeStartToken(); const Token *funcArgTypeToken = funcarg->typeStartToken(); auto parseDecl = [](const Token *typeToken) -> ValueType { ValueType ret; while (Token::Match(typeToken->previous(), "%name%")) typeToken = typeToken->previous(); while (Token::Match(typeToken, "%name%|*|&|::|<")) { if (typeToken->str() == "const") ret.constness |= (1 << ret.pointer); else if (typeToken->str() == "*") ret.pointer++; else if (typeToken->str() == "<") { if (!typeToken->link()) break; typeToken = typeToken->link(); } typeToken = typeToken->next(); } return ret; }; const std::string type1 = getTypeString(callArgTypeToken); const std::string type2 = getTypeString(funcArgTypeToken); if (!type1.empty() && type1 == type2) { ValueType callArgType = parseDecl(callArgTypeToken); callArgType.pointer += pointer; ValueType funcArgType = parseDecl(funcArgTypeToken); callArgType.sign = funcArgType.sign = ValueType::Sign::SIGNED; callArgType.type = funcArgType.type = ValueType::Type::INT; ValueType::MatchResult res = ValueType::matchParameter(&callArgType, &funcArgType); if (res == ValueType::MatchResult::SAME) ++same; else if (res == ValueType::MatchResult::FALLBACK1) ++fallback1; else if (res == ValueType::MatchResult::FALLBACK2) ++fallback2; continue; } } } // check for a match with a variable if (Token::Match(arguments[j], "%var% ,|)")) { const Variable * callarg = arguments[j]->variable(); checkVariableCallMatch(callarg, funcarg, same, fallback1, fallback2); } else if (funcarg->isStlStringType() && arguments[j]->valueType() && arguments[j]->valueType()->pointer == 1 && arguments[j]->valueType()->type == ValueType::Type::CHAR) fallback2++; // check for a match with nullptr else if (funcarg->isPointer() && Token::Match(arguments[j], "nullptr|NULL ,|)")) same++; else if (arguments[j]->isNumber() && funcarg->isPointer() && MathLib::isNullValue(arguments[j]->str())) fallback1++; // Try to evaluate the apparently more complex expression else { const Token *vartok = arguments[j]; while (vartok->isUnaryOp("&") || vartok->isUnaryOp("*")) vartok = vartok->astOperand1(); ValueType::MatchResult res = ValueType::matchParameter(arguments[j]->valueType(), vartok->variable(), funcarg); if (res == ValueType::MatchResult::SAME) ++same; else if (res == ValueType::MatchResult::FALLBACK1) ++fallback1; else if (res == ValueType::MatchResult::FALLBACK2) ++fallback2; else if (res == ValueType::MatchResult::NOMATCH) { // can't match so remove this function from possible matches matches.erase(matches.begin() + i); erased = true; break; } } } const size_t hasToBe = func->isVariadic() ? (func->argCount() - 1) : args; // check if all arguments matched if (same == hasToBe) { if (constFallback || (!requireConst && func->isConst())) fallback1Func = func; else return func; } else if (!fallback1Func) { if (same + fallback1 == hasToBe) fallback1Func = func; else if (!fallback2Func && same + fallback2 + fallback1 == hasToBe) fallback2Func = func; } if (!erased) ++i; } // Fallback cases if (fallback1Func) return fallback1Func; if (fallback2Func) return fallback2Func; // Only one candidate left if (matches.size() == 1) return matches[0]; return nullptr; } //--------------------------------------------------------------------------- const Function* SymbolDatabase::findFunction(const Token *tok) const { // find the scope this function is in const Scope *currScope = tok->scope(); while (currScope && currScope->isExecutable()) { if (currScope->functionOf) currScope = currScope->functionOf; else currScope = currScope->nestedIn; } // check for a qualified name and use it when given if (tok->strAt(-1) == "::") { // find start of qualified function name const Token *tok1 = tok; while (Token::Match(tok1->tokAt(-2), ">|%type% ::")) { if (tok1->strAt(-2) == ">") { if (tok1->linkAt(-2)) tok1 = tok1->linkAt(-2)->tokAt(-1); else { if (mSettings->debugwarnings) debugMessage(tok1->tokAt(-2), "SymbolDatabase::findFunction found '>' without link."); return nullptr; } } else tok1 = tok1->tokAt(-2); } // check for global scope if (tok1->strAt(-1) == "::") { currScope = &scopeList.front(); if (Token::Match(tok1, "%name% (")) return currScope->findFunction(tok); currScope = currScope->findRecordInNestedList(tok1->str()); } // find start of qualification else { while (currScope) { if (currScope->className == tok1->str()) break; else { const Scope *scope = currScope->findRecordInNestedList(tok1->str()); if (scope) { currScope = scope; break; } else currScope = currScope->nestedIn; } } } if (currScope) { while (currScope && !(Token::Match(tok1, "%type% :: %any% (") || (Token::Match(tok1, "%type% <") && Token::Match(tok1->linkAt(1), "> :: %any% (")))) { if (tok1->strAt(1) == "::") tok1 = tok1->tokAt(2); else tok1 = tok1->linkAt(1)->tokAt(2); currScope = currScope->findRecordInNestedList(tok1->str()); } tok1 = tok1->tokAt(2); if (currScope && tok1) return currScope->findFunction(tok1); } } // check for member function else if (Token::Match(tok->tokAt(-2), "!!this .")) { const Token *tok1 = tok->tokAt(-2); if (Token::Match(tok1, "%var% .")) { const Variable *var = getVariableFromVarId(tok1->varId()); if (var && var->typeScope()) return var->typeScope()->findFunction(tok, var->valueType()->constness == 1); if (var && var->smartPointerType() && var->smartPointerType()->classScope && tok1->next()->originalName() == "->") return var->smartPointerType()->classScope->findFunction(tok, var->valueType()->constness == 1); } else if (Token::simpleMatch(tok->previous()->astOperand1(), "(")) { const Token *castTok = tok->previous()->astOperand1(); if (castTok->isCast()) { ValueType vt = ValueType::parseDecl(castTok->next(),mSettings); if (vt.typeScope) return vt.typeScope->findFunction(tok, vt.constness == 1); } } } // check in enclosing scopes else { while (currScope) { const Function *func = currScope->findFunction(tok); if (func) return func; currScope = currScope->nestedIn; } } return nullptr; } //--------------------------------------------------------------------------- const Scope *SymbolDatabase::findScopeByName(const std::string& name) const { for (std::list::const_iterator it = scopeList.begin(); it != scopeList.end(); ++it) { if (it->className == name) return &*it; } return nullptr; } //--------------------------------------------------------------------------- Scope *Scope::findInNestedList(const std::string & name) { std::list::iterator it; for (it = nestedList.begin(); it != nestedList.end(); ++it) { if ((*it)->className == name) return (*it); } return nullptr; } //--------------------------------------------------------------------------- const Scope *Scope::findRecordInNestedList(const std::string & name) const { std::list::const_iterator it; for (it = nestedList.begin(); it != nestedList.end(); ++it) { if ((*it)->className == name && (*it)->type != eFunction) return (*it); } const Type * nested_type = findType(name); if (nested_type) { if (nested_type->isTypeAlias()) { if (nested_type->typeStart == nested_type->typeEnd) return findRecordInNestedList(nested_type->typeStart->str()); } else return nested_type->classScope; } return nullptr; } //--------------------------------------------------------------------------- const Type* Scope::findType(const std::string & name) const { auto it = definedTypesMap.find(name); // Type was found if (definedTypesMap.end() != it) return (*it).second; // is type defined in anonymous namespace.. it = definedTypesMap.find(""); if (it != definedTypesMap.end()) { for (const Scope *scope : nestedList) { if (scope->className.empty() && (scope->type == eNamespace || scope->isClassOrStructOrUnion())) { const Type *t = scope->findType(name); if (t) return t; } } } // Type was not found return nullptr; } //--------------------------------------------------------------------------- Scope *Scope::findInNestedListRecursive(const std::string & name) { std::list::iterator it; for (it = nestedList.begin(); it != nestedList.end(); ++it) { if ((*it)->className == name) return (*it); } for (it = nestedList.begin(); it != nestedList.end(); ++it) { Scope *child = (*it)->findInNestedListRecursive(name); if (child) return child; } return nullptr; } //--------------------------------------------------------------------------- const Function *Scope::getDestructor() const { std::list::const_iterator it; for (it = functionList.begin(); it != functionList.end(); ++it) { if (it->type == Function::eDestructor) return &(*it); } return nullptr; } //--------------------------------------------------------------------------- bool SymbolDatabase::isCPP() const { return mTokenizer->isCPP(); } //--------------------------------------------------------------------------- const Scope *SymbolDatabase::findScope(const Token *tok, const Scope *startScope) const { const Scope *scope = nullptr; // absolute path if (tok->str() == "::") { tok = tok->next(); scope = &scopeList.front(); } // relative path else if (tok->isName()) { scope = startScope; } while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::") { scope = scope->findRecordInNestedList(tok->str()); tok = tok->tokAt(2); } else if (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::")) { scope = scope->findRecordInNestedList(tok->str()); tok = tok->linkAt(1)->tokAt(2); } else return scope->findRecordInNestedList(tok->str()); } // not a valid path return nullptr; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findType(const Token *startTok, const Scope *startScope) const { // skip over struct or union if (Token::Match(startTok, "struct|union")) startTok = startTok->next(); // type same as scope if (startTok->str() == startScope->className && startScope->isClassOrStruct() && startTok->strAt(1) != "::") return startScope->definedType; const Scope* start_scope = startScope; // absolute path - directly start in global scope if (startTok->str() == "::") { startTok = startTok->next(); start_scope = &scopeList.front(); } const Token* tok = startTok; const Scope* scope = start_scope; while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { scope = scope->findRecordInNestedList(tok->str()); if (scope) { if (tok->strAt(1) == "::") tok = tok->tokAt(2); else tok = tok->linkAt(1)->tokAt(2); } else { start_scope = start_scope->nestedIn; if (!start_scope) break; scope = start_scope; tok = startTok; } } else { const Type * type = scope->findType(tok->str()); if (type) return type; else break; } } // check using namespaces while (startScope) { for (std::list::const_iterator it = startScope->usingList.begin(); it != startScope->usingList.end(); ++it) { tok = startTok; scope = it->scope; start_scope = startScope; while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { scope = scope->findRecordInNestedList(tok->str()); if (scope) { if (tok->strAt(1) == "::") tok = tok->tokAt(2); else tok = tok->linkAt(1)->tokAt(2); } else { start_scope = start_scope->nestedIn; if (!start_scope) break; scope = start_scope; tok = startTok; } } else { const Type * type = scope->findType(tok->str()); if (type) return type; else break; } } } startScope = startScope->nestedIn; } // not a valid path return nullptr; } //--------------------------------------------------------------------------- const Type* SymbolDatabase::findTypeInNested(const Token *startTok, const Scope *startScope) const { // skip over struct or union if (Token::Match(startTok, "struct|union|enum")) startTok = startTok->next(); // type same as scope if (startTok->str() == startScope->className && startScope->isClassOrStruct()) return startScope->definedType; bool hasPath = false; // absolute path - directly start in global scope if (startTok->str() == "::") { hasPath = true; startTok = startTok->next(); startScope = &scopeList.front(); } const Token* tok = startTok; const Scope* scope = startScope; while (scope && tok && tok->isName()) { if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { hasPath = true; scope = scope->findRecordInNestedList(tok->str()); if (scope) { if (tok->strAt(1) == "::") tok = tok->tokAt(2); else tok = tok->linkAt(1)->tokAt(2); } else { startScope = startScope->nestedIn; if (!startScope) break; scope = startScope; tok = startTok; } } else { const Type * type = scope->findType(tok->str()); if (hasPath || type) return type; else { scope = scope->nestedIn; if (!scope) break; } } } // not a valid path return nullptr; } //--------------------------------------------------------------------------- const Scope * SymbolDatabase::findNamespace(const Token * tok, const Scope * scope) const { const Scope * s = findScope(tok, scope); if (s) return s; else if (scope->nestedIn) return findNamespace(tok, scope->nestedIn); return nullptr; } //--------------------------------------------------------------------------- Function * SymbolDatabase::findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length) { const Function * function = nullptr; const bool destructor = func->strAt(-1) == "~"; for (std::multimap::const_iterator it = ns->functionMap.find(func->str()); it != ns->functionMap.end() && it->first == func->str(); ++it) { if (Function::argsMatch(ns, it->second->argDef, func->next(), path, path_length) && it->second->isDestructor() == destructor) { function = it->second; break; } } if (!function) { const Scope * scope = ns->findRecordInNestedList(func->str()); if (scope && Token::Match(func->tokAt(1), "::|<")) { if (func->strAt(1) == "::") func = func->tokAt(2); else if (func->linkAt(1)) func = func->linkAt(1)->tokAt(2); else return nullptr; if (func->str() == "~") func = func->next(); function = findFunctionInScope(func, scope, path, path_length); } } return const_cast(function); } //--------------------------------------------------------------------------- namespace { #define C_KEYWORDS\ "_Bool", "auto", "break", "case", "char", "const", "continue", "default", "do",\ "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline",\ "int", "long", "register", "restrict", "return", "short", "signed", "sizeof",\ "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile",\ "while" const std::set c_keywords = { C_KEYWORDS }; const std::set cpp_keywords = { C_KEYWORDS, "alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor", "bool", "catch", "class", "compl", "concept", "constexpr", "const_cast", "decltype", "delete", "dynamic_cast", "explicit", "export", "false", "friend", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "reinterpret_cast", "requires", "static_assert", "static_cast", "template", "this", "thread_local", "throw", "true", "try", "typeid", "typename", "using", "virtual", "wchar_t", "xor", "xor_eq" }; } bool SymbolDatabase::isReservedName(const std::string& iName) const { if (isCPP()) return cpp_keywords.find(iName) != cpp_keywords.cend(); else return c_keywords.find(iName) != c_keywords.cend(); } nonneg int SymbolDatabase::sizeOfType(const Token *type) const { int size = mTokenizer->sizeOfType(type); if (size == 0 && type->type() && type->type()->isEnumType() && type->type()->classScope) { size = mSettings->sizeof_int; const Token * enum_type = type->type()->classScope->enumType; if (enum_type) size = mTokenizer->sizeOfType(enum_type); } return size; } static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Settings* settings); void SymbolDatabase::setValueType(Token *tok, const Variable &var) { ValueType valuetype; if (var.nameToken()) valuetype.bits = var.nameToken()->bits(); valuetype.pointer = var.dimensions().size(); valuetype.typeScope = var.typeScope(); if (var.valueType()) { valuetype.container = var.valueType()->container; } valuetype.smartPointerType = var.smartPointerType(); if (parsedecl(var.typeStartToken(), &valuetype, mDefaultSignedness, mSettings)) { if (tok->str() == "." && tok->astOperand1()) { const ValueType * const vt = tok->astOperand1()->valueType(); if (vt && (vt->constness & 1) != 0) valuetype.constness |= 1; } setValueType(tok, valuetype); } } void SymbolDatabase::setValueType(Token *tok, const Enumerator &enumerator) { ValueType valuetype; valuetype.typeScope = enumerator.scope; const Token * type = enumerator.scope->enumType; if (type) { valuetype.type = ValueType::typeFromString(type->str(), type->isLong()); if (valuetype.type == ValueType::Type::UNKNOWN_TYPE && type->isStandardType()) valuetype.fromLibraryType(type->str(), mSettings); if (valuetype.isIntegral()) { if (type->isSigned()) valuetype.sign = ValueType::Sign::SIGNED; else if (type->isUnsigned()) valuetype.sign = ValueType::Sign::UNSIGNED; else if (valuetype.type == ValueType::Type::CHAR) valuetype.sign = mDefaultSignedness; else valuetype.sign = ValueType::Sign::SIGNED; } setValueType(tok, valuetype); } else { valuetype.sign = ValueType::SIGNED; valuetype.type = ValueType::INT; setValueType(tok, valuetype); } } static void setAutoTokenProperties(Token * const autoTok) { const ValueType *valuetype = autoTok->valueType(); if (valuetype->isIntegral() || valuetype->isFloat()) autoTok->isStandardType(true); } void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype) { tok->setValueType(new ValueType(valuetype)); Token *parent = tok->astParent(); if (!parent || parent->valueType()) return; if (!parent->astOperand1()) return; const ValueType *vt1 = parent->astOperand1() ? parent->astOperand1()->valueType() : nullptr; const ValueType *vt2 = parent->astOperand2() ? parent->astOperand2()->valueType() : nullptr; if (vt1 && Token::Match(parent, "<<|>>")) { if (!mIsCpp || (vt2 && vt2->isIntegral())) setValueType(parent, *vt1); return; } if (vt1 && vt1->smartPointerType && Token::Match(parent, ". %name% (") && parent->originalName() == "->" && !parent->next()->function()) { const Scope *scope = vt1->smartPointerType->classScope; const Function *f = scope ? scope->findFunction(parent->next(), false) : nullptr; if (f) parent->next()->function(f); } if (parent->isAssignmentOp()) { if (vt1) setValueType(parent, *vt1); else if (mIsCpp && ((Token::Match(parent->tokAt(-3), "%var% ; %var% =") && parent->strAt(-3) == parent->strAt(-1)) || Token::Match(parent->tokAt(-1), "%var% ="))) { Token *var1Tok = parent->strAt(-2) == ";" ? parent->tokAt(-3) : parent->tokAt(-1); Token *autoTok = nullptr; if (Token::Match(var1Tok->tokAt(-2), ";|{|}|(|const auto")) autoTok = var1Tok->previous(); else if (Token::Match(var1Tok->tokAt(-3), ";|{|}|(|const auto *")) autoTok = var1Tok->tokAt(-2); if (autoTok) { ValueType vt(*vt2); if (vt.constness & (1 << vt.pointer)) vt.constness &= ~(1 << vt.pointer); if (autoTok->strAt(1) == "*" && vt.pointer) vt.pointer--; if (autoTok->strAt(-1) == "const") vt.constness |= 1; setValueType(autoTok, vt); setAutoTokenProperties(autoTok); if (vt2->pointer > vt.pointer) vt.pointer++; setValueType(var1Tok, vt); if (var1Tok != parent->previous()) setValueType(parent->previous(), vt); Variable *var = const_cast(parent->previous()->variable()); if (var) { ValueType vt2_(*vt2); if (vt2_.pointer == 0 && autoTok->strAt(1) == "*") vt2_.pointer = 1; if ((vt.constness & (1 << vt2->pointer)) != 0) vt2_.constness |= (1 << vt2->pointer); if (!Token::Match(autoTok->tokAt(1), "*|&")) vt2_.constness = vt.constness; var->setValueType(vt2_); if (vt2->typeScope && vt2->typeScope->definedType) { var->type(vt2->typeScope->definedType); if (autoTok->valueType()->pointer == 0) autoTok->type(vt2->typeScope->definedType); } } } } return; } if (parent->str() == "[" && (!mIsCpp || parent->astOperand1() == tok) && valuetype.pointer > 0U && !Token::Match(parent->previous(), "[{,]")) { const Token *op1 = parent->astOperand1(); while (op1 && op1->str() == "[") op1 = op1->astOperand1(); ValueType vt(valuetype); // the "[" is a dereference unless this is a variable declaration if (!(op1 && op1->variable() && op1->variable()->nameToken() == op1)) vt.pointer -= 1U; setValueType(parent, vt); return; } if (Token::Match(parent->previous(), "%name% (") && parent->astOperand1() == tok && valuetype.pointer > 0U) { ValueType vt(valuetype); vt.pointer -= 1U; setValueType(parent, vt); return; } // std::move if (vt2 && parent->str() == "(" && Token::simpleMatch(parent->tokAt(-3), "std :: move (")) { setValueType(parent, valuetype); return; } if (parent->str() == "*" && !parent->astOperand2() && valuetype.pointer > 0U) { ValueType vt(valuetype); vt.pointer -= 1U; setValueType(parent, vt); return; } if (parent->str() == "*" && Token::simpleMatch(parent->astOperand2(), "[") && valuetype.pointer > 0U) { const Token *op1 = parent->astOperand2()->astOperand1(); while (op1 && op1->str() == "[") op1 = op1->astOperand1(); ValueType vt(valuetype); if (op1 && op1->variable() && op1->variable()->nameToken() == op1) { setValueType(parent, vt); return; } } if (parent->str() == "&" && !parent->astOperand2()) { ValueType vt(valuetype); vt.pointer += 1U; setValueType(parent, vt); return; } if ((parent->str() == "." || parent->str() == "::") && parent->astOperand2() && parent->astOperand2()->isName()) { const Variable* var = parent->astOperand2()->variable(); if (!var && valuetype.typeScope && vt1) { const std::string &name = parent->astOperand2()->str(); const Scope *typeScope = vt1->typeScope; if (!typeScope) return; for (std::list::const_iterator it = typeScope->varlist.begin(); it != typeScope->varlist.end(); ++it) { if (it->nameToken()->str() == name) { var = &*it; break; } } } if (var) setValueType(parent, *var); return; } // range for loop, auto if (vt2 && parent->str() == ":" && Token::Match(parent->astParent(), "( const| auto *|&| %var% :") && !parent->previous()->valueType() && Token::simpleMatch(parent->astParent()->astOperand1(), "for")) { const bool isconst = Token::simpleMatch(parent->astParent()->next(), "const"); Token * const autoToken = parent->astParent()->tokAt(isconst ? 2 : 1); if (vt2->pointer) { ValueType autovt(*vt2); autovt.pointer--; autovt.constness = 0; setValueType(autoToken, autovt); setAutoTokenProperties(autoToken); ValueType varvt(*vt2); varvt.pointer--; if (isconst) varvt.constness |= 1; setValueType(parent->previous(), varvt); Variable *var = const_cast(parent->previous()->variable()); if (var) { var->setValueType(varvt); if (vt2->typeScope && vt2->typeScope->definedType) { var->type(vt2->typeScope->definedType); autoToken->type(vt2->typeScope->definedType); } } } else if (vt2->container) { // TODO: Determine exact type of RHS const Token *typeStart = parent->astOperand2(); while (typeStart) { if (typeStart->variable()) typeStart = typeStart->variable()->typeStartToken(); else if (typeStart->str() == "(" && typeStart->previous() && typeStart->previous()->function()) typeStart = typeStart->previous()->function()->retDef; else break; } // Try to determine type of "auto" token. // TODO: Get type better bool setType = false; ValueType autovt; const Type *templateArgType = nullptr; // container element type / smart pointer type if (vt2->containerTypeToken) { if (mSettings->library.isSmartPointer(vt2->containerTypeToken)) { const Token *smartPointerTypeTok = vt2->containerTypeToken; while (Token::Match(smartPointerTypeTok, "%name%|::")) smartPointerTypeTok = smartPointerTypeTok->next(); if (Token::Match(smartPointerTypeTok, "< %name% > >") && smartPointerTypeTok->next()->type()) { setType = true; templateArgType = smartPointerTypeTok->next()->type(); autovt.smartPointerType = templateArgType; autovt.type = ValueType::Type::NONSTD; } } else if (parsedecl(vt2->containerTypeToken, &autovt, mDefaultSignedness, mSettings)) { setType = true; templateArgType = vt2->containerTypeToken->type(); } } if (setType) { // Type of "auto" has been determined.. set type information for "auto" and variable tokens setValueType(autoToken, autovt); setAutoTokenProperties(autoToken); ValueType varvt(autovt); if (isconst) varvt.constness |= 1; setValueType(parent->previous(), varvt); Variable * var = const_cast(parent->previous()->variable()); if (var) { var->setValueType(varvt); if (templateArgType && templateArgType->classScope && templateArgType->classScope->definedType) { autoToken->type(templateArgType->classScope->definedType); var->type(templateArgType->classScope->definedType); } } } } } if (vt1 && vt1->containerTypeToken && parent->str() == "[") { ValueType vtParent; if (parsedecl(vt1->containerTypeToken, &vtParent, mDefaultSignedness, mSettings)) { setValueType(parent, vtParent); return; } } if (!vt1) return; if (parent->astOperand2() && !vt2) return; const bool ternary = parent->str() == ":" && parent->astParent() && parent->astParent()->str() == "?"; if (ternary) { if (vt2 && vt1->pointer == vt2->pointer && vt1->type == vt2->type && vt1->sign == vt2->sign) setValueType(parent, *vt2); parent = parent->astParent(); } if (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eIncDecOp) { if (vt1->pointer != 0U && vt2 && vt2->pointer == 0U) { setValueType(parent, *vt1); return; } if (vt1->pointer == 0U && vt2 && vt2->pointer != 0U) { setValueType(parent, *vt2); return; } if (vt1->pointer != 0U) { if (ternary || parent->tokType() == Token::eIncDecOp) // result is pointer setValueType(parent, *vt1); else // result is pointer diff setValueType(parent, ValueType(ValueType::Sign::SIGNED, ValueType::Type::INT, 0U, 0U, "ptrdiff_t")); return; } if (vt1->type == ValueType::Type::LONGDOUBLE || (vt2 && vt2->type == ValueType::Type::LONGDOUBLE)) { setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::LONGDOUBLE, 0U)); return; } if (vt1->type == ValueType::Type::DOUBLE || (vt2 && vt2->type == ValueType::Type::DOUBLE)) { setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::DOUBLE, 0U)); return; } if (vt1->type == ValueType::Type::FLOAT || (vt2 && vt2->type == ValueType::Type::FLOAT)) { setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U)); return; } // iterator +/- integral = iterator if (vt1->type == ValueType::Type::ITERATOR && vt2 && vt2->isIntegral() && (parent->str() == "+" || parent->str() == "-")) { setValueType(parent, *vt1); return; } if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->type == ValueType::Type::CONTAINER && vt1->container == vt2->container) { setValueType(parent, *vt1); return; } } if (vt1->isIntegral() && vt1->pointer == 0U && (!vt2 || (vt2->isIntegral() && vt2->pointer == 0U)) && (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eBitOp || parent->tokType() == Token::eIncDecOp || parent->isAssignmentOp())) { ValueType vt; if (!vt2 || vt1->type > vt2->type) { vt.type = vt1->type; vt.sign = vt1->sign; vt.originalTypeName = vt1->originalTypeName; } else if (vt1->type == vt2->type) { vt.type = vt1->type; if (vt1->sign == ValueType::Sign::UNSIGNED || vt2->sign == ValueType::Sign::UNSIGNED) vt.sign = ValueType::Sign::UNSIGNED; else if (vt1->sign == ValueType::Sign::UNKNOWN_SIGN || vt2->sign == ValueType::Sign::UNKNOWN_SIGN) vt.sign = ValueType::Sign::UNKNOWN_SIGN; else vt.sign = ValueType::Sign::SIGNED; vt.originalTypeName = (vt1->originalTypeName.empty() ? vt2 : vt1)->originalTypeName; } else { vt.type = vt2->type; vt.sign = vt2->sign; vt.originalTypeName = vt2->originalTypeName; } if (vt.type < ValueType::Type::INT && !(ternary && vt.type==ValueType::Type::BOOL)) { vt.type = ValueType::Type::INT; vt.sign = ValueType::Sign::SIGNED; vt.originalTypeName.clear(); } setValueType(parent, vt); return; } } static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Settings* settings) { const Token * const previousType = type; const unsigned int pointer0 = valuetype->pointer; while (Token::Match(type->previous(), "%name%")) type = type->previous(); valuetype->sign = ValueType::Sign::UNKNOWN_SIGN; if (!valuetype->typeScope && !valuetype->smartPointerType) valuetype->type = ValueType::Type::UNKNOWN_TYPE; else if (valuetype->smartPointerType) valuetype->type = ValueType::Type::NONSTD; else if (valuetype->typeScope->type == Scope::eEnum) { const Token * enum_type = valuetype->typeScope->enumType; if (enum_type) { if (enum_type->isSigned()) valuetype->sign = ValueType::Sign::SIGNED; else if (enum_type->isUnsigned()) valuetype->sign = ValueType::Sign::UNSIGNED; else valuetype->sign = defaultSignedness; const ValueType::Type t = ValueType::typeFromString(enum_type->str(), enum_type->isLong()); if (t != ValueType::Type::UNKNOWN_TYPE) valuetype->type = t; else if (enum_type->isStandardType()) valuetype->fromLibraryType(enum_type->str(), settings); } else valuetype->type = ValueType::Type::INT; } else valuetype->type = ValueType::Type::RECORD; while (Token::Match(type, "%name%|*|&|::") && !Token::Match(type, "typename|template") && !type->variable() && !type->function()) { if (type->isSigned()) valuetype->sign = ValueType::Sign::SIGNED; else if (type->isUnsigned()) valuetype->sign = ValueType::Sign::UNSIGNED; if (valuetype->type == ValueType::Type::UNKNOWN_TYPE && type->type() && type->type()->isTypeAlias() && type->type()->typeStart && type->type()->typeStart->str() != type->str() && type->type()->typeStart != previousType) parsedecl(type->type()->typeStart, valuetype, defaultSignedness, settings); else if (type->str() == "const") valuetype->constness |= (1 << (valuetype->pointer - pointer0)); else if (const Library::Container *container = settings->library.detectContainer(type)) { valuetype->type = ValueType::Type::CONTAINER; valuetype->container = container; while (Token::Match(type, "%name%|::|<")) { if (type->str() == "<" && type->link()) { if (container->type_templateArgNo >= 0) { const Token *templateType = type->next(); for (int j = 0; templateType && j < container->type_templateArgNo; j++) templateType = templateType->nextTemplateArgument(); valuetype->containerTypeToken = templateType; } type = type->link(); } type = type->next(); } continue; } else if (settings->library.isSmartPointer(type)) { const Token* argTok = Token::findsimplematch(type, "<"); if (!argTok) continue; valuetype->smartPointerTypeToken = argTok->next(); valuetype->smartPointerType = argTok->next()->type(); valuetype->type = ValueType::Type::NONSTD; type = argTok->link(); continue; } else if (Token::Match(type, "%name% :: %name%")) { std::string typestr; const Token *end = type; while (Token::Match(end, "%name% :: %name%")) { typestr += end->str() + "::"; end = end->tokAt(2); } typestr += end->str(); if (valuetype->fromLibraryType(typestr, settings)) type = end; } else if (ValueType::Type::UNKNOWN_TYPE != ValueType::typeFromString(type->str(), type->isLong())) valuetype->type = ValueType::typeFromString(type->str(), type->isLong()); else if (type->str() == "auto") { const ValueType *vt = type->valueType(); if (!vt) return nullptr; valuetype->type = vt->type; valuetype->pointer = vt->pointer; if (vt->sign != ValueType::Sign::UNKNOWN_SIGN) valuetype->sign = vt->sign; valuetype->constness = vt->constness; valuetype->originalTypeName = vt->originalTypeName; while (Token::Match(type, "%name%|*|&|::") && !type->variable()) type = type->next(); break; } else if (!valuetype->typeScope && (type->str() == "struct" || type->str() == "enum")) valuetype->type = type->str() == "struct" ? ValueType::Type::RECORD : ValueType::Type::NONSTD; else if (!valuetype->typeScope && type->type() && type->type()->classScope) { valuetype->type = ValueType::Type::RECORD; valuetype->typeScope = type->type()->classScope; } else if (type->isName() && valuetype->sign != ValueType::Sign::UNKNOWN_SIGN && valuetype->pointer == 0U) return nullptr; else if (type->str() == "*") valuetype->pointer++; else if (type->isStandardType()) valuetype->fromLibraryType(type->str(), settings); else if (Token::Match(type->previous(), "!!:: %name% !!::")) valuetype->fromLibraryType(type->str(), settings); if (!type->originalName().empty()) valuetype->originalTypeName = type->originalName(); type = type->next(); } // Set signedness for integral types.. if (valuetype->isIntegral() && valuetype->sign == ValueType::Sign::UNKNOWN_SIGN) { if (valuetype->type == ValueType::Type::CHAR) valuetype->sign = defaultSignedness; else if (valuetype->type >= ValueType::Type::SHORT) valuetype->sign = ValueType::Sign::SIGNED; } return (type && (valuetype->type != ValueType::Type::UNKNOWN_TYPE || valuetype->pointer > 0)) ? type : nullptr; } static const Scope *getClassScope(const Token *tok) { return tok && tok->valueType() && tok->valueType()->typeScope && tok->valueType()->typeScope->isClassOrStruct() ? tok->valueType()->typeScope : nullptr; } static const Function *getOperatorFunction(const Token * const tok) { const std::string functionName("operator" + tok->str()); std::multimap::const_iterator it; const Scope *classScope = getClassScope(tok->astOperand1()); if (classScope) { it = classScope->functionMap.find(functionName); if (it != classScope->functionMap.end()) return it->second; } classScope = getClassScope(tok->astOperand2()); if (classScope) { it = classScope->functionMap.find(functionName); if (it != classScope->functionMap.end()) return it->second; } return nullptr; } void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings) { Token * tokens = const_cast(mTokenizer)->list.front(); for (Token *tok = tokens; tok; tok = tok->next()) tok->setValueType(nullptr); for (Token *tok = tokens; tok; tok = tok->next()) { if (tok->isNumber()) { if (MathLib::isFloat(tok->str())) { ValueType::Type type = ValueType::Type::DOUBLE; const char suffix = tok->str()[tok->str().size() - 1]; if (suffix == 'f' || suffix == 'F') type = ValueType::Type::FLOAT; else if (suffix == 'L' || suffix == 'l') type = ValueType::Type::LONGDOUBLE; setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, type, 0U)); } else if (MathLib::isInt(tok->str())) { const bool unsignedSuffix = (tok->str().find_last_of("uU") != std::string::npos); ValueType::Sign sign = unsignedSuffix ? ValueType::Sign::UNSIGNED : ValueType::Sign::SIGNED; ValueType::Type type; const MathLib::bigint value = MathLib::toLongNumber(tok->str()); if (mSettings->platformType == cppcheck::Platform::Unspecified) type = ValueType::Type::INT; else if (mSettings->isIntValue(unsignedSuffix ? (value >> 1) : value)) type = ValueType::Type::INT; else if (mSettings->isLongValue(unsignedSuffix ? (value >> 1) : value)) type = ValueType::Type::LONG; else type = ValueType::Type::LONGLONG; if (MathLib::isIntHex(tok->str())) sign = ValueType::Sign::UNSIGNED; for (std::size_t pos = tok->str().size() - 1U; pos > 0U; --pos) { const char suffix = tok->str()[pos]; if (suffix == 'u' || suffix == 'U') sign = ValueType::Sign::UNSIGNED; else if (suffix == 'l' || suffix == 'L') type = (type == ValueType::Type::INT) ? ValueType::Type::LONG : ValueType::Type::LONGLONG; else if (pos > 2U && suffix == '4' && tok->str()[pos - 1] == '6' && tok->str()[pos - 2] == 'i') { type = ValueType::Type::LONGLONG; pos -= 2; } else break; } setValueType(tok, ValueType(sign, type, 0U)); } } else if (tok->isComparisonOp() || tok->tokType() == Token::eLogicalOp) { if (mIsCpp && tok->isComparisonOp() && (getClassScope(tok->astOperand1()) || getClassScope(tok->astOperand2()))) { const Function *function = getOperatorFunction(tok); if (function) { ValueType vt; parsedecl(function->retDef, &vt, mDefaultSignedness, mSettings); setValueType(tok, vt); continue; } } setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); } else if (tok->isBoolean()) { setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); } else if (tok->tokType() == Token::eChar || tok->tokType() == Token::eString) { nonneg int pointer = tok->tokType() == Token::eChar ? 0U : 1U; nonneg int constness = tok->tokType() == Token::eChar ? 0U : 1U; ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, pointer, constness); if (mIsCpp && mSettings->standards.cpp >= Standards::CPP20 && tok->isUtf8()) { valuetype.originalTypeName = "char8_t"; valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); } else if (tok->isUtf16()) { valuetype.originalTypeName = "char16_t"; valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); } else if (tok->isUtf32()) { valuetype.originalTypeName = "char32_t"; valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); } else if (tok->isLong()) { valuetype.originalTypeName = "wchar_t"; valuetype.type = ValueType::Type::WCHAR_T; } else if ((tok->tokType() == Token::eChar) && ((tok->isCChar() && !mIsCpp) || (tok->isCMultiChar()))) { valuetype.type = ValueType::Type::INT; valuetype.sign = ValueType::Sign::SIGNED; } setValueType(tok, valuetype); } else if (tok->str() == "(") { // cast if (tok->isCast() && !tok->astOperand2() && Token::Match(tok, "( %name%")) { ValueType valuetype; if (Token::simpleMatch(parsedecl(tok->next(), &valuetype, mDefaultSignedness, mSettings), ")")) setValueType(tok, valuetype); } // C++ cast else if (tok->astOperand2() && Token::Match(tok->astOperand1(), "static_cast|const_cast|dynamic_cast|reinterpret_cast < %name%") && tok->astOperand1()->linkAt(1)) { ValueType valuetype; if (Token::simpleMatch(parsedecl(tok->astOperand1()->tokAt(2), &valuetype, mDefaultSignedness, mSettings), ">")) setValueType(tok, valuetype); } // function else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->retDef) { ValueType valuetype; if (parsedecl(tok->previous()->function()->retDef, &valuetype, mDefaultSignedness, mSettings)) setValueType(tok, valuetype); } else if (Token::simpleMatch(tok->previous(), "sizeof (")) { // TODO: use specified size_t type ValueType valuetype(ValueType::Sign::UNSIGNED, ValueType::Type::LONG, 0U); valuetype.originalTypeName = "size_t"; setValueType(tok, valuetype); if (Token::Match(tok, "( %type% %type%| *| *| )")) { ValueType vt; if (parsedecl(tok->next(), &vt, mDefaultSignedness, mSettings)) { setValueType(tok->next(), vt); } } } // function style cast else if (tok->previous() && tok->previous()->isStandardType()) { ValueType valuetype; if (tok->astOperand1() && valuetype.fromLibraryType(tok->astOperand1()->expressionString(), mSettings)) { setValueType(tok, valuetype); continue; } valuetype.type = ValueType::typeFromString(tok->previous()->str(), tok->previous()->isLong()); if (tok->previous()->isUnsigned()) valuetype.sign = ValueType::Sign::UNSIGNED; else if (tok->previous()->isSigned()) valuetype.sign = ValueType::Sign::SIGNED; setValueType(tok, valuetype); } // constructor call else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->isConstructor()) { ValueType valuetype; valuetype.type = ValueType::RECORD; valuetype.typeScope = tok->previous()->function()->token->scope(); setValueType(tok, valuetype); } // library type/function else if (tok->previous()) { if (tok->astParent() && Token::Match(tok->astOperand1(), "%name%|::")) { const Token *typeStartToken = tok->astOperand1(); while (typeStartToken && typeStartToken->str() == "::") typeStartToken = typeStartToken->astOperand1(); if (const Library::Container *c = mSettings->library.detectContainer(typeStartToken)) { ValueType vt; vt.pointer = 0; vt.container = c; vt.type = ValueType::Type::CONTAINER; setValueType(tok, vt); continue; } const std::string e = tok->astOperand1()->expressionString(); if ((e == "std::make_shared" || e == "std::make_unique") && Token::Match(tok->astOperand1(), ":: %name% < %name%")) { ValueType vt; parsedecl(tok->astOperand1()->tokAt(3), &vt, mDefaultSignedness, mSettings); if (vt.typeScope) { vt.smartPointerType = vt.typeScope->definedType; vt.typeScope = nullptr; setValueType(tok, vt); continue; } } ValueType podtype; if (podtype.fromLibraryType(e, mSettings)) { setValueType(tok, podtype); continue; } } const std::string& typestr(mSettings->library.returnValueType(tok->previous())); if (!typestr.empty()) { ValueType valuetype; if (valuetype.fromLibraryType(typestr, mSettings)) { setValueType(tok, valuetype); } } if (typestr.empty() || typestr == "iterator") { if (Token::simpleMatch(tok->astOperand1(), ".") && tok->astOperand1()->astOperand1() && tok->astOperand1()->astOperand2() && tok->astOperand1()->astOperand1()->valueType() && tok->astOperand1()->astOperand1()->valueType()->container) { const Library::Container *cont = tok->astOperand1()->astOperand1()->valueType()->container; const std::map::const_iterator it = cont->functions.find(tok->astOperand1()->astOperand2()->str()); if (it != cont->functions.end()) { if (it->second.yield == Library::Container::Yield::START_ITERATOR || it->second.yield == Library::Container::Yield::END_ITERATOR || it->second.yield == Library::Container::Yield::ITERATOR) { ValueType vt; vt.type = ValueType::Type::ITERATOR; vt.container = cont; setValueType(tok, vt); } } } continue; } TokenList tokenList(mSettings); std::istringstream istr(typestr+";"); if (tokenList.createTokens(istr)) { ValueType vt; tokenList.simplifyPlatformTypes(); tokenList.simplifyStdType(); if (parsedecl(tokenList.front(), &vt, mDefaultSignedness, mSettings)) { setValueType(tok, vt); } } } } else if (tok->variable()) { setValueType(tok, *tok->variable()); } else if (tok->enumerator()) { setValueType(tok, *tok->enumerator()); } else if (mIsCpp && tok->str() == "new") { const Token *typeTok = tok->next(); if (Token::Match(typeTok, "( std| ::| nothrow )")) typeTok = typeTok->link()->next(); if (const Library::Container *c = mSettings->library.detectContainer(typeTok)) { ValueType vt; vt.pointer = 1; vt.container = c; vt.type = ValueType::Type::CONTAINER; setValueType(tok, vt); continue; } std::string typestr; while (Token::Match(typeTok, "%name% :: %name%")) { typestr += typeTok->str() + "::"; typeTok = typeTok->tokAt(2); } if (!Token::Match(typeTok, "%type% ;|[|(")) continue; typestr += typeTok->str(); ValueType vt; vt.pointer = 1; if (typeTok->type() && typeTok->type()->classScope) { vt.type = ValueType::Type::RECORD; vt.typeScope = typeTok->type()->classScope; } else { vt.type = ValueType::typeFromString(typestr, typeTok->isLong()); if (vt.type == ValueType::Type::UNKNOWN_TYPE) vt.fromLibraryType(typestr, mSettings); if (vt.type == ValueType::Type::UNKNOWN_TYPE) continue; if (typeTok->isUnsigned()) vt.sign = ValueType::Sign::UNSIGNED; else if (typeTok->isSigned()) vt.sign = ValueType::Sign::SIGNED; if (vt.sign == ValueType::Sign::UNKNOWN_SIGN && vt.isIntegral()) vt.sign = (vt.type == ValueType::Type::CHAR) ? mDefaultSignedness : ValueType::Sign::SIGNED; } setValueType(tok, vt); } else if (tok->str() == "return" && tok->scope()) { const Scope* fscope = tok->scope(); while (fscope && !fscope->function) fscope = fscope->nestedIn; if (fscope && fscope->function && fscope->function->retDef) { ValueType vt; parsedecl(fscope->function->retDef, &vt, mDefaultSignedness, mSettings); setValueType(tok, vt); } } } if (reportDebugWarnings && mSettings->debugwarnings) { for (Token *tok = tokens; tok; tok = tok->next()) { if (tok->str() == "auto" && !tok->valueType()) debugMessage(tok, "auto token with no type."); } } // Update functions with new type information. createSymbolDatabaseSetFunctionPointers(false); // Update auto variables with new type information. createSymbolDatabaseSetVariablePointers(); } ValueType ValueType::parseDecl(const Token *type, const Settings *settings) { ValueType vt; parsedecl(type, &vt, settings->defaultSign == 'u' ? Sign::UNSIGNED : Sign::SIGNED, settings); return vt; } ValueType::Type ValueType::typeFromString(const std::string &typestr, bool longType) { if (typestr == "void") return ValueType::Type::VOID; if (typestr == "bool" || typestr == "_Bool") return ValueType::Type::BOOL; if (typestr== "char") return ValueType::Type::CHAR; if (typestr == "short") return ValueType::Type::SHORT; if (typestr == "wchar_t") return ValueType::Type::WCHAR_T; if (typestr == "int") return ValueType::Type::INT; if (typestr == "long") return longType ? ValueType::Type::LONGLONG : ValueType::Type::LONG; if (typestr == "float") return ValueType::Type::FLOAT; if (typestr == "double") return longType ? ValueType::Type::LONGDOUBLE : ValueType::Type::DOUBLE; return ValueType::Type::UNKNOWN_TYPE; } bool ValueType::fromLibraryType(const std::string &typestr, const Settings *settings) { const Library::PodType* podtype = settings->library.podtype(typestr); if (podtype && (podtype->sign == 's' || podtype->sign == 'u')) { if (podtype->size == 1) type = ValueType::Type::CHAR; else if (podtype->size == settings->sizeof_int) type = ValueType::Type::INT; else if (podtype->size == settings->sizeof_short) type = ValueType::Type::SHORT; else if (podtype->size == settings->sizeof_long) type = ValueType::Type::LONG; else if (podtype->size == settings->sizeof_long_long) type = ValueType::Type::LONGLONG; else if (podtype->stdtype == Library::PodType::BOOL) type = ValueType::Type::BOOL; else if (podtype->stdtype == Library::PodType::CHAR) type = ValueType::Type::CHAR; else if (podtype->stdtype == Library::PodType::SHORT) type = ValueType::Type::SHORT; else if (podtype->stdtype == Library::PodType::INT) type = ValueType::Type::INT; else if (podtype->stdtype == Library::PodType::LONG) type = ValueType::Type::LONG; else if (podtype->stdtype == Library::PodType::LONGLONG) type = ValueType::Type::LONGLONG; else type = ValueType::Type::UNKNOWN_INT; sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED; return true; } const Library::PlatformType *platformType = settings->library.platform_type(typestr, settings->platformString()); if (platformType) { if (platformType->mType == "char") type = ValueType::Type::CHAR; else if (platformType->mType == "short") type = ValueType::Type::SHORT; else if (platformType->mType == "wchar_t") type = ValueType::Type::WCHAR_T; else if (platformType->mType == "int") type = platformType->mLong ? ValueType::Type::LONG : ValueType::Type::INT; else if (platformType->mType == "long") type = platformType->mLong ? ValueType::Type::LONGLONG : ValueType::Type::LONG; if (platformType->mSigned) sign = ValueType::SIGNED; else if (platformType->mUnsigned) sign = ValueType::UNSIGNED; if (platformType->mPointer) pointer = 1; if (platformType->mPtrPtr) pointer = 2; if (platformType->mConstPtr) constness = 1; return true; } else if (!podtype && (typestr == "size_t" || typestr == "std::size_t")) { originalTypeName = "size_t"; sign = ValueType::UNSIGNED; if (settings->sizeof_size_t == settings->sizeof_long) type = ValueType::Type::LONG; else if (settings->sizeof_size_t == settings->sizeof_long_long) type = ValueType::Type::LONGLONG; else if (settings->sizeof_size_t == settings->sizeof_int) type = ValueType::Type::INT; else type = ValueType::Type::UNKNOWN_INT; return true; } return false; } std::string ValueType::dump() const { std::ostringstream ret; switch (type) { case UNKNOWN_TYPE: return ""; case NONSTD: ret << "valueType-type=\"nonstd\""; break; case RECORD: ret << "valueType-type=\"record\""; break; case CONTAINER: ret << "valueType-type=\"container\""; break; case ITERATOR: ret << "valueType-type=\"iterator\""; break; case VOID: ret << "valueType-type=\"void\""; break; case BOOL: ret << "valueType-type=\"bool\""; break; case CHAR: ret << "valueType-type=\"char\""; break; case SHORT: ret << "valueType-type=\"short\""; break; case WCHAR_T: ret << "valueType-type=\"wchar_t\""; break; case INT: ret << "valueType-type=\"int\""; break; case LONG: ret << "valueType-type=\"long\""; break; case LONGLONG: ret << "valueType-type=\"long long\""; break; case UNKNOWN_INT: ret << "valueType-type=\"unknown int\""; break; case FLOAT: ret << "valueType-type=\"float\""; break; case DOUBLE: ret << "valueType-type=\"double\""; break; case LONGDOUBLE: ret << "valueType-type=\"long double\""; break; }; switch (sign) { case Sign::UNKNOWN_SIGN: break; case Sign::SIGNED: ret << " valueType-sign=\"signed\""; break; case Sign::UNSIGNED: ret << " valueType-sign=\"unsigned\""; break; }; if (bits > 0) ret << " valueType-bits=\"" << bits << '\"'; if (pointer > 0) ret << " valueType-pointer=\"" << pointer << '\"'; if (constness > 0) ret << " valueType-constness=\"" << constness << '\"'; if (typeScope) ret << " valueType-typeScope=\"" << typeScope << '\"'; if (!originalTypeName.empty()) ret << " valueType-originalTypeName=\"" << originalTypeName << '\"'; return ret.str(); } MathLib::bigint ValueType::typeSize(const cppcheck::Platform &platform) const { switch (type) { case ValueType::Type::BOOL: return platform.sizeof_bool; case ValueType::Type::CHAR: return 1; case ValueType::Type::SHORT: return platform.sizeof_short; case ValueType::Type::WCHAR_T: return platform.sizeof_wchar_t; case ValueType::Type::INT: return platform.sizeof_int; case ValueType::Type::LONG: return platform.sizeof_long; case ValueType::Type::LONGLONG: return platform.sizeof_long_long; case ValueType::Type::FLOAT: return platform.sizeof_float; case ValueType::Type::DOUBLE: return platform.sizeof_double; case ValueType::Type::LONGDOUBLE: return platform.sizeof_long_double; default: return 0; }; } std::string ValueType::str() const { std::string ret; if (constness & 1) ret = " const"; if (type == VOID) ret += " void"; else if (isIntegral()) { if (sign == SIGNED) ret += " signed"; else if (sign == UNSIGNED) ret += " unsigned"; if (type == BOOL) ret += " bool"; else if (type == CHAR) ret += " char"; else if (type == SHORT) ret += " short"; else if (type == WCHAR_T) ret += " wchar_t"; else if (type == INT) ret += " int"; else if (type == LONG) ret += " long"; else if (type == LONGLONG) ret += " long long"; else if (type == UNKNOWN_INT) ret += " unknown_int"; } else if (type == FLOAT) ret += " float"; else if (type == DOUBLE) ret += " double"; else if (type == LONGDOUBLE) ret += " long double"; else if ((type == ValueType::Type::NONSTD || type == ValueType::Type::RECORD) && typeScope) { std::string className(typeScope->className); const Scope *scope = typeScope->definedType ? typeScope->definedType->enclosingScope : typeScope->nestedIn; while (scope && scope->type != Scope::eGlobal) { if (scope->type == Scope::eClass || scope->type == Scope::eStruct || scope->type == Scope::eNamespace) className = scope->className + "::" + className; scope = scope->definedType ? scope->definedType->enclosingScope : scope->nestedIn; } ret += ' ' + className; } else if (type == ValueType::Type::CONTAINER && container) { ret += " container(" + container->startPattern + ')'; } else if (type == ValueType::Type::ITERATOR && container) { ret += " iterator(" + container->startPattern + ')'; } else if (smartPointerType) { ret += " smart-pointer<" + smartPointerType->name() + ">"; } for (unsigned int p = 0; p < pointer; p++) { ret += " *"; if (constness & (2 << p)) ret += " const"; } return ret.empty() ? ret : ret.substr(1); } ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const ValueType *func) { if (!call || !func) return ValueType::MatchResult::UNKNOWN; if (call->pointer != func->pointer) { if (call->pointer > 1 && func->pointer == 1 && func->type == ValueType::Type::VOID) return ValueType::MatchResult::FALLBACK1; if (call->pointer == 1 && func->pointer == 0 && func->isIntegral() && func->sign != ValueType::Sign::SIGNED) return ValueType::MatchResult::FALLBACK1; if (call->pointer == 1 && call->type == ValueType::Type::CHAR && func->pointer == 0 && func->container && func->container->stdStringLike) return ValueType::MatchResult::FALLBACK2; return ValueType::MatchResult::NOMATCH; // TODO } if (call->pointer > 0 && ((call->constness | func->constness) != func->constness)) return ValueType::MatchResult::NOMATCH; if (call->type != func->type) { if (call->type == ValueType::Type::VOID || func->type == ValueType::Type::VOID) return ValueType::MatchResult::FALLBACK1; if (call->pointer > 0 && func->pointer > 0) return func->type == ValueType::UNKNOWN_TYPE ? ValueType::MatchResult::UNKNOWN : ValueType::MatchResult::NOMATCH; if (call->isIntegral() && func->isIntegral()) return call->type < func->type ? ValueType::MatchResult::FALLBACK1 : ValueType::MatchResult::FALLBACK2; else if (call->isFloat() && func->isFloat()) return ValueType::MatchResult::FALLBACK1; else if (call->isIntegral() && func->isFloat()) return ValueType::MatchResult::FALLBACK2; else if (call->isFloat() && func->isIntegral()) return ValueType::MatchResult::FALLBACK2; return ValueType::MatchResult::UNKNOWN; // TODO } if (call->typeScope != nullptr || func->typeScope != nullptr) return call->typeScope == func->typeScope ? ValueType::MatchResult::SAME : ValueType::MatchResult::NOMATCH; if (call->container != nullptr || func->container != nullptr) { if (call->container != func->container) return ValueType::MatchResult::NOMATCH; } else if (func->type < ValueType::Type::VOID || func->type == ValueType::Type::UNKNOWN_INT) return ValueType::MatchResult::UNKNOWN; if (call->isIntegral() && func->isIntegral() && call->sign != ValueType::Sign::UNKNOWN_SIGN && func->sign != ValueType::Sign::UNKNOWN_SIGN && call->sign != func->sign) return ValueType::MatchResult::FALLBACK1; return ValueType::MatchResult::SAME; } ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar) { ValueType::MatchResult res = ValueType::matchParameter(call, funcVar->valueType()); if (res == ValueType::MatchResult::SAME && callVar && call->container) { const std::string type1 = getTypeString(callVar->typeStartToken()); const std::string type2 = getTypeString(funcVar->typeStartToken()); if (type1 != type2) return ValueType::MatchResult::NOMATCH; } return res; } cppcheck-1.90/lib/symboldatabase.h000066400000000000000000001243011357737443600171600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef symboldatabaseH #define symboldatabaseH //--------------------------------------------------------------------------- #include "config.h" #include "library.h" #include "mathlib.h" #include "platform.h" #include "token.h" #include #include #include #include #include #include #include class ErrorLogger; class Function; class Scope; class Settings; class SymbolDatabase; class Tokenizer; class ValueType; /** * @brief Access control enumerations. */ enum class AccessControl { Public, Protected, Private, Global, Namespace, Argument, Local, Throw }; /** * @brief Array dimension information. */ struct Dimension { Dimension() : tok(nullptr), num(0), known(true) { } const Token *tok; ///< size token MathLib::bigint num; ///< (assumed) dimension length when size is a number, 0 if not known bool known; ///< Known size }; /** @brief Information about a class type. */ class CPPCHECKLIB Type { public: const Token* classDef; ///< Points to "class" token const Scope* classScope; const Scope* enclosingScope; enum class NeedInitialization { Unknown, True, False } needInitialization; class BaseInfo { public: BaseInfo() : type(nullptr), nameTok(nullptr), access(AccessControl::Public), isVirtual(false) { } std::string name; const Type* type; const Token* nameTok; AccessControl access; // public/protected/private bool isVirtual; // allow ordering within containers bool operator<(const BaseInfo& rhs) const { return this->type < rhs.type; } }; struct FriendInfo { FriendInfo() : nameStart(nullptr), nameEnd(nullptr), type(nullptr) { } const Token* nameStart; const Token* nameEnd; const Type* type; }; std::vector derivedFrom; std::vector friendList; const Token * typeStart; const Token * typeEnd; Type(const Token* classDef_ = nullptr, const Scope* classScope_ = nullptr, const Scope* enclosingScope_ = nullptr) : classDef(classDef_), classScope(classScope_), enclosingScope(enclosingScope_), needInitialization(NeedInitialization::Unknown), typeStart(nullptr), typeEnd(nullptr) { if (classDef_ && classDef_->str() == "enum") needInitialization = NeedInitialization::True; else if (classDef_ && classDef_->str() == "using") { typeStart = classDef->tokAt(3); typeEnd = typeStart; while (typeEnd->next() && typeEnd->next()->str() != ";") typeEnd = typeEnd->next(); } } const std::string& name() const; const std::string& type() const { return classDef ? classDef->str() : emptyString; } bool isClassType() const { return classDef && classDef->str() == "class"; } bool isEnumType() const { return classDef && classDef->str() == "enum"; } bool isTypeAlias() const { return classDef && classDef->str() == "using"; } bool isStructType() const { return classDef && classDef->str() == "struct"; } const Token *initBaseInfo(const Token *tok, const Token *tok1); const Function* getFunction(const std::string& funcName) const; /** * Check for circulare dependencies, i.e. loops within the class hierarchy * @param ancestors list of ancestors. For internal usage only, clients should not supply this argument. * @return true if there is a circular dependency */ bool hasCircularDependencies(std::set* ancestors = nullptr) const; /** * Check for dependency * @param ancestor potential ancestor * @return true if there is a dependency */ bool findDependency(const Type* ancestor) const; bool isDerivedFrom(const std::string & ancestor) const; }; class CPPCHECKLIB Enumerator { public: explicit Enumerator(const Scope * scope_) : scope(scope_), name(nullptr), value(0), start(nullptr), end(nullptr), value_known(false) { } const Scope * scope; const Token * name; MathLib::bigint value; const Token * start; const Token * end; bool value_known; }; /** @brief Information about a member variable. */ class CPPCHECKLIB Variable { /** @brief flags mask used to access specific bit. */ enum { fIsMutable = (1 << 0), /** @brief mutable variable */ fIsStatic = (1 << 1), /** @brief static variable */ fIsConst = (1 << 2), /** @brief const variable */ fIsExtern = (1 << 3), /** @brief extern variable */ fIsClass = (1 << 4), /** @brief user defined type */ fIsArray = (1 << 5), /** @brief array variable */ fIsPointer = (1 << 6), /** @brief pointer variable */ fIsReference = (1 << 7), /** @brief reference variable */ fIsRValueRef = (1 << 8), /** @brief rvalue reference variable */ fHasDefault = (1 << 9), /** @brief function argument with default value */ fIsStlType = (1 << 10), /** @brief STL type ('std::') */ fIsStlString = (1 << 11), /** @brief std::string|wstring|basic_string<T>|u16string|u32string */ fIsFloatType = (1 << 12), /** @brief Floating point type */ fIsVolatile = (1 << 13), /** @brief volatile */ fIsSmartPointer = (1 << 14) /** @brief std::shared_ptr|unique_ptr */ }; /** * Get specified flag state. * @param flag_ flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(unsigned int flag_) const { return ((mFlags & flag_) != 0); } /** * Set specified flag state. * @param flag_ flag to set state * @param state_ new state of flag */ void setFlag(unsigned int flag_, bool state_) { mFlags = state_ ? mFlags | flag_ : mFlags & ~flag_; } /** * @brief parse and save array dimension information * @param settings Platform settings and library * @return true if array, false if not */ bool arrayDimensions(const Settings* settings); public: Variable(const Token *name_, const Token *start_, const Token *end_, nonneg int index_, AccessControl access_, const Type *type_, const Scope *scope_, const Settings* settings) : mNameToken(name_), mTypeStartToken(start_), mTypeEndToken(end_), mIndex(index_), mAccess(access_), mFlags(0), mType(type_), mScope(scope_), mValueType(nullptr) { evaluate(settings); } ~Variable(); /** * Get name token. * @return name token */ const Token *nameToken() const { return mNameToken; } /** * Get type start token. * The type start token doesn't account 'static' and 'const' qualifiers * E.g.: * static const int * const p = ...; * type start token ^ * @return type start token */ const Token *typeStartToken() const { return mTypeStartToken; } /** * Get type end token. * The type end token doesn't account the forward 'const' qualifier * E.g.: * static const int * const p = ...; * type end token ^ * @return type end token */ const Token *typeEndToken() const { return mTypeEndToken; } /** * Get end token of variable declaration * E.g. * int i[2][3] = ... * end token ^ * @return variable declaration end token */ const Token *declEndToken() const; /** * Get name string. * @return name string */ const std::string &name() const { // name may not exist for function arguments if (mNameToken) return mNameToken->str(); return emptyString; } /** * Get declaration ID (varId used for variable in its declaration). * @return declaration ID */ nonneg int declarationId() const { // name may not exist for function arguments if (mNameToken) return mNameToken->varId(); return 0; } /** * Get index of variable in declared order. * @return variable index */ nonneg int index() const { return mIndex; } /** * Is variable public. * @return true if public, false if not */ bool isPublic() const { return mAccess == AccessControl::Public; } /** * Is variable protected. * @return true if protected, false if not */ bool isProtected() const { return mAccess == AccessControl::Protected; } /** * Is variable private. * @return true if private, false if not */ bool isPrivate() const { return mAccess == AccessControl::Private; } /** * Is variable global. * @return true if global, false if not */ bool isGlobal() const { return mAccess == AccessControl::Global; } /** * Is variable in a namespace. * @return true if in a namespace, false if not */ bool isNamespace() const { return mAccess == AccessControl::Namespace; } /** * Is variable a function argument. * @return true if a function argument, false if not */ bool isArgument() const { return mAccess == AccessControl::Argument; } /** * Is variable local. * @return true if local, false if not */ bool isLocal() const { return (mAccess == AccessControl::Local) && !isExtern(); } /** * Is variable mutable. * @return true if mutable, false if not */ bool isMutable() const { return getFlag(fIsMutable); } /** * Is variable volatile. * @return true if volatile, false if not */ bool isVolatile() const { return getFlag(fIsVolatile); } /** * Is variable static. * @return true if static, false if not */ bool isStatic() const { return getFlag(fIsStatic); } /** * Is variable extern. * @return true if extern, false if not */ bool isExtern() const { return getFlag(fIsExtern); } /** * Is variable const. * @return true if const, false if not */ bool isConst() const { return getFlag(fIsConst); } /** * Is variable a throw type. * @return true if throw type, false if not */ bool isThrow() const { return mAccess == AccessControl::Throw; } /** * Is variable a user defined (or unknown) type. * @return true if user defined type, false if not */ bool isClass() const { return getFlag(fIsClass); } /** * Is variable an array. * @return true if array, false if not */ bool isArray() const { return getFlag(fIsArray) && !getFlag(fIsPointer); } /** * Is pointer variable. * @return true if pointer, false otherwise */ bool isPointer() const { return getFlag(fIsPointer); } /** * Is variable a pointer to an array * @return true if pointer to array, false otherwise */ bool isPointerToArray() const { return isPointer() && getFlag(fIsArray); } /** * Is variable an array of pointers * @return true if array or pointers, false otherwise */ bool isPointerArray() const; /** * Is array or pointer variable. * @return true if pointer or array, false otherwise */ bool isArrayOrPointer() const { return getFlag(fIsArray) || getFlag(fIsPointer); } /** * Is reference variable. * @return true if reference, false otherwise */ bool isReference() const { return getFlag(fIsReference); } /** * Is reference variable. * @return true if reference, false otherwise */ bool isRValueReference() const { return getFlag(fIsRValueRef); } /** * Does variable have a default value. * @return true if has a default falue, false if not */ bool hasDefault() const { return getFlag(fHasDefault); } /** * Get Type pointer of known type. * @return pointer to type if known, NULL if not known */ const Type *type() const { return mType; } /** * Get Scope pointer of known type. * @return pointer to type scope if known, NULL if not known */ const Scope *typeScope() const { return mType ? mType->classScope : nullptr; } /** * Get Scope pointer of enclosing scope. * @return pointer to enclosing scope */ const Scope *scope() const { return mScope; } /** * Get array dimensions. * @return array dimensions vector */ const std::vector &dimensions() const { return mDimensions; } /** * Get array dimension length. * @return length of dimension */ MathLib::bigint dimension(nonneg int index_) const { return mDimensions[index_].num; } /** * Get array dimension known. * @return length of dimension known */ bool dimensionKnown(nonneg int index_) const { return mDimensions[index_].known; } /** * Checks if the variable is an STL type ('std::') * E.g.: * std::string s; * ... * sVar->isStlType() == true * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ bool isStlType() const { return getFlag(fIsStlType); } /** * Checks if the variable is an STL type ('std::') * E.g.: * std::string s; * ... * sVar->isStlType() == true * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ bool isStlStringType() const { return getFlag(fIsStlString); } bool isSmartPointer() const { return getFlag(fIsSmartPointer); } const Type *smartPointerType() const; /** * Checks if the variable is of any of the STL types passed as arguments ('std::') * E.g.: * std::string s; * ... * const char *str[] = {"string", "wstring"}; * sVar->isStlType(str) == true * @param stlType stl type * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ bool isStlType(const std::string& stlType) const { return isStlType() && stlType==mTypeStartToken->strAt(2); } /** * Checks if the variable is of any of the STL types passed as arguments ('std::') * E.g.: * std::string s; * ... * const std::set str = make_container< std::set >() << "string" << "wstring"; * sVar->isStlType(str) == true * @param stlTypes set of stl types * @return true if it is an stl type and its type matches any of the types in 'stlTypes' */ bool isStlType(const std::set& stlTypes) const { return isStlType() && stlTypes.find(mTypeStartToken->strAt(2))!=stlTypes.end(); } /** * Determine whether it's a floating number type * @return true if the type is known and it's a floating type (float, double and long double) or a pointer/array to it */ bool isFloatingType() const { return getFlag(fIsFloatType); } /** * Determine whether it's an enumeration type * @return true if the type is known and it's an enumeration type */ bool isEnumType() const { return type() && type()->isEnumType(); } const ValueType *valueType() const { return mValueType; } void setValueType(const ValueType &valueType); AccessControl accessControl() const { return mAccess; } std::string getTypeName() const; private: // only symbol database can change the type friend class SymbolDatabase; /** * Set Type pointer to known type. * @param t type */ void type(const Type * t) { mType = t; } /** @brief variable name token */ const Token *mNameToken; /** @brief variable type start token */ const Token *mTypeStartToken; /** @brief variable type end token */ const Token *mTypeEndToken; /** @brief order declared */ nonneg int mIndex; /** @brief what section is this variable declared in? */ AccessControl mAccess; // public/protected/private /** @brief flags */ unsigned int mFlags; /** @brief pointer to user defined type info (for known types) */ const Type *mType; /** @brief pointer to scope this variable is in */ const Scope *mScope; ValueType *mValueType; /** @brief array dimensions */ std::vector mDimensions; /** @brief fill in information, depending on Tokens given at instantiation */ void evaluate(const Settings* settings); }; class CPPCHECKLIB Function { // only symbol database can change this friend class SymbolDatabase; /** @brief flags mask used to access specific bit. */ enum { fHasBody = (1 << 0), ///< @brief has implementation fIsInline = (1 << 1), ///< @brief implementation in class definition fIsConst = (1 << 2), ///< @brief is const fHasVirtualSpecifier = (1 << 3), ///< @brief does declaration contain 'virtual' specifier fIsPure = (1 << 4), ///< @brief is pure virtual fIsStatic = (1 << 5), ///< @brief is static fIsStaticLocal = (1 << 6), ///< @brief is static local fIsExtern = (1 << 7), ///< @brief is extern fIsFriend = (1 << 8), ///< @brief is friend fIsExplicit = (1 << 9), ///< @brief is explicit fIsDefault = (1 << 10), ///< @brief is default fIsDelete = (1 << 11), ///< @brief is delete fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier? fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier? fIsNoExcept = (1 << 14), ///< @brief is noexcept fIsThrow = (1 << 15), ///< @brief is throw fIsOperator = (1 << 16), ///< @brief is operator fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier fIsVariadic = (1 << 19), ///< @brief is variadic fIsVolatile = (1 << 20), ///< @brief is volatile fHasTrailingReturnType = (1 << 21), ///< @brief has trailing return type fIsEscapeFunction = (1 << 22), ///< @brief Function throws or exits }; /** * Get specified flag state. * @param flag flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(unsigned int flag) const { return ((mFlags & flag) != 0); } /** * Set specified flag state. * @param flag flag to set state * @param state new state of flag */ void setFlag(unsigned int flag, bool state) { mFlags = state ? mFlags | flag : mFlags & ~flag; } public: enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction, eLambda }; Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *scope, const Token *tokDef, const Token *tokArgDef); const std::string &name() const { return tokenDef->str(); } nonneg int argCount() const { return argumentList.size(); } nonneg int minArgCount() const { return argumentList.size() - initArgCount; } const Variable* getArgumentVar(nonneg int num) const; nonneg int initializedArgCount() const { return initArgCount; } void addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope); /** @brief check if this function is virtual in the base classes */ bool isImplicitlyVirtual(bool defaultVal = false) const; /** @brief get function in base class that is overridden */ const Function *getOverriddenFunction(bool *foundAllBaseClasses = nullptr) const; bool isLambda() const { return type==eLambda; } bool isConstructor() const { return type==eConstructor || type==eCopyConstructor || type==eMoveConstructor; } bool isDestructor() const { return type==eDestructor; } bool isAttributeConstructor() const { return tokenDef->isAttributeConstructor(); } bool isAttributeDestructor() const { return tokenDef->isAttributeDestructor(); } bool isAttributePure() const { return tokenDef->isAttributePure(); } bool isAttributeConst() const { return tokenDef->isAttributeConst(); } bool isAttributeNoreturn() const { return tokenDef->isAttributeNoreturn(); } bool isAttributeNothrow() const { return tokenDef->isAttributeNothrow(); } bool isAttributeNodiscard() const { return tokenDef->isAttributeNodiscard(); } bool hasBody() const { return getFlag(fHasBody); } bool isInline() const { return getFlag(fIsInline); } bool isConst() const { return getFlag(fIsConst); } bool hasVirtualSpecifier() const { return getFlag(fHasVirtualSpecifier); } bool isPure() const { return getFlag(fIsPure); } bool isStatic() const { return getFlag(fIsStatic); } bool isStaticLocal() const { return getFlag(fIsStaticLocal); } bool isExtern() const { return getFlag(fIsExtern); } bool isFriend() const { return getFlag(fIsFriend); } bool isExplicit() const { return getFlag(fIsExplicit); } bool isDefault() const { return getFlag(fIsDefault); } bool isDelete() const { return getFlag(fIsDelete); } bool isNoExcept() const { return getFlag(fIsNoExcept); } bool isThrow() const { return getFlag(fIsThrow); } bool hasOverrideSpecifier() const { return getFlag(fHasOverrideSpecifier); } bool hasFinalSpecifier() const { return getFlag(fHasFinalSpecifier); } bool isOperator() const { return getFlag(fIsOperator); } bool hasLvalRefQualifier() const { return getFlag(fHasLvalRefQual); } bool hasRvalRefQualifier() const { return getFlag(fHasRvalRefQual); } bool isVariadic() const { return getFlag(fIsVariadic); } bool isVolatile() const { return getFlag(fIsVolatile); } bool hasTrailingReturnType() const { return getFlag(fHasTrailingReturnType); } void hasBody(bool state) { setFlag(fHasBody, state); } bool isEscapeFunction() const { return getFlag(fIsEscapeFunction); } void isEscapeFunction(bool state) { setFlag(fIsEscapeFunction, state); } bool isSafe(const Settings *settings) const; const Token *tokenDef; ///< function name token in class definition const Token *argDef; ///< function argument start '(' in class definition const Token *token; ///< function name token in implementation const Token *arg; ///< function argument start '(' const Token *retDef; ///< function return type token const ::Type *retType; ///< function return type const Scope *functionScope; ///< scope of function body const Scope* nestedIn; ///< Scope the function is declared in std::list argumentList; ///< argument list nonneg int initArgCount; ///< number of args with default values Type type; ///< constructor, destructor, ... AccessControl access; ///< public/protected/private const Token *noexceptArg; ///< noexcept token const Token *throwArg; ///< throw token const Token *templateDef; ///< points to 'template <' before function static bool argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length); static bool returnsReference(const Function* function, bool unknown = false); const Token* returnDefEnd() const { if (this->hasTrailingReturnType()) { return Token::findmatch(retDef, "{|;"); } else { return tokenDef; } } /** * @return token to ":" if the function is a constructor * and it contains member initialization otherwise a nullptr is returned */ const Token * constructorMemberInitialization() const; private: /** Recursively determine if this function overrides a virtual function in a base class */ const Function * getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const; unsigned int mFlags; void isInline(bool state) { setFlag(fIsInline, state); } void isConst(bool state) { setFlag(fIsConst, state); } void hasVirtualSpecifier(bool state) { setFlag(fHasVirtualSpecifier, state); } void isPure(bool state) { setFlag(fIsPure, state); } void isStatic(bool state) { setFlag(fIsStatic, state); } void isStaticLocal(bool state) { setFlag(fIsStaticLocal, state); } void isExtern(bool state) { setFlag(fIsExtern, state); } void isFriend(bool state) { setFlag(fIsFriend, state); } void isExplicit(bool state) { setFlag(fIsExplicit, state); } void isDefault(bool state) { setFlag(fIsDefault, state); } void isDelete(bool state) { setFlag(fIsDelete, state); } void isNoExcept(bool state) { setFlag(fIsNoExcept, state); } void isThrow(bool state) { setFlag(fIsThrow, state); } void isOperator(bool state) { setFlag(fIsOperator, state); } void hasLvalRefQualifier(bool state) { setFlag(fHasLvalRefQual, state); } void hasRvalRefQualifier(bool state) { setFlag(fHasRvalRefQual, state); } void isVariadic(bool state) { setFlag(fIsVariadic, state); } void isVolatile(bool state) { setFlag(fIsVolatile, state); } void hasTrailingReturnType(bool state) { return setFlag(fHasTrailingReturnType, state); } }; class CPPCHECKLIB Scope { // let tests access private function for testing friend class TestSymbolDatabase; public: struct UsingInfo { const Token *start; const Scope *scope; }; enum ScopeType { eGlobal, eClass, eStruct, eUnion, eNamespace, eFunction, eIf, eElse, eFor, eWhile, eDo, eSwitch, eUnconditional, eTry, eCatch, eLambda, eEnum }; Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_); Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_); const SymbolDatabase *check; std::string className; const Token *classDef; ///< class/struct/union/namespace token const Token *bodyStart; ///< '{' token const Token *bodyEnd; ///< '}' token std::list functionList; std::multimap functionMap; std::list varlist; const Scope *nestedIn; std::list nestedList; nonneg int numConstructors; nonneg int numCopyOrMoveConstructors; std::list usingList; ScopeType type; Type* definedType; std::map definedTypesMap; // function specific fields const Scope *functionOf; ///< scope this function belongs to Function *function; ///< function info for this function // enum specific fields const Token * enumType; bool enumClass; std::vector enumeratorList; const Enumerator * findEnumerator(const std::string & name) const { for (const Enumerator & i : enumeratorList) { if (i.name->str() == name) return &i; } return nullptr; } bool isNestedIn(const Scope * outer) const { if (!outer) return false; if (outer == this) return true; const Scope * parent = nestedIn; while (outer != parent && parent) parent = parent->nestedIn; if (parent && parent == outer) return true; return false; } bool isClassOrStruct() const { return (type == eClass || type == eStruct); } bool isClassOrStructOrUnion() const { return (type == eClass || type == eStruct || type == eUnion); } bool isExecutable() const { return type != eClass && type != eStruct && type != eUnion && type != eGlobal && type != eNamespace && type != eEnum; } bool isLocal() const { return (type == eIf || type == eElse || type == eFor || type == eWhile || type == eDo || type == eSwitch || type == eUnconditional || type == eTry || type == eCatch); } // Is there lambda/inline function(s) in this scope? bool hasInlineOrLambdaFunction() const; /** * @brief find a function * @param tok token of function call * @param requireConst if const refers to a const variable only const methods should be matched * @return pointer to function if found or NULL if not found */ const Function *findFunction(const Token *tok, bool requireConst=false) const; /** * @brief find if name is in nested list * @param name name of nested scope */ Scope *findInNestedList(const std::string & name); const Scope *findRecordInNestedList(const std::string & name) const; Scope *findRecordInNestedList(const std::string & name) { return const_cast(const_cast(this)->findRecordInNestedList(name)); } const Type* findType(const std::string& name) const; Type* findType(const std::string& name) { return const_cast(const_cast(this)->findType(name)); } /** * @brief find if name is in nested list * @param name name of nested scope */ Scope *findInNestedListRecursive(const std::string & name); void addVariable(const Token *token_, const Token *start_, const Token *end_, AccessControl access_, const Type *type_, const Scope *scope_, const Settings* settings) { varlist.emplace_back(token_, start_, end_, varlist.size(), access_, type_, scope_, settings); } /** @brief initialize varlist */ void getVariableList(const Settings* settings); const Function *getDestructor() const; void addFunction(const Function & func) { functionList.push_back(func); const Function * back = &functionList.back(); functionMap.insert(make_pair(back->tokenDef->str(), back)); } bool hasDefaultConstructor() const; AccessControl defaultAccess() const; /** * @brief check if statement is variable declaration and add it if it is * @param tok pointer to start of statement * @param varaccess access control of statement * @param settings Settings * @return pointer to last token */ const Token *checkVariable(const Token *tok, AccessControl varaccess, const Settings* settings); /** * @brief get variable from name * @param varname name of variable * @return pointer to variable */ const Variable *getVariable(const std::string &varname) const; const Token * addEnum(const Token * tok, bool isCpp); private: /** * @brief helper function for getVariableList() * @param tok pointer to token to check * @param vartok populated with pointer to the variable token, if found * @param typetok populated with pointer to the type token, if found * @return true if tok points to a variable declaration, false otherwise */ bool isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const; void findFunctionInBase(const std::string & name, nonneg int args, std::vector & matches) const; }; /** Value type */ class CPPCHECKLIB ValueType { public: enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign; enum Type { UNKNOWN_TYPE, NONSTD, RECORD, CONTAINER, ITERATOR, VOID, BOOL, CHAR, SHORT, WCHAR_T, INT, LONG, LONGLONG, UNKNOWN_INT, FLOAT, DOUBLE, LONGDOUBLE } type; nonneg int bits; ///< bitfield bitcount nonneg int pointer; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc nonneg int constness; ///< bit 0=data, bit 1=*, bit 2=** const Scope *typeScope; ///< if the type definition is seen this point out the type scope const ::Type *smartPointerType; ///< Smart pointer type const Token* smartPointerTypeToken; ///< Smart pointer type token const Library::Container *container; ///< If the type is a container defined in a cfg file, this is the used container const Token *containerTypeToken; ///< The container type token. the template argument token that defines the container element type. std::string originalTypeName; ///< original type name as written in the source code. eg. this might be "uint8_t" when type is CHAR. ValueType() : sign(UNKNOWN_SIGN), type(UNKNOWN_TYPE), bits(0), pointer(0U), constness(0U), typeScope(nullptr), smartPointerType(nullptr), smartPointerTypeToken(nullptr), container(nullptr), containerTypeToken(nullptr) {} ValueType(enum Sign s, enum Type t, nonneg int p) : sign(s), type(t), bits(0), pointer(p), constness(0U), typeScope(nullptr), smartPointerType(nullptr), smartPointerTypeToken(nullptr), container(nullptr), containerTypeToken(nullptr) {} ValueType(enum Sign s, enum Type t, nonneg int p, nonneg int c) : sign(s), type(t), bits(0), pointer(p), constness(c), typeScope(nullptr), smartPointerType(nullptr), smartPointerTypeToken(nullptr), container(nullptr), containerTypeToken(nullptr) {} ValueType(enum Sign s, enum Type t, nonneg int p, nonneg int c, const std::string& otn) : sign(s), type(t), bits(0), pointer(p), constness(c), typeScope(nullptr), smartPointerType(nullptr), smartPointerTypeToken(nullptr), container(nullptr), containerTypeToken(nullptr), originalTypeName(otn) {} static ValueType parseDecl(const Token *type, const Settings *settings); static Type typeFromString(const std::string &typestr, bool longType); enum class MatchResult { UNKNOWN, SAME, FALLBACK1, FALLBACK2, NOMATCH }; static MatchResult matchParameter(const ValueType *call, const ValueType *func); static MatchResult matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar); bool isIntegral() const { return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT); } bool isFloat() const { return (type >= ValueType::Type::FLOAT && type <= ValueType::Type::LONGDOUBLE); } bool fromLibraryType(const std::string &typestr, const Settings *settings); bool isEnum() const { return typeScope && typeScope->type == Scope::eEnum; } MathLib::bigint typeSize(const cppcheck::Platform &platform) const; std::string str() const; std::string dump() const; }; class CPPCHECKLIB SymbolDatabase { friend class TestSymbolDatabase; public: SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger); ~SymbolDatabase(); /** @brief Information about all namespaces/classes/structrues */ std::list scopeList; /** @brief Fast access to function scopes */ std::vector functionScopes; /** @brief Fast access to class and struct scopes */ std::vector classAndStructScopes; /** @brief Fast access to types */ std::list typeList; /** * @brief find a variable type if it's a user defined type * @param start scope to start looking in * @param typeTok token containing variable type * @return pointer to type if found or NULL if not found */ const Type *findVariableType(const Scope *start, const Token *typeTok) const; /** * @brief find a function * @param tok token of function call * @return pointer to function if found or NULL if not found */ const Function *findFunction(const Token *tok) const; const Scope *findScopeByName(const std::string& name) const; const Type* findType(const Token *startTok, const Scope *startScope) const; Type* findType(const Token *startTok, Scope *startScope) const { return const_cast(this->findType(startTok, const_cast(startScope))); } const Scope *findScope(const Token *tok, const Scope *startScope) const; Scope *findScope(const Token *tok, Scope *startScope) const { return const_cast(this->findScope(tok, const_cast(startScope))); } const Variable *getVariableFromVarId(nonneg int varId) const { return mVariableList.at(varId); } const std::vector & variableList() const { return mVariableList; } /** * @brief output a debug message */ void debugMessage(const Token *tok, const std::string &msg) const; void printOut(const char * title = nullptr) const; void printVariable(const Variable *var, const char *indent) const; void printXml(std::ostream &out) const; bool isCPP() const; /* * @brief Do a sanity check */ void validate() const; void validateExecutableScopes() const; /** * @brief Check variable list, e.g. variables w/o scope */ void validateVariables() const; /** Set valuetype in provided tokenlist */ void setValueTypeInTokenList(bool reportDebugWarnings); /** * Calculates sizeof value for given type. * @param type Token which will contain e.g. "int", "*", or string. * @return sizeof for given type, or 0 if it can't be calculated. */ nonneg int sizeOfType(const Token *type) const; /** Set array dimensions when valueflow analysis is completed */ void setArrayDimensionsUsingValueFlow(); private: friend class Scope; friend class Function; // Create symboldatabase... void createSymbolDatabaseFindAllScopes(); void createSymbolDatabaseClassInfo(); void createSymbolDatabaseVariableInfo(); void createSymbolDatabaseCopyAndMoveConstructors(); void createSymbolDatabaseFunctionScopes(); void createSymbolDatabaseClassAndStructScopes(); void createSymbolDatabaseFunctionReturnTypes(); void createSymbolDatabaseNeedInitialization(); void createSymbolDatabaseVariableSymbolTable(); void createSymbolDatabaseSetScopePointers(); void createSymbolDatabaseSetFunctionPointers(bool firstPass); void createSymbolDatabaseSetVariablePointers(); void createSymbolDatabaseSetTypePointers(); void createSymbolDatabaseEnums(); void createSymbolDatabaseEscapeFunctions(); void createSymbolDatabaseIncompleteVars(); void addClassFunction(Scope **scope, const Token **tok, const Token *argStart); Function *addGlobalFunctionDecl(Scope*& scope, const Token* tok, const Token *argStart, const Token* funcStart); Function *addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart); void addNewFunction(Scope **scope, const Token **tok); bool isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart, const Token** declEnd) const; const Type *findTypeInNested(const Token *startTok, const Scope *startScope) const; const Scope *findNamespace(const Token * tok, const Scope * scope) const; Function *findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length); const Type *findVariableTypeInBase(const Scope *scope, const Token *typeTok) const; typedef std::map MemberIdMap; typedef std::map VarIdMap; void fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar); /** Whether iName is a keyword as defined in http://en.cppreference.com/w/c/keyword and http://en.cppreference.com/w/cpp/keyword*/ bool isReservedName(const std::string& iName) const; const Enumerator * findEnumerator(const Token * tok) const; void setValueType(Token *tok, const ValueType &valuetype); void setValueType(Token *tok, const Variable &var); void setValueType(Token *tok, const Enumerator &enumerator); const Tokenizer *mTokenizer; const Settings *mSettings; ErrorLogger *mErrorLogger; /** variable symbol table */ std::vector mVariableList; /** list for missing types */ std::list mBlankTypes; bool mIsCpp; ValueType::Sign mDefaultSignedness; /** "negative cache" list of tokens that we find are not enumeration values */ mutable std::set mTokensThatAreNotEnumeratorValues; }; //--------------------------------------------------------------------------- #endif // symboldatabaseH cppcheck-1.90/lib/templatesimplifier.cpp000066400000000000000000004554411357737443600204340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "templatesimplifier.h" #include "errorlogger.h" #include "mathlib.h" #include "settings.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include #include #include namespace { class FindToken { public: explicit FindToken(const Token *token) : mToken(token) {} bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { return tokenAndName.token() == mToken; } private: const Token * const mToken; }; class FindName { public: explicit FindName(const std::string &name) : mName(name) {} bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { return tokenAndName.name() == mName; } private: const std::string mName; }; class FindFullName { public: explicit FindFullName(const std::string &fullName) : mFullName(fullName) {} bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { return tokenAndName.fullName() == mFullName; } private: const std::string mFullName; }; } TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string &scope) : mToken(token), mScope(scope), mName(mToken ? mToken->str() : ""), mFullName(mScope.empty() ? mName : (mScope + " :: " + mName)), mNameToken(nullptr), mParamEnd(nullptr), mFlags(0) { if (mToken) mToken->templateSimplifierPointer(this); } TemplateSimplifier::TokenAndName::TokenAndName(Token *token, const std::string &scope, const Token *nameToken, const Token *paramEnd) : mToken(token), mScope(scope), mName(nameToken->str()), mFullName(mScope.empty() ? mName : (mScope + " :: " + mName)), mNameToken(nameToken), mParamEnd(paramEnd), mFlags(0) { // only set flags for declaration if (mToken && mNameToken && mParamEnd) { isSpecialization(Token::simpleMatch(mToken, "template < >")); if (!isSpecialization()) { if (Token::simpleMatch(mToken->next()->findClosingBracket(), "> template <")) { const Token * temp = mNameToken->tokAt(-2); while (Token::Match(temp, ">|%name% ::")) { if (temp->str() == ">") temp = temp->findOpeningBracket()->previous(); else temp = temp->tokAt(-2); } isPartialSpecialization(temp->strAt(1) == "<"); } else isPartialSpecialization(mNameToken->strAt(1) == "<"); } isAlias(mParamEnd->strAt(1) == "using"); if (isAlias() && isPartialSpecialization()) { throw InternalError(mToken, "partial specialization of alias templates is not permitted", InternalError::SYNTAX); } if (isAlias() && isSpecialization()) { throw InternalError(mToken, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX); } isFriend(mParamEnd->strAt(1) == "friend"); const Token *next = mParamEnd->next(); if (isFriend()) next = next->next(); isClass(Token::Match(next, "class|struct|union %name% <|{|:|;|::")); if (mToken->strAt(1) == "<" && !isSpecialization()) { const Token *end = mToken->next()->findClosingBracket(); isVariadic(end && Token::findmatch(mToken->tokAt(2), "typename|class ...", end)); } const Token *tok1 = mNameToken->next(); if (tok1->str() == "<") { const Token *closing = tok1->findClosingBracket(); if (closing) tok1 = closing->next(); else throw InternalError(mToken, "unsupported syntax", InternalError::SYNTAX); } isFunction(tok1->str() == "("); isVariable(!isClass() && !isAlias() && !isFriend() && Token::Match(tok1, "=|;")); if (!isFriend()) { if (isVariable()) isForwardDeclaration(tok1->str() == ";"); else if (!isAlias()) { if (isFunction()) tok1 = tok1->link()->next(); while (tok1 && !Token::Match(tok1, ";|{")) { if (tok1->str() == "<") tok1 = tok1->findClosingBracket(); else if (Token::Match(tok1, "(|[") && tok1->link()) tok1 = tok1->link(); if (tok1) tok1 = tok1->next(); } if (tok1) isForwardDeclaration(tok1->str() == ";"); } } // check for member class or function and adjust scope if ((isFunction() || isClass()) && mNameToken->strAt(-1) == "::") { const Token * start = mNameToken; while (Token::Match(start->tokAt(-2), "%name% ::") || (Token::simpleMatch(start->tokAt(-2), "> ::") && start->tokAt(-2)->findOpeningBracket() && Token::Match(start->tokAt(-2)->findOpeningBracket()->previous(), "%name% <"))) { if (start->strAt(-2) == ">") start = start->tokAt(-2)->findOpeningBracket()->previous(); else start = start->tokAt(-2); } if (start && start != nameToken) { if (!mScope.empty()) mScope += " ::"; while (start && start->next() != mNameToken) { if (start->str() == "<") start = start->findClosingBracket(); else { if (!mScope.empty()) mScope += " "; mScope += start->str(); } start = start->next(); } if (start) mFullName = mScope.empty() ? mName : (mScope + " :: " + mName); } } } // make sure at most only one family flag is set assert(isClass() ? !(isFunction() || isVariable()) : true); assert(isFunction() ? !(isClass() || isVariable()) : true); assert(isVariable() ? !(isClass() || isFunction()) : true); if (mToken) mToken->templateSimplifierPointer(this); } TemplateSimplifier::TokenAndName::TokenAndName(const TokenAndName& other) : mToken(other.mToken), mScope(other.mScope), mName(other.mName), mFullName(other.mFullName), mNameToken(other.mNameToken), mParamEnd(other.mParamEnd), mFlags(other.mFlags) { if (mToken) mToken->templateSimplifierPointer(this); } TemplateSimplifier::TokenAndName::~TokenAndName() { if (mToken) mToken->templateSimplifierPointers().erase(this); } const Token * TemplateSimplifier::TokenAndName::aliasStartToken() const { if (mParamEnd) return mParamEnd->tokAt(4); return nullptr; } const Token * TemplateSimplifier::TokenAndName::aliasEndToken() const { if (aliasStartToken()) return Token::findsimplematch(aliasStartToken(), ";"); return nullptr; } bool TemplateSimplifier::TokenAndName::isAliasToken(const Token *tok) const { const Token *end = aliasEndToken(); for (const Token *tok1 = aliasStartToken(); tok1 != end; tok1 = tok1->next()) { if (tok1 == tok) return true; } return false; } TemplateSimplifier::TemplateSimplifier(Tokenizer *tokenizer) : mTokenizer(tokenizer), mTokenList(tokenizer->list), mSettings(tokenizer->mSettings), mErrorLogger(tokenizer->mErrorLogger), mChanged(false) { } TemplateSimplifier::~TemplateSimplifier() { } void TemplateSimplifier::fixAngleBrackets() { for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { // Ticket #6181: normalize C++11 template parameter list closing syntax if (tok->str() == "<" && templateParameters(tok)) { Token *endTok = tok->findClosingBracket(); if (endTok && endTok->str() == ">>") { endTok->str(">"); endTok->insertToken(">"); } } else if (Token::Match(tok, "class|struct|union|=|:|public|protected|private %name% <")) { Token *endTok = tok->tokAt(2)->findClosingBracket(); if (Token::Match(endTok, ">> ;|{|%type%")) { endTok->str(">"); endTok->insertToken(">"); } } } } void TemplateSimplifier::cleanupAfterSimplify() { bool goback = false; for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (goback) { tok = tok->previous(); goback = false; } if (tok->str() == "(") tok = tok->link(); else if (Token::Match(tok, "template < > %name%")) { const Token *end = tok; while (end) { if (end->str() == ";") break; if (end->str() == "{") { end = end->link()->next(); break; } if (!Token::Match(end, "%name%|::|<|>|,")) { end = nullptr; break; } end = end->next(); } if (end) { Token::eraseTokens(tok,end); tok->deleteThis(); } } else if (Token::Match(tok, "%type% <") && (!tok->previous() || tok->previous()->str() == ";")) { const Token *tok2 = tok->tokAt(2); std::string type; while (Token::Match(tok2, "%type%|%num% ,")) { type += tok2->str() + ","; tok2 = tok2->tokAt(2); } if (Token::Match(tok2, "%type%|%num% > (")) { type += tok2->str(); tok->str(tok->str() + "<" + type + ">"); Token::eraseTokens(tok, tok2->tokAt(2)); if (tok == mTokenList.front()) goback = true; } } } } void TemplateSimplifier::checkComplicatedSyntaxErrorsInTemplates() { // check for more complicated syntax errors when using templates.. for (const Token *tok = mTokenList.front(); tok; tok = tok->next()) { // skip executing scopes (ticket #3183).. if (Token::simpleMatch(tok, "( {")) { tok = tok->link(); if (!tok) syntaxError(nullptr); } // skip executing scopes.. const Token *start = Tokenizer::startOfExecutableScope(tok); if (start) { tok = start->link(); } // skip executing scopes (ticket #1985).. else if (Token::simpleMatch(tok, "try {")) { tok = tok->next()->link(); while (Token::simpleMatch(tok, "} catch (")) { tok = tok->linkAt(2); if (Token::simpleMatch(tok, ") {")) tok = tok->next()->link(); } } if (!tok) syntaxError(nullptr); // not start of statement? if (tok->previous() && !Token::Match(tok, "[;{}]")) continue; // skip starting tokens.. ;;; typedef typename foo::bar::.. while (Token::Match(tok, ";|{")) tok = tok->next(); while (Token::Match(tok, "typedef|typename")) tok = tok->next(); while (Token::Match(tok, "%type% ::")) tok = tok->tokAt(2); if (!tok) break; // template variable or type.. if (Token::Match(tok, "%type% <")) { // these are used types.. std::set usedtypes; // parse this statement and see if the '<' and '>' are matching unsigned int level = 0; for (const Token *tok2 = tok; tok2 && !Token::simpleMatch(tok2, ";"); tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "{") && (!Token::Match(tok2->previous(), ">|%type%") || Token::simpleMatch(tok2->link(), "} ;"))) break; if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == "<") { bool inclevel = false; if (Token::simpleMatch(tok2->previous(), "operator <")) ; else if (level == 0 && Token::Match(tok2->previous(), "%type%")) { // @todo add better expression detection if (!Token::Match(tok2->next(), "*| %type%|%num% ;")) inclevel = true; } else if (tok2->next() && tok2->next()->isStandardType()) inclevel = true; else if (Token::simpleMatch(tok2, "< typename")) inclevel = true; else if (Token::Match(tok2->tokAt(-2), "<|, %type% <") && usedtypes.find(tok2->previous()->str()) != usedtypes.end()) inclevel = true; else if (Token::Match(tok2, "< %type%") && usedtypes.find(tok2->next()->str()) != usedtypes.end()) inclevel = true; else if (Token::Match(tok2, "< %type%")) { // is the next token a type and not a variable/constant? // assume it's a type if there comes another "<" const Token *tok3 = tok2->next(); while (Token::Match(tok3, "%type% ::")) tok3 = tok3->tokAt(2); if (Token::Match(tok3, "%type% <")) inclevel = true; } else if (tok2->strAt(-1) == ">") syntaxError(tok); if (inclevel) { ++level; if (Token::Match(tok2->tokAt(-2), "<|, %type% <")) usedtypes.insert(tok2->previous()->str()); } } else if (tok2->str() == ">") { if (level > 0) --level; } else if (tok2->str() == ">>") { if (level > 0) --level; if (level > 0) --level; } } if (level > 0) syntaxError(tok); } } } unsigned int TemplateSimplifier::templateParameters(const Token *tok) { unsigned int numberOfParameters = 1; if (!tok) return 0; if (tok->str() != "<") return 0; if (Token::Match(tok->previous(), "%var% <")) return 0; tok = tok->next(); if (!tok || tok->str() == ">") return 0; unsigned int level = 0; while (tok) { // skip template template if (level == 0 && Token::simpleMatch(tok, "template <")) { const Token *closing = tok->next()->findClosingBracket(); if (closing) { if (closing->str() == ">>") return numberOfParameters; tok = closing->next(); if (tok->str() == ">" || tok->str() == ">>") return numberOfParameters; else if (tok->str() == ",") { ++numberOfParameters; tok = tok->next(); continue; } } else return 0; } // skip const/volatile if (Token::Match(tok, "const|volatile")) tok = tok->next(); // skip struct/union if (Token::Match(tok, "struct|union")) tok = tok->next(); // Skip '&' if (Token::Match(tok, "& ::| %name%")) tok = tok->next(); // Skip variadic types (Ticket #5774, #6059, #6172) if (Token::simpleMatch(tok, "...")) { if ((tok->previous()->isName() && !Token::Match(tok->tokAt(-2), "<|,|::")) || (!tok->previous()->isName() && tok->strAt(-1) != ">")) return 0; // syntax error tok = tok->next(); if (!tok) return 0; if (tok->str() == ">") { if (level == 0) return numberOfParameters; --level; } else if (tok->str() == ">>") { if (level == 1) return numberOfParameters; level -= 2; } else if (tok->str() == "," && level == 0) { ++numberOfParameters; tok = tok->next(); continue; } } // Skip '=', '?', ':' if (Token::Match(tok, "=|?|:")) tok = tok->next(); if (!tok) return 0; // Skip links if (Token::Match(tok, "(|{")) { tok = tok->link(); if (tok) tok = tok->next(); if (!tok) return 0; if (tok->str() == ">" && level == 0) return numberOfParameters; else if (tok->str() == ">>" && level == 1) return numberOfParameters; else if (tok->str() == ",") { if (level == 0) ++numberOfParameters; tok = tok->next(); } continue; } // skip std:: if (tok && tok->str() == "::") tok = tok->next(); while (Token::Match(tok, "%name% ::")) { tok = tok->tokAt(2); if (tok && tok->str() == "*") // Ticket #5759: Class member pointer as a template argument; skip '*' tok = tok->next(); } if (!tok) return 0; // num/type .. if (!tok->isNumber() && tok->tokType() != Token::eChar && !tok->isName() && !tok->isOp()) return 0; tok = tok->next(); if (!tok) return 0; // * / const while (Token::Match(tok, "*|&|&&|const")) tok = tok->next(); if (!tok) return 0; // Function pointer or prototype.. while (Token::Match(tok, "(|[")) { if (!tok->link()) syntaxError(tok); tok = tok->link()->next(); while (Token::Match(tok, "const|volatile")) // Ticket #5786: Skip function cv-qualifiers tok = tok->next(); } if (!tok) return 0; // inner template if (tok->str() == "<") { ++level; tok = tok->next(); } if (!tok) return 0; // ,/> while (Token::Match(tok, ">|>>")) { if (level == 0) return tok->str() == ">" && !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0; --level; if (tok->str() == ">>") { if (level == 0) return !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0; --level; } tok = tok->next(); if (Token::simpleMatch(tok,"(")) tok = tok->link()->next(); if (!tok) return 0; } if (tok->str() != ",") continue; if (level == 0) ++numberOfParameters; tok = tok->next(); } return 0; } const Token *TemplateSimplifier::findTemplateDeclarationEnd(const Token *tok) { return const_cast(findTemplateDeclarationEnd(const_cast(tok))); } Token *TemplateSimplifier::findTemplateDeclarationEnd(Token *tok) { if (Token::simpleMatch(tok, "template <")) { tok = tok->next()->findClosingBracket(); if (tok) tok = tok->next(); } if (!tok) return nullptr; Token * tok2 = tok; while (tok2 && !Token::Match(tok2, ";|{")) { if (tok2->str() == "<") tok2 = tok2->findClosingBracket(); else if (Token::Match(tok2, "(|[") && tok2->link()) tok2 = tok2->link(); if (tok2) tok2 = tok2->next(); } if (tok2 && tok2->str() == "{") { tok = tok2->link(); if (tok && tok->strAt(1) == ";") tok = tok->next(); } else if (tok2 && tok2->str() == ";") tok = tok2; else tok = nullptr; return tok; } void TemplateSimplifier::eraseTokens(Token *begin, const Token *end) { if (!begin || begin == end) return; while (begin->next() && begin->next() != end) { begin->deleteNext(); } } void TemplateSimplifier::deleteToken(Token *tok) { if (tok->next()) tok->next()->deletePrevious(); else tok->deleteThis(); } bool TemplateSimplifier::removeTemplate(Token *tok) { if (!Token::simpleMatch(tok, "template <")) return false; int indentlevel = 0; unsigned int countgt = 0; // Counter for ">" for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") { tok2 = tok2->link(); } else if (tok2->str() == ")") { // garbage code! (#3504) eraseTokens(tok,tok2); deleteToken(tok); return false; } else if (tok2->str() == "{") { tok2 = tok2->link(); if (indentlevel < 2) { tok2 = tok2->next(); if (tok2 && tok2->str() == ";" && tok2->next()) tok2 = tok2->next(); eraseTokens(tok, tok2); deleteToken(tok); return true; } } else if (tok2->str() == "}") { // garbage code! (#3449) eraseTokens(tok,tok2); deleteToken(tok); return false; } // Count ">" if (tok2->str() == ">") countgt++; // don't remove constructor if (tok2->str() == "explicit" || (countgt == 1 && Token::Match(tok2->previous(), "> %type% (") && Tokenizer::startOfExecutableScope(tok2->linkAt(1)))) { eraseTokens(tok, tok2); deleteToken(tok); return true; } if (tok2->str() == ";") { tok2 = tok2->next(); eraseTokens(tok, tok2); deleteToken(tok); return true; } if (tok2->str() == "<") ++indentlevel; else if (indentlevel >= 2 && tok2->str() == ">") --indentlevel; else if (Token::Match(tok2, "> class|struct|union %name% [,)]")) { tok2 = tok2->next(); eraseTokens(tok, tok2); deleteToken(tok); return true; } } return false; } bool TemplateSimplifier::getTemplateDeclarations() { bool codeWithTemplates = false; for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "template <")) continue; // ignore template template parameter if (tok->strAt(-1) == "<" || tok->strAt(-1) == ",") continue; // ignore nested template if (tok->strAt(-1) == ">") continue; // skip to last nested template parameter const Token *tok1 = tok; while (tok1 && tok1->next() && Token::simpleMatch(tok1->next()->findClosingBracket(), "> template <")) { const Token *closing = tok1->next()->findClosingBracket(); if (!closing) syntaxError(tok1->next()); tok1 = closing->next(); } if (!tok1) syntaxError(tok); if (!tok1->next()) syntaxError(tok); // Some syntax checks, see #6865 if (!tok->tokAt(2)) syntaxError(tok->next()); if (tok->strAt(2)=="typename" && !Token::Match(tok->tokAt(3), "%name%|...|,|=|>")) syntaxError(tok->next()); codeWithTemplates = true; const Token * const parmEnd = tok1->next()->findClosingBracket(); for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) { if (tok2->str() == "(" && tok2->link()) tok2 = tok2->link(); else if (tok2->str() == ")") break; // skip decltype(...) else if (Token::simpleMatch(tok2, "decltype (")) tok2 = tok2->linkAt(1); else if (Token::Match(tok2, "{|=|;")) { const int namepos = getTemplateNamePosition(parmEnd); if (namepos > 0) { TokenAndName decl(tok, tok->scopeInfo()->name, parmEnd->tokAt(namepos), parmEnd); if (decl.isForwardDeclaration()) { // Declaration => add to mTemplateForwardDeclarations mTemplateForwardDeclarations.emplace_back(std::move(decl)); } else { // Implementation => add to mTemplateDeclarations mTemplateDeclarations.emplace_back(std::move(decl)); } break; } } } } return codeWithTemplates; } void TemplateSimplifier::addInstantiation(Token *token, const std::string &scope) { simplifyTemplateArgs(token->tokAt(2), token->next()->findClosingBracket()); mTemplateInstantiations.emplace_back(token, scope); } void TemplateSimplifier::getTemplateInstantiations() { std::multimap functionNameMap; for (const auto & decl : mTemplateDeclarations) { if (decl.isFunction()) functionNameMap.insert(std::make_pair(decl.name(), &decl)); } for (const auto & decl : mTemplateForwardDeclarations) { if (decl.isFunction()) functionNameMap.insert(std::make_pair(decl.name(), &decl)); } const Token *skip = nullptr; for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { // template definition.. skip it if (Token::simpleMatch(tok, "template <")) { tok = tok->next()->findClosingBracket(); if (!tok) break; const bool isUsing = tok->strAt(1) == "using"; if (isUsing && Token::Match(tok->tokAt(2), "%name% <")) { // Can't have specialized type alias so ignore it Token *tok2 = Token::findsimplematch(tok->tokAt(3), ";"); if (tok2) tok = tok2; } else if (tok->strAt(-1) == "<") { // Don't ignore user specialization but don't consider it an instantiation. // Instantiations in return type, function parameters, and executable code // are not ignored. unsigned int pos = getTemplateNamePosition(tok); if (pos > 0) skip = tok->tokAt(pos); } else { // #7914 // Ignore template instantiations within template definitions: they will only be // handled if the definition is actually instantiated Token * tok2 = findTemplateDeclarationEnd(tok->next()); if (tok2) tok = tok2; } } else if (Token::Match(tok, "template using %name% <")) { // Can't have specialized type alias so ignore it Token *tok2 = Token::findsimplematch(tok->tokAt(3), ";"); if (tok2) tok = tok2; } else if (Token::Match(tok, "using %name% <")) { // Can't have specialized type alias so ignore it Token *tok2 = Token::findsimplematch(tok->tokAt(2), ";"); if (tok2) tok = tok2; } else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|&|return|<|,|!|[ %name% ::|<|(") || Token::Match(tok->previous(), "%type% %name% ::|<") || Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) { std::string scopeName = tok->scopeInfo()->name; std::string qualification; Token * qualificationTok = tok; while (Token::Match(tok, "%name% :: %name%")) { // ignore redundant namespaces if (scopeName.find(tok->str()) == std::string::npos) qualification += (qualification.empty() ? "" : " :: ") + tok->str(); tok = tok->tokAt(2); } // look for function instantiation with type deduction // fixme: only single argument functions supported if (tok->strAt(1) == "(") { std::string fullName = qualification + (qualification.empty() ? "" : " :: ") + tok->str(); // get all declarations with this name for (auto pos = functionNameMap.lower_bound(tok->str()); pos != functionNameMap.upper_bound(tok->str()); ++pos) { // look for declaration with same qualification if (pos->second->fullName() == fullName) { // make sure it is a single argument function if (Token::Match(pos->second->token()->tokAt(2), "typename|class %name% >") && Token::Match(pos->second->nameToken()->tokAt(2), "const| %type% &| %name%| )") && Token::Match(tok->tokAt(2), "%num%|%str%|%char%|%bool% )")) { tok->insertToken(">"); switch (tok->tokAt(3)->tokType()) { case Token::eBoolean: tok->insertToken("bool"); break; case Token::eChar: if (tok->tokAt(3)->isLong()) tok->insertToken("wchar_t"); else tok->insertToken("char"); break; case Token::eString: tok->insertToken("*"); if (tok->tokAt(4)->isLong()) tok->insertToken("wchar_t"); else tok->insertToken("char"); tok->insertToken("const"); break; case Token::eNumber: { MathLib::value num(tok->strAt(3)); if (num.isFloat()) { // MathLib::getSuffix doesn't work for floating point numbers char suffix = tok->strAt(3).back(); if (suffix == 'f' || suffix == 'F') tok->insertToken("float"); else if (suffix == 'l' || suffix == 'L') { tok->insertToken("double"); tok->insertToken("long"); } else tok->insertToken("double"); } else if (num.isInt()) { std::string suffix = MathLib::getSuffix(tok->strAt(3)); if (suffix.find("LL") != std::string::npos) { tok->insertToken("long"); tok->insertToken("long"); } else if (suffix.find('L') != std::string::npos) tok->insertToken("long"); else tok->insertToken("int"); if (suffix.find('U') != std::string::npos) tok->insertToken("unsigned"); } break; } default: break; } tok->insertToken("<"); break; } } } } if (!Token::Match(tok, "%name% <") || Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast")) continue; if (tok == skip) { skip = nullptr; continue; } // Add inner template instantiations first => go to the ">" // and then parse backwards, adding all seen instantiations Token *tok2 = tok->next()->findClosingBracket(); // parse backwards and add template instantiations // TODO for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { if (Token::Match(tok2, ",|< %name% <") && (tok2->strAt(3) == ">" || templateParameters(tok2->tokAt(2)))) { addInstantiation(tok2->next(), tok->scopeInfo()->name); } else if (Token::Match(tok2->next(), "class|struct")) tok2->deleteNext(); } // Add outer template.. if (templateParameters(tok->next()) || tok->strAt(2) == ">") { const std::string scopeName1(scopeName); while (true) { const std::string fullName = scopeName + (scopeName.empty()?"":" :: ") + qualification + (qualification.empty()?"":" :: ") + tok->str(); const std::list::const_iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindFullName(fullName)); if (it != mTemplateDeclarations.end()) { // full name matches addInstantiation(tok, it->scope()); break; } else { // full name doesn't match so try with using namespaces if available bool found = false; for (const auto & nameSpace : tok->scopeInfo()->usingNamespaces) { std::string fullNameSpace = scopeName + (scopeName.empty()?"":" :: ") + nameSpace + (qualification.empty()?"":" :: ") + qualification; std::string newFullName = fullNameSpace + " :: " + tok->str(); const std::list::const_iterator it1 = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindFullName(newFullName)); if (it1 != mTemplateDeclarations.end()) { // insert using namespace into token stream std::string::size_type offset = 0; std::string::size_type pos = 0; while ((pos = nameSpace.substr(offset).find(' ')) != std::string::npos) { qualificationTok->insertToken(nameSpace.substr(offset, pos), "", true); offset = offset + pos + 1; } qualificationTok->insertToken(nameSpace.substr(offset), "", true); qualificationTok->insertToken("::", "", true); addInstantiation(tok, it1->scope()); found = true; break; } } if (found) break; if (scopeName.empty()) { if (!qualification.empty()) addInstantiation(tok, qualification); else addInstantiation(tok, tok->scopeInfo()->name); break; } const std::string::size_type pos = scopeName.rfind(" :: "); scopeName = (pos == std::string::npos) ? std::string() : scopeName.substr(0,pos); } } } } } } void TemplateSimplifier::useDefaultArgumentValues() { for (TokenAndName &declaration : mTemplateDeclarations) useDefaultArgumentValues(declaration); for (TokenAndName &declaration : mTemplateForwardDeclarations) useDefaultArgumentValues(declaration); } void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration) { // Ticket #5762: Skip specialization tokens if (declaration.isSpecialization() || declaration.isAlias() || declaration.isFriend()) return; // template parameters with default value has syntax such as: // x = y // this list will contain all the '=' tokens for such arguments std::list eq; // and this set the position of parameters with a default value std::set defaultedArgPos; // parameter number. 1,2,3,.. std::size_t templatepar = 1; // parameter depth std::size_t templateParmDepth = 0; // map type parameter name to index std::map typeParameterNames; // Scan template declaration.. for (Token *tok = declaration.token(); tok; tok = tok->next()) { if (tok->link() && Token::Match(tok, "{|(|[")) { // Ticket #6835 tok = tok->link(); continue; } if (tok->str() == "<" && (tok->strAt(1) == ">" || (tok->previous()->isName() && typeParameterNames.find(tok->strAt(-1)) == typeParameterNames.end()))) ++templateParmDepth; // end of template parameters? if (tok->str() == ">") { if (templateParmDepth<2) break; else --templateParmDepth; } // map type parameter name to index if (Token::Match(tok, "typename|class|%type% %name% ,|>")) typeParameterNames[tok->strAt(1)] = templatepar - 1; // next template parameter if (tok->str() == "," && (1 == templateParmDepth)) // Ticket #5823: Properly count parameters ++templatepar; // default parameter value? else if (Token::Match(tok, "= !!>")) { if (defaultedArgPos.insert(templatepar).second) { eq.push_back(tok); } else { // Ticket #5605: Syntax error (two equal signs for the same parameter), bail out eq.clear(); break; } } } if (eq.empty()) return; // iterate through all template instantiations for (const TokenAndName &instantiation : mTemplateInstantiations) { if (declaration.fullName() != instantiation.fullName()) continue; // instantiation arguments.. std::vector> instantiationArgs; std::size_t index = 0; const Token *end = instantiation.token()->next()->findClosingBracket(); if (!end) continue; if (end != instantiation.token()->tokAt(2)) instantiationArgs.resize(1); for (const Token *tok1 = instantiation.token()->tokAt(2); tok1 && tok1 != end; tok1 = tok1->next()) { if (tok1->link() && Token::Match(tok1, "{|(|[")) { const Token *endLink = tok1->link(); do { instantiationArgs[index].push_back(tok1); tok1 = tok1->next(); } while (tok1 && tok1 != endLink); instantiationArgs[index].push_back(tok1); } else if (tok1->str() == "<" && (tok1->strAt(1) == ">" || (tok1->previous()->isName() && typeParameterNames.find(tok1->strAt(-1)) == typeParameterNames.end()))) { const Token *endLink = tok1->findClosingBracket(); do { instantiationArgs[index].push_back(tok1); tok1 = tok1->next(); } while (tok1 && tok1 != endLink); instantiationArgs[index].push_back(tok1); } else if (tok1->str() == ",") { ++index; instantiationArgs.resize(index + 1); } else instantiationArgs[index].push_back(tok1); } // count the parameters.. Token *tok = instantiation.token()->next(); unsigned int usedpar = templateParameters(tok); Token *instantiationEnd = tok->findClosingBracket(); tok = instantiationEnd; if (tok && tok->str() == ">") { tok = tok->previous(); std::list::const_iterator it = eq.begin(); for (std::size_t i = (templatepar - eq.size()); it != eq.end() && i < usedpar; ++i) ++it; int count = 0; while (it != eq.end()) { int indentlevel = 0; if ((usedpar + count) && usedpar <= (instantiationArgs.size() + count)) { tok->insertToken(","); tok = tok->next(); } const Token *from = (*it)->next(); std::stack links; while (from && (!links.empty() || indentlevel || !Token::Match(from, ",|>"))) { if (from->str() == "<" && (from->strAt(1) == ">" || (from->previous()->isName() && typeParameterNames.find(from->strAt(-1)) == typeParameterNames.end()))) ++indentlevel; else if (from->str() == ">") --indentlevel; auto entry = typeParameterNames.find(from->str()); if (entry != typeParameterNames.end() && entry->second < instantiationArgs.size()) { for (const Token *tok1 : instantiationArgs[entry->second]) { tok->insertToken(tok1->str(), tok1->originalName()); tok = tok->next(); if (Token::Match(tok, "(|[|{")) links.push(tok); else if (!links.empty() && Token::Match(tok, ")|]|}")) { Token::createMutualLinks(links.top(), tok); links.pop(); } } } else { tok->insertToken(from->str(), from->originalName()); tok = tok->next(); if (Token::Match(tok, "(|[|{")) links.push(tok); else if (!links.empty() && Token::Match(tok, ")|]|}")) { Token::createMutualLinks(links.top(), tok); links.pop(); } } from = from->next(); } ++it; count++; usedpar++; } } simplifyTemplateArgs(instantiation.token()->next(), instantiationEnd); } for (Token * const eqtok : eq) { Token *tok2; int indentlevel = 0; for (tok2 = eqtok->next(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, ";|)|}|]")) { // bail out #6607 tok2 = nullptr; break; } if (Token::Match(tok2, "(|{|[")) tok2 = tok2->link(); else if (Token::Match(tok2, "%type% <") && (tok2->strAt(2) == ">" || templateParameters(tok2->next()))) { std::list::iterator ti = std::find_if(mTemplateInstantiations.begin(), mTemplateInstantiations.end(), FindToken(tok2)); if (ti != mTemplateInstantiations.end()) mTemplateInstantiations.erase(ti); ++indentlevel; } else if (indentlevel > 0 && tok2->str() == ">") --indentlevel; else if (indentlevel == 0 && Token::Match(tok2, ",|>")) break; if (indentlevel < 0) break; } // something went wrong, don't call eraseTokens() // with a nullptr "end" parameter (=all remaining tokens). if (!tok2) continue; // don't strip args from uninstantiated templates std::list::iterator ti2 = std::find_if(mTemplateInstantiations.begin(), mTemplateInstantiations.end(), FindName(declaration.name())); if (ti2 == mTemplateInstantiations.end()) continue; eraseTokens(eqtok, tok2); eqtok->deleteThis(); // update parameter end pointer declaration.paramEnd(declaration.token()->next()->findClosingBracket()); } } void TemplateSimplifier::simplifyTemplateAliases() { for (std::list::iterator it1 = mTemplateDeclarations.begin(); it1 != mTemplateDeclarations.end();) { TokenAndName &aliasDeclaration = *it1; if (!aliasDeclaration.isAlias()) { ++it1; continue; } // alias parameters.. std::vector aliasParameters; getTemplateParametersInDeclaration(aliasDeclaration.token()->tokAt(2), aliasParameters); std::map aliasParameterNames; for (unsigned int argnr = 0; argnr < aliasParameters.size(); ++argnr) aliasParameterNames[aliasParameters[argnr]->str()] = argnr; // Look for alias usages.. bool found = false; for (std::list::iterator it2 = mTemplateInstantiations.begin(); it2 != mTemplateInstantiations.end();) { TokenAndName &aliasUsage = *it2; if (!aliasUsage.token() || aliasUsage.fullName() != aliasDeclaration.fullName()) { ++it2; continue; } // don't recurse if (aliasDeclaration.isAliasToken(aliasUsage.token())) { ++it2; continue; } std::vector> args; Token *tok2 = aliasUsage.token()->tokAt(2); while (tok2) { Token * const start = tok2; while (tok2 && !Token::Match(tok2, "[,>;{}]")) { if (tok2->link() && Token::Match(tok2, "(|[")) tok2 = tok2->link(); else if (tok2->str() == "<") tok2 = tok2->findClosingBracket(); tok2 = tok2->next(); } args.emplace_back(start, tok2); if (tok2 && tok2->str() == ",") { tok2 = tok2->next(); } else { break; } } if (!tok2 || tok2->str() != ">" || (!aliasDeclaration.isVariadic() && (args.size() != aliasParameters.size())) || (aliasDeclaration.isVariadic() && (args.size() < aliasParameters.size()))) { ++it2; continue; } mChanged = true; // copy template-id from declaration to after instantiation Token * dst = aliasUsage.token()->next()->findClosingBracket(); Token * end = TokenList::copyTokens(dst, aliasDeclaration.aliasStartToken(), aliasDeclaration.aliasEndToken()->previous(), false)->next(); // replace parameters for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) { if (!tok1->isName()) continue; if (aliasParameterNames.find(tok1->str()) != aliasParameterNames.end()) { const unsigned int argnr = aliasParameterNames[tok1->str()]; const Token * const fromStart = args[argnr].first; const Token * const fromEnd = args[argnr].second->previous(); Token *temp = TokenList::copyTokens(tok1, fromStart, fromEnd, true); const bool tempOK(temp && temp != tok1->next()); tok1->deleteThis(); if (tempOK) tok1 = temp; // skip over inserted parameters } else if (tok1->str() == "typename") tok1->deleteThis(); } // add new instantiations for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) { if (!tok1->isName()) continue; if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) { // Create template instance.. if (Token::Match(tok1, "%name% <")) { const std::list::iterator it = std::find_if(mTemplateInstantiations.begin(), mTemplateInstantiations.end(), FindToken(tok1)); if (it != mTemplateInstantiations.end()) addInstantiation(tok2, it->scope()); } } } // erase the instantiation tokens eraseTokens(aliasUsage.token()->previous(), dst->next()); found = true; // erase this instantiation it2 = mTemplateInstantiations.erase(it2); } if (found) { Token *end = const_cast(aliasDeclaration.aliasEndToken()); // remove declaration tokens if (aliasDeclaration.token()->previous()) eraseTokens(aliasDeclaration.token()->previous(), end->next() ? end->next() : end); else { eraseTokens(mTokenList.front(), end->next() ? end->next() : end); deleteToken(mTokenList.front()); } // remove declaration it1 = mTemplateDeclarations.erase(it1); } else ++it1; } } bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[]) { assert(instance->strAt(1) == "<"); if (numberOfArguments != templateParameters(instance->next())) return false; if (patternAfter) { const Token *tok = instance->next()->findClosingBracket(); if (!tok || !Token::Match(tok->next(), patternAfter)) return false; } // nothing mismatching was found.. return true; } // Utility function for TemplateSimplifier::getTemplateNamePosition, that works on template functions bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos) { namepos = 1; while (tok && tok->next()) { if (Token::Match(tok->next(), ";|{")) return false; // skip decltype(...) else if (Token::simpleMatch(tok->next(), "decltype (")) { const Token * end = tok->linkAt(2)->previous(); while (tok && tok->next() && tok != end) { tok = tok->next(); namepos++; } } else if (Token::Match(tok->next(), "%type% <")) { const Token *closing = tok->tokAt(2)->findClosingBracket(); if (closing) { if (closing->strAt(1) == "(" && Tokenizer::isFunctionHead(closing->next(), ";|{|:", true)) return true; while (tok && tok->next() && tok->next() != closing) { tok = tok->next(); namepos++; } } } else if (Token::Match(tok->next(), "%type% (") && Tokenizer::isFunctionHead(tok->tokAt(2), ";|{|:", true)) { return true; } tok = tok->next(); namepos++; } return false; } bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *tok, int &namepos) { namepos = 1; while (tok && tok->next()) { if (Token::Match(tok->next(), ";|{|(|using")) return false; // skip decltype(...) else if (Token::simpleMatch(tok->next(), "decltype (")) { const Token * end = tok->linkAt(2); while (tok && tok->next() && tok != end) { tok = tok->next(); namepos++; } } else if (Token::Match(tok->next(), "%type% <")) { const Token *closing = tok->tokAt(2)->findClosingBracket(); if (closing) { if (Token::Match(closing->next(), "=|;")) return true; while (tok && tok->next() && tok->next() != closing) { tok = tok->next(); namepos++; } } } else if (Token::Match(tok->next(), "%type% =|;")) { return true; } tok = tok->next(); namepos++; } return false; } bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok, int &namepos) { if (Token::Match(tok, "> friend| class|struct|union %type% :|<|;|{|::")) { namepos = tok->strAt(1) == "friend" ? 3 : 2; tok = tok->tokAt(namepos); while (Token::Match(tok, "%type% :: %type%") || (Token::Match(tok, "%type% <") && Token::Match(tok->next()->findClosingBracket(), "> :: %type%"))) { if (tok->strAt(1) == "::") { tok = tok->tokAt(2); namepos += 2; } else { const Token *end = tok->next()->findClosingBracket(); if (!end || !end->tokAt(2)) { // syntax error namepos = -1; return true; } end = end->tokAt(2); do { tok = tok->next(); namepos += 1; } while (tok && tok != end); } } return true; } return false; } int TemplateSimplifier::getTemplateNamePosition(const Token *tok) { assert(tok && tok->str() == ">"); auto it = mTemplateNamePos.find(tok); if (!mSettings->debugtemplate && it != mTemplateNamePos.end()) { return it->second; } // get the position of the template name int namepos = 0; if (getTemplateNamePositionTemplateClass(tok, namepos)) ; else if (Token::Match(tok, "> using %name% =")) { // types may not be defined in alias template declarations if (!Token::Match(tok->tokAt(4), "class|struct|union|enum %name%| {")) namepos = 2; } else if (getTemplateNamePositionTemplateVariable(tok, namepos)) ; else if (!getTemplateNamePositionTemplateFunction(tok, namepos)) namepos = -1; // Name not found mTemplateNamePos[tok] = namepos; return namepos; } void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, const Token *tok) { // find start of qualification const Token * tokStart = tok; int offset = 0; while (Token::Match(tokStart->tokAt(-2), "%name% ::")) { tokStart = tokStart->tokAt(-2); offset -= 2; } // decide if namespace needs to be inserted in or appended to token list const bool insert = tokStart != tok; std::string::size_type start = 0; std::string::size_type end = 0; bool inTemplate = false; int level = 0; while ((end = templateDeclaration.scope().find(" ", start)) != std::string::npos) { std::string token = templateDeclaration.scope().substr(start, end - start); // done if scopes overlap if (token == tokStart->str() && tok->strAt(-1) != "::") break; if (token == "<") { inTemplate = true; ++level; } if (inTemplate) { if (insert) mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + token); else mTokenList.back()->str(mTokenList.back()->str() + token); if (token == ">") { --level; if (level == 0) inTemplate = false; } } else { if (insert) mTokenList.back()->tokAt(offset)->insertToken(token, ""); else mTokenList.addtoken(token, tok->linenr(), tok->fileIndex()); } start = end + 1; } // don't add if it already exists std::string token = templateDeclaration.scope().substr(start, end - start); if (token != tokStart->str() || tok->strAt(-1) != "::") { if (insert) { if (!inTemplate) mTokenList.back()->tokAt(offset)->insertToken(templateDeclaration.scope().substr(start), ""); else mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + templateDeclaration.scope().substr(start)); mTokenList.back()->tokAt(offset)->insertToken("::", ""); } else { if (!inTemplate) mTokenList.addtoken(templateDeclaration.scope().substr(start), tok->linenr(), tok->fileIndex()); else mTokenList.back()->str(mTokenList.back()->str() + templateDeclaration.scope().substr(start)); mTokenList.addtoken("::", tok->linenr(), tok->fileIndex()); } } } bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok) { std::string scope = templateDeclaration.scope(); // get the length in tokens of the namespace std::string::size_type pos = 0; int offset = -2; while ((pos = scope.find("::", pos)) != std::string::npos) { offset -= 2; pos += 2; } return Token::simpleMatch(tok->tokAt(offset), scope.c_str()) ; } void TemplateSimplifier::expandTemplate( const TokenAndName &templateDeclaration, const TokenAndName &templateInstantiation, const std::vector &typeParametersInDeclaration, const std::string &newName, bool copy) { bool inTemplateDefinition = false; const Token *startOfTemplateDeclaration = nullptr; const Token *endOfTemplateDefinition = nullptr; const Token * const templateDeclarationNameToken = templateDeclaration.nameToken(); const Token * const templateDeclarationToken = templateDeclaration.paramEnd(); const bool isClass = templateDeclaration.isClass(); const bool isFunction = templateDeclaration.isFunction(); const bool isSpecialization = templateDeclaration.isSpecialization(); const bool isVariable = templateDeclaration.isVariable(); struct newInstantiation { newInstantiation(Token *t, const std::string &s) : token(t), scope(s) { } Token *token; std::string scope; }; std::vector newInstantiations; // add forward declarations if (copy && isClass) { templateDeclaration.token()->insertToken(templateDeclarationToken->strAt(1), "", true); templateDeclaration.token()->insertToken(newName, "", true); templateDeclaration.token()->insertToken(";", "", true); } else if ((isFunction && (copy || isSpecialization)) || (isVariable && !isSpecialization) || (isClass && isSpecialization && mTemplateSpecializationMap.find(templateDeclaration.token()) != mTemplateSpecializationMap.end())) { Token * dst = templateDeclaration.token(); Token * dstStart = dst->previous(); bool isStatic = false; std::string scope; Token * start; Token * end; auto it = mTemplateForwardDeclarationsMap.find(dst); if (it != mTemplateForwardDeclarationsMap.end()) { dst = it->second; dstStart = dst->previous(); const Token * temp1 = dst->tokAt(1)->findClosingBracket(); const Token * temp2 = temp1->tokAt(getTemplateNamePosition(temp1)); start = temp1->next(); end = temp2->linkAt(1)->next(); } else { auto it2 = mTemplateSpecializationMap.find(dst); if (it2 != mTemplateSpecializationMap.end()) { dst = it2->second; dstStart = dst->previous(); isStatic = dst->next()->findClosingBracket()->strAt(1) == "static"; const Token * temp = templateDeclarationNameToken; while (Token::Match(temp->tokAt(-2), "%name% ::")) { scope.insert(0, temp->strAt(-2) + " :: "); temp = temp->tokAt(-2); } } start = templateDeclarationToken->next(); end = templateDeclarationNameToken->next(); if (end->str() == "<") end = end->findClosingBracket()->next(); if (end->str() == "(") end = end->link()->next(); else if (isVariable && end->str() == "=") { Token *temp = end->next(); while (temp && temp->str() != ";") { if (temp->link() && Token::Match(temp, "{|[|(")) temp = temp->link(); temp = temp->next(); } end = temp; } } unsigned int typeindentlevel = 0; while (end && !(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) { if (Token::Match(end, "<|(|{")) ++typeindentlevel; else if (Token::Match(end, ">|)|}")) --typeindentlevel; end = end->next(); } if (isStatic) dst->insertToken("static", "", true); std::map links; bool inAssignment = false; while (start && start != end) { if (isVariable && start->str() == "=") inAssignment = true; unsigned int itype = 0; while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != start->str()) ++itype; if (itype < typeParametersInDeclaration.size() && (!isVariable || !Token::Match(typeParametersInDeclaration[itype]->previous(), "<|, %type% >|,"))) { typeindentlevel = 0; std::stack brackets1; // holds "(" and "{" tokens for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); typetok && (typeindentlevel > 0 || !Token::Match(typetok, ",|>")); typetok = typetok->next()) { if (Token::simpleMatch(typetok, "...")) continue; if (Token::Match(typetok, "%name% <") && (typetok->strAt(2) == ">" || templateParameters(typetok->next()))) ++typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">") --typeindentlevel; else if (typetok->str() == "(") ++typeindentlevel; else if (typetok->str() == ")") --typeindentlevel; dst->insertToken(typetok->str(), typetok->originalName(), true); Token *previous = dst->previous(); previous->isTemplateArg(true); previous->isSigned(typetok->isSigned()); previous->isUnsigned(typetok->isUnsigned()); previous->isLong(typetok->isLong()); if (Token::Match(previous, "{|(|[")) { brackets1.push(previous); } else if (previous->str() == "}") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "{"); Token::createMutualLinks(brackets1.top(), previous); brackets1.pop(); } else if (previous->str() == ")") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "("); Token::createMutualLinks(brackets1.top(), previous); brackets1.pop(); } else if (previous->str() == "]") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "["); Token::createMutualLinks(brackets1.top(), previous); brackets1.pop(); } } } else { if (isSpecialization && !copy && !scope.empty() && Token::Match(start, (scope + templateDeclarationNameToken->str()).c_str())) { // skip scope while (start->strAt(1) != templateDeclarationNameToken->str()) start = start->next(); } else if (start->str() == templateDeclarationNameToken->str() && !(templateDeclaration.isFunction() && templateDeclaration.scope().empty() && (start->strAt(-1) == "." || Token::simpleMatch(start->tokAt(-2), ". template")))) { if (start->strAt(1) != "<" || Token::Match(start, newName.c_str()) || !inAssignment) { dst->insertToken(newName, "", true); if (start->strAt(1) == "<") start = start->next()->findClosingBracket(); } else { dst->insertToken(start->str(), "", true); newInstantiations.emplace_back(dst->previous(), templateDeclaration.scope()); } } else { // check if type is a template if (start->strAt(1) == "<") { // get the instantiated name Token * closing = start->next()->findClosingBracket(); if (closing) { std::string name; const Token * type = start; while (type && type != closing->next()) { if (!name.empty()) name += " "; name += type->str(); type = type->next(); } // check if type is instantiated for (const auto & inst : mTemplateInstantiations) { if (Token::simpleMatch(inst.token(), name.c_str())) { // use the instantiated name dst->insertToken(name, "", true); start = closing; break; } } } // just copy the token if it wasn't instantiated if (start != closing) { dst->insertToken(start->str(), start->originalName(), true); dst->previous()->isSigned(start->isSigned()); dst->previous()->isUnsigned(start->isUnsigned()); dst->previous()->isLong(start->isLong()); } } else { dst->insertToken(start->str(), start->originalName(), true); dst->previous()->isSigned(start->isSigned()); dst->previous()->isUnsigned(start->isUnsigned()); dst->previous()->isLong(start->isLong()); } } if (!start) continue; if (start->link()) { if (Token::Match(start, "[|{|(")) { links[start->link()] = dst->previous(); } else if (Token::Match(start, "]|}|)")) { Token::createMutualLinks(links[start], dst->previous()); links.erase(start); } } } start = start->next(); } dst->insertToken(";", "", true); if (isVariable || isFunction) simplifyTemplateArgs(dstStart, dst); } if (copy && (isClass || isFunction)) { // check if this is an explicit instantiation Token * start = templateInstantiation.token(); while (start && !Token::Match(start->previous(), "}|;|extern")) start = start->previous(); if (Token::Match(start, "template !!<")) { if (start->strAt(-1) == "extern") start = start->previous(); mExplicitInstantiationsToDelete.emplace_back(start, ""); } } for (Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { if (inTemplateDefinition) { if (!endOfTemplateDefinition) { if (isVariable) { Token *temp = tok3->findClosingBracket(); if (temp) { while (temp && temp->str() != ";") { if (temp->link() && Token::Match(temp, "{|[|(")) temp = temp->link(); temp = temp->next(); } endOfTemplateDefinition = temp; } } else if (tok3->str() == "{") endOfTemplateDefinition = tok3->link(); } if (tok3 == endOfTemplateDefinition) { inTemplateDefinition = false; startOfTemplateDeclaration = nullptr; } } if (tok3->str()=="template") { if (tok3->next() && tok3->next()->str()=="<") { std::vector localTypeParametersInDeclaration; getTemplateParametersInDeclaration(tok3->tokAt(2), localTypeParametersInDeclaration); if (localTypeParametersInDeclaration.size() != typeParametersInDeclaration.size()) inTemplateDefinition = false; // Partial specialization else inTemplateDefinition = true; } else { inTemplateDefinition = false; // Only template instantiation } startOfTemplateDeclaration = tok3; } if (Token::Match(tok3, "(|[")) tok3 = tok3->link(); // Start of template.. if (tok3 == templateDeclarationToken) { tok3 = tok3->next(); if (tok3->str() == "static") tok3 = tok3->next(); } // member function implemented outside class definition else if (inTemplateDefinition && Token::Match(tok3, "%name% <") && templateInstantiation.name() == tok3->str() && instantiateMatch(tok3, typeParametersInDeclaration.size(), ":: ~| %name% (")) { // there must be template.. bool istemplate = false; Token * tok5 = nullptr; // start of function return type for (Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { if (prev->str() == "template") { istemplate = true; tok5 = prev; break; } } if (!istemplate) continue; const Token *tok4 = tok3->next()->findClosingBracket(); while (tok4 && tok4->str() != "(") tok4 = tok4->next(); if (!Tokenizer::isFunctionHead(tok4, ":{", true)) continue; // find function return type start tok5 = tok5->next()->findClosingBracket(); if (tok5) tok5 = tok5->next(); // copy return type std::stack brackets2; // holds "(" and "{" tokens while (tok5 && tok5 != tok3) { // replace name if found if (Token::Match(tok5, "%name% <") && tok5->str() == templateInstantiation.name()) { if (copy) { if (!templateDeclaration.scope().empty() && tok5->strAt(-1) != "::") addNamespace(templateDeclaration, tok5); mTokenList.addtoken(newName, tok5->linenr(), tok5->fileIndex()); tok5 = tok5->next()->findClosingBracket(); } else { tok5->str(newName); eraseTokens(tok5, tok5->next()->findClosingBracket()->next()); } } else if (copy) { bool added = false; if (tok5->isName() && !Token::Match(tok5, "class|typename|struct") && !tok5->isStandardType()) { // search for this token in the type vector unsigned int itype = 0; while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok5->str()) ++itype; // replace type with given type.. if (itype < typeParametersInDeclaration.size()) { unsigned int typeindentlevel = 0; std::stack brackets1; // holds "(" and "{" tokens for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); typetok && (typeindentlevel>0 || !Token::Match(typetok, ",|>")); typetok = typetok->next()) { if (!Token::simpleMatch(typetok, "...")) { if (Token::Match(typetok, "%name% <") && (typetok->strAt(2) == ">" || templateParameters(typetok->next()))) ++typeindentlevel; else if (typeindentlevel > 0 && typetok->str() == ">") --typeindentlevel; else if (typetok->str() == "(") ++typeindentlevel; else if (typetok->str() == ")") --typeindentlevel; mTokenList.addtoken(typetok, tok5); Token *back = mTokenList.back(); if (Token::Match(back, "{|(|[")) { brackets1.push(back); } else if (back->str() == "}") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "{"); Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } else if (back->str() == ")") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "("); Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } else if (back->str() == "]") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "["); Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } back->isTemplateArg(true); added = true; break; } } } } if (!added) { mTokenList.addtoken(tok5); Token *back = mTokenList.back(); if (Token::Match(back, "{|(|[")) { brackets2.push(back); } else if (back->str() == "}") { assert(brackets2.empty() == false); assert(brackets2.top()->str() == "{"); Token::createMutualLinks(brackets2.top(), back); brackets2.pop(); } else if (back->str() == ")") { assert(brackets2.empty() == false); assert(brackets2.top()->str() == "("); Token::createMutualLinks(brackets2.top(), back); brackets2.pop(); } else if (back->str() == "]") { assert(brackets2.empty() == false); assert(brackets2.top()->str() == "["); Token::createMutualLinks(brackets2.top(), back); brackets2.pop(); } } } tok5 = tok5->next(); } if (copy) { if (!templateDeclaration.scope().empty() && tok3->strAt(-1) != "::") addNamespace(templateDeclaration, tok3); mTokenList.addtoken(newName, tok3->linenr(), tok3->fileIndex()); } while (tok3 && tok3->str() != "::") tok3 = tok3->next(); std::list::iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindToken(startOfTemplateDeclaration)); if (it != mTemplateDeclarations.end()) mMemberFunctionsToDelete.push_back(*it); } // not part of template.. go on to next token else continue; std::stack brackets; // holds "(", "[" and "{" tokens // FIXME use full name matching somehow const std::string lastName = (templateInstantiation.name().find(' ') != std::string::npos) ? templateInstantiation.name().substr(templateInstantiation.name().rfind(' ')+1) : templateInstantiation.name(); std::stack templates; for (; tok3; tok3 = tok3->next()) { if (tok3->isName() && !Token::Match(tok3, "class|typename|struct") && !tok3->isStandardType()) { // search for this token in the type vector unsigned int itype = 0; while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok3->str()) ++itype; // replace type with given type.. if (itype < typeParametersInDeclaration.size()) { unsigned int typeindentlevel = 0; std::stack brackets1; // holds "(" and "{" tokens for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); typetok && (typeindentlevel > 0 || !Token::Match(typetok, ",|>")); typetok = typetok->next()) { if (Token::simpleMatch(typetok, "...")) continue; if (Token::Match(typetok, "%name% <") && (typetok->strAt(2) == ">" || templateParameters(typetok->next()))) { brackets1.push(typetok->next()); ++typeindentlevel; } else if (typeindentlevel > 0 && typetok->str() == ">" && brackets1.top()->str() == "<") { --typeindentlevel; brackets1.pop(); } else if (typetok->str() == "(") ++typeindentlevel; else if (typetok->str() == ")") --typeindentlevel; Token *back; if (copy) { mTokenList.addtoken(typetok, tok3); back = mTokenList.back(); } else back = const_cast(typetok); if (Token::Match(back, "{|(|[")) brackets1.push(back); else if (back->str() == "}") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "{"); if (copy) Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } else if (back->str() == ")") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "("); if (copy) Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } else if (back->str() == "]") { assert(brackets1.empty() == false); assert(brackets1.top()->str() == "["); if (copy) Token::createMutualLinks(brackets1.top(), back); brackets1.pop(); } if (copy) back->isTemplateArg(true); } continue; } } // replace name.. if (tok3->str() == lastName) { if (Token::simpleMatch(tok3->next(), "<")) { Token *closingBracket = tok3->next()->findClosingBracket(); if (closingBracket) { // replace multi token name with single token name if (tok3 == templateDeclarationNameToken || Token::Match(tok3, newName.c_str())) { if (copy) { mTokenList.addtoken(newName, tok3); tok3 = closingBracket; } else { tok3->str(newName); eraseTokens(tok3, closingBracket->next()); } continue; } else if (!templateDeclaration.scope().empty() && !alreadyHasNamespace(templateDeclaration, tok3) && !Token::Match(closingBracket->next(), "(|::")) { if (copy) addNamespace(templateDeclaration, tok3); } } } else { // don't modify friend if (Token::Match(tok3->tokAt(-3), "> friend class|struct|union")) { if (copy) mTokenList.addtoken(tok3); } else if (copy) { // add namespace if necessary if (!templateDeclaration.scope().empty() && (isClass ? tok3->strAt(1) != "(" : true)) { addNamespace(templateDeclaration, tok3); } mTokenList.addtoken(newName, tok3); } else if (!Token::Match(tok3->next(), ":|{|=|;")) tok3->str(newName); continue; } } // copy if (copy) mTokenList.addtoken(tok3); // look for template definitions if (Token::simpleMatch(tok3, "template <")) { Token * tok2 = findTemplateDeclarationEnd(tok3); if (tok2) templates.push(tok2); } else if (!templates.empty() && templates.top() == tok3) templates.pop(); if (Token::Match(tok3, "%type% <") && !Token::Match(tok3, "template|static_cast|const_cast|reinterpret_cast|dynamic_cast") && Token::Match(tok3->next()->findClosingBracket(), ">|>>")) { const Token *closingBracket = tok3->next()->findClosingBracket(); if (Token::simpleMatch(closingBracket->next(), "&")) { int num = 0; const Token *par = tok3->next(); while (num < typeParametersInDeclaration.size() && par != closingBracket) { const std::string pattern("[<,] " + typeParametersInDeclaration[num]->str() + " [,>]"); if (!Token::Match(par, pattern.c_str())) break; ++num; par = par->tokAt(2); } if (num < typeParametersInDeclaration.size() || par != closingBracket) continue; } std::string scope; const Token *prev = tok3; for (; Token::Match(prev->tokAt(-2), "%name% ::"); prev = prev->tokAt(-2)) { if (scope.empty()) scope = prev->strAt(-2); else scope = prev->strAt(-2) + " :: " + scope; } // check for global scope if (prev->strAt(-1) != "::") { // adjust for current scope std::string token_scope = tok3->scopeInfo()->name; std::string::size_type end = token_scope.find_last_of(" :: "); if (end != std::string::npos) { token_scope.resize(end); if (scope.empty()) scope = token_scope; else scope = token_scope + " :: " + scope; } } // don't add instantiations in template definitions if (templates.empty()) { if (copy) newInstantiations.emplace_back(mTokenList.back(), scope); else if (!inTemplateDefinition) newInstantiations.emplace_back(tok3, scope); } } // link() newly tokens manually else if (copy) { if (tok3->str() == "{") { brackets.push(mTokenList.back()); } else if (tok3->str() == "(") { brackets.push(mTokenList.back()); } else if (tok3->str() == "[") { brackets.push(mTokenList.back()); } else if (tok3->str() == "}") { assert(brackets.empty() == false); assert(brackets.top()->str() == "{"); Token::createMutualLinks(brackets.top(), mTokenList.back()); if (tok3->strAt(1) == ";") { const Token * tokSemicolon = tok3->next(); mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->fileIndex()); } brackets.pop(); if (brackets.empty() && !Token::Match(tok3, "} >|,|%cop%")) { inTemplateDefinition = false; break; } } else if (tok3->str() == ")") { assert(brackets.empty() == false); assert(brackets.top()->str() == "("); Token::createMutualLinks(brackets.top(), mTokenList.back()); brackets.pop(); } else if (tok3->str() == "]") { assert(brackets.empty() == false); assert(brackets.top()->str() == "["); Token::createMutualLinks(brackets.top(), mTokenList.back()); brackets.pop(); } } } assert(brackets.empty()); } // add new instantiations for (const auto & inst : newInstantiations) { std::string fullName = inst.scope + (inst.scope.empty() ? "" : " :: ") + inst.token->str(); simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket()); // only add recursive instantiation if its arguments are a constant expression if (templateDeclaration.fullName() != fullName || (inst.token->tokAt(2)->isNumber() || inst.token->tokAt(2)->isStandardType())) mTemplateInstantiations.emplace_back(inst.token, inst.scope); } } static bool isLowerThanLogicalAnd(const Token *lower) { return lower->isAssignmentOp() || Token::Match(lower, "}|;|(|[|]|)|,|?|:|%oror%|return|throw|case"); } static bool isLowerThanOr(const Token* lower) { return isLowerThanLogicalAnd(lower) || lower->str() == "&&"; } static bool isLowerThanXor(const Token* lower) { return isLowerThanOr(lower) || lower->str() == "|"; } static bool isLowerThanAnd(const Token* lower) { return isLowerThanXor(lower) || lower->str() == "^"; } static bool isLowerThanShift(const Token* lower) { return isLowerThanAnd(lower) || lower->str() == "&"; } static bool isLowerThanPlusMinus(const Token* lower) { return isLowerThanShift(lower) || Token::Match(lower, "%comp%|<<|>>"); } static bool isLowerThanMulDiv(const Token* lower) { return isLowerThanPlusMinus(lower) || Token::Match(lower, "+|-"); } static bool isLowerEqualThanMulDiv(const Token* lower) { return isLowerThanMulDiv(lower) || Token::Match(lower, "[*/%]"); } bool TemplateSimplifier::simplifyNumericCalculations(Token *tok, bool isTemplate) { bool ret = false; // (1-2) while (tok->tokAt(3) && tok->isNumber() && tok->tokAt(2)->isNumber()) { // %any% %num% %any% %num% %any% const Token *before = tok->previous(); if (!before) break; const Token* op = tok->next(); const Token* after = tok->tokAt(3); const std::string &num1 = op->previous()->str(); const std::string &num2 = op->next()->str(); if (Token::Match(before, "* %num% /") && (num2 != "0") && num1 == MathLib::multiply(num2, MathLib::divide(num1, num2))) { // Division where result is a whole number } else if (!((op->str() == "*" && (isLowerThanMulDiv(before) || before->str() == "*") && isLowerEqualThanMulDiv(after)) || // associative (Token::Match(op, "[/%]") && isLowerThanMulDiv(before) && isLowerEqualThanMulDiv(after)) || // NOT associative (Token::Match(op, "[+-]") && isLowerThanMulDiv(before) && isLowerThanMulDiv(after)) || // Only partially (+) associative, but handled later (Token::Match(op, ">>|<<") && isLowerThanShift(before) && isLowerThanPlusMinus(after)) || // NOT associative (op->str() == "&" && isLowerThanShift(before) && isLowerThanShift(after)) || // associative (op->str() == "^" && isLowerThanAnd(before) && isLowerThanAnd(after)) || // associative (op->str() == "|" && isLowerThanXor(before) && isLowerThanXor(after)) || // associative (op->str() == "&&" && isLowerThanOr(before) && isLowerThanOr(after)) || (op->str() == "||" && isLowerThanLogicalAnd(before) && isLowerThanLogicalAnd(after)))) break; // Don't simplify "%num% / 0" if (Token::Match(op, "[/%] 0")) { if (isTemplate) throw InternalError(op, "Instantiation error: Divide by zero in template instantiation.", InternalError::INSTANTIATION); else return ret; } // Integer operations if (Token::Match(op, ">>|<<|&|^|%or%")) { // Don't simplify if operand is negative, shifting with negative // operand is UB. Bitmasking with negative operand is implementation // defined behaviour. if (MathLib::isNegative(num1) || MathLib::isNegative(num2)) break; const MathLib::value v1(num1); const MathLib::value v2(num2); if (!v1.isInt() || !v2.isInt()) break; switch (op->str()[0]) { case '<': tok->str((v1 << v2).str()); break; case '>': tok->str((v1 >> v2).str()); break; case '&': tok->str((v1 & v2).str()); break; case '|': tok->str((v1 | v2).str()); break; case '^': tok->str((v1 ^ v2).str()); break; }; } // Logical operations else if (Token::Match(op, "%oror%|&&")) { const bool op1 = !MathLib::isNullValue(num1); const bool op2 = !MathLib::isNullValue(num2); const bool result = (op->str() == "||") ? (op1 || op2) : (op1 && op2); tok->str(result ? "1" : "0"); } else if (Token::Match(tok->previous(), "- %num% - %num%")) tok->str(MathLib::add(num1, num2)); else if (Token::Match(tok->previous(), "- %num% + %num%")) tok->str(MathLib::subtract(num1, num2)); else { try { tok->str(MathLib::calculate(num1, num2, op->str()[0])); } catch (InternalError &e) { e.token = tok; throw; } } tok->deleteNext(2); ret = true; } return ret; } static Token *skipTernaryOp(Token *tok, Token *backToken) { unsigned int colonLevel = 1; while (nullptr != (tok = tok->next())) { if (tok->str() == "?") { ++colonLevel; } else if (tok->str() == ":") { --colonLevel; if (colonLevel == 0) { tok = tok->next(); break; } } if (tok->link() && tok->str() == "(") tok = tok->link(); else if (Token::Match(tok->next(), "[{};)]") || tok->next() == backToken) break; } if (colonLevel > 0) // Ticket #5214: Make sure the ':' matches the proper '?' return nullptr; return tok; } void TemplateSimplifier::simplifyTemplateArgs(Token *start, Token *end) { // start could be erased so use the token before start if available Token * first = (start && start->previous()) ? start->previous() : mTokenList.front(); bool again = true; while (again) { again = false; for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { if (tok->str() == "sizeof") { // sizeof('x') if (Token::Match(tok->next(), "( %char% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream sz; sz << 1; tok->str(sz.str()); again = true; } // sizeof ("text") else if (Token::Match(tok->next(), "( %str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream ostr; ostr << (Token::getStrLength(tok) + 1); tok->str(ostr.str()); again = true; } else if (Token::Match(tok->next(), "( %type% * )")) { tok->str(MathLib::toString(mTokenizer->sizeOfType(tok->tokAt(3)))); tok->deleteNext(4); again = true; } else if (Token::simpleMatch(tok->next(), "( * )")) { tok->str(MathLib::toString(mTokenizer->sizeOfType(tok->tokAt(2)))); tok->deleteNext(3); again = true; } else if (Token::Match(tok->next(), "( %type% )")) { const unsigned int size = mTokenizer->sizeOfType(tok->tokAt(2)); if (size > 0) { tok->str(MathLib::toString(size)); tok->deleteNext(3); again = true; } } else if (tok->strAt(1) == "(") { tok = tok->linkAt(1); } } else if (Token::Match(tok, "%num% %comp% %num%") && MathLib::isInt(tok->str()) && MathLib::isInt(tok->strAt(2))) { if ((Token::Match(tok->previous(), "(|&&|%oror%|,") || tok == start) && (Token::Match(tok->tokAt(3), ")|&&|%oror%|?") || tok->tokAt(3) == end)) { const MathLib::bigint op1(MathLib::toLongNumber(tok->str())); const std::string &cmp(tok->next()->str()); const MathLib::bigint op2(MathLib::toLongNumber(tok->strAt(2))); std::string result; if (cmp == "==") result = (op1 == op2) ? "true" : "false"; else if (cmp == "!=") result = (op1 != op2) ? "true" : "false"; else if (cmp == "<=") result = (op1 <= op2) ? "true" : "false"; else if (cmp == ">=") result = (op1 >= op2) ? "true" : "false"; else if (cmp == "<") result = (op1 < op2) ? "true" : "false"; else result = (op1 > op2) ? "true" : "false"; tok->str(result); tok->deleteNext(2); again = true; tok = tok->previous(); } } } if (simplifyCalculations(first->next(), end)) again = true; for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { if (tok->str() == "?" && ((tok->previous()->isNumber() || tok->previous()->isBoolean()) || Token::Match(tok->tokAt(-3), "( %bool%|%num% )"))) { const int offset = (tok->previous()->str() == ")") ? 2 : 1; // Find the token ":" then go to the next token Token *colon = skipTernaryOp(tok, end); if (!colon || colon->previous()->str() != ":" || !colon->next()) continue; //handle the GNU extension: "x ? : y" <-> "x ? x : y" if (colon->previous() == tok->next()) tok->insertToken(tok->strAt(-offset)); // go back before the condition, if possible tok = tok->tokAt(-2); if (offset == 2) { // go further back before the "(" tok = tok->tokAt(-2); //simplify the parentheses tok->deleteNext(); tok->next()->deleteNext(); } if (Token::Match(tok->next(), "false|0")) { // Use code after colon, remove code before it. Token::eraseTokens(tok, colon); tok = tok->next(); again = true; } // The condition is true. Delete the operator after the ":".. else { // delete the condition token and the "?" tok->deleteNext(2); unsigned int ternaryOplevel = 0; for (const Token *endTok = colon; endTok; endTok = endTok->next()) { if (Token::Match(endTok, "(|[|{")) endTok = endTok->link(); else if (endTok->str() == "<" && (endTok->strAt(1) == ">" || templateParameters(endTok))) endTok = endTok->findClosingBracket(); else if (endTok->str() == "?") ++ternaryOplevel; else if (Token::Match(endTok, ")|}|]|;|,|:|>")) { if (endTok->str() == ":" && ternaryOplevel) --ternaryOplevel; else if (endTok->str() == ">" && !end) ; else { Token::eraseTokens(colon->tokAt(-2), endTok); again = true; break; } } } } } } for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { if (Token::Match(tok, "( %num%|%bool% )") && (tok->previous() && !Token::Match(tok->previous(), "%name%"))) { tok->deleteThis(); tok->deleteNext(); again = true; } } } } static bool validTokenStart(bool bounded, const Token *tok, const Token *frontToken, int offset) { if (!bounded) return true; if (frontToken) frontToken = frontToken->previous(); while (tok && offset <= 0) { if (tok == frontToken) return false; ++offset; tok = tok->previous(); } return tok && offset > 0; } static bool validTokenEnd(bool bounded, const Token *tok, const Token *backToken, int offset) { if (!bounded) return true; while (tok && offset >= 0) { if (tok == backToken) return false; --offset; tok = tok->next(); } return tok && offset < 0; } // TODO: This is not the correct class for simplifyCalculations(), so it // should be moved away. bool TemplateSimplifier::simplifyCalculations(Token* frontToken, Token *backToken, bool isTemplate) { bool ret = false; const bool bounded = frontToken || backToken; if (!frontToken) { frontToken = mTokenList.front(); } for (Token *tok = frontToken; tok && tok != backToken; tok = tok->next()) { // Remove parentheses around variable.. // keep parentheses here: dynamic_cast(p); // keep parentheses here: A operator * (int); // keep parentheses here: int ( * ( * f ) ( ... ) ) (int) ; // keep parentheses here: int ( * * ( * compilerHookVector ) (void) ) ( ) ; // keep parentheses here: operator new [] (size_t); // keep parentheses here: Functor()(a ... ) // keep parentheses here: ) ( var ) ; if (validTokenEnd(bounded, tok, backToken, 4) && (Token::Match(tok->next(), "( %name% ) ;|)|,|]") || (Token::Match(tok->next(), "( %name% ) %cop%") && (tok->tokAt(2)->varId()>0 || !Token::Match(tok->tokAt(4), "[*&+-~]")))) && !tok->isName() && tok->str() != ">" && tok->str() != ")" && tok->str() != "]") { tok->deleteNext(); tok = tok->next(); tok->deleteNext(); ret = true; } if (validTokenEnd(bounded, tok, backToken, 3) && Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) { tok->str(MathLib::toString(MathLib::toLongNumber(tok->str()))); } if (validTokenEnd(bounded, tok, backToken, 5) && Token::Match(tok, "decltype ( %type% { } )")) { tok->deleteThis(); tok->deleteThis(); tok->deleteNext(); tok->deleteNext(); tok->deleteNext(); ret = true; } if (validTokenEnd(bounded, tok, backToken, 3) && Token::Match(tok, "decltype ( %bool%|%num% )")) { tok->deleteThis(); tok->deleteThis(); if (tok->isBoolean()) tok->str("bool"); else if (MathLib::isFloat(tok->str())) { // MathLib::getSuffix doesn't work for floating point numbers char suffix = tok->str().back(); if (suffix == 'f' || suffix == 'F') tok->str("float"); else if (suffix == 'l' || suffix == 'L') { tok->str("double"); tok->isLong(true); } else tok->str("double"); } else if (MathLib::isInt(tok->str())) { std::string suffix = MathLib::getSuffix(tok->str()); if (suffix.find("LL") != std::string::npos) { tok->str("long"); tok->isLong(true); } else if (suffix.find('L') != std::string::npos) tok->str("long"); else tok->str("int"); tok->isUnsigned(suffix.find('U') != std::string::npos); } tok->deleteNext(); ret = true; } if (validTokenEnd(bounded, tok, backToken, 2) && Token::Match(tok, "char|short|int|long { }")) { tok->str("0"); // FIXME add type suffix tok->isSigned(false); tok->isUnsigned(false); tok->isLong(false); tok->deleteNext(); tok->deleteNext(); ret = true; } if (tok && tok->isNumber()) { if (validTokenEnd(bounded, tok, backToken, 2) && simplifyNumericCalculations(tok, isTemplate)) { ret = true; Token *prev = tok->tokAt(-2); while (validTokenStart(bounded, tok, frontToken, -2) && prev && simplifyNumericCalculations(prev, isTemplate)) { tok = prev; prev = prev->tokAt(-2); } } // Remove redundant conditions (0&&x) (1||x) if (validTokenStart(bounded, tok, frontToken, -1) && validTokenEnd(bounded, tok, backToken, 1) && (Token::Match(tok->previous(), "[(=,] 0 &&") || Token::Match(tok->previous(), "[(=,] 1 %oror%"))) { unsigned int par = 0; const Token *tok2 = tok; const bool andAnd = (tok->next()->str() == "&&"); for (; tok2; tok2 = tok2->next()) { if (tok2->str() == "(" || tok2->str() == "[") ++par; else if (tok2->str() == ")" || tok2->str() == "]") { if (par == 0) break; --par; } else if (par == 0 && isLowerThanLogicalAnd(tok2) && (andAnd || tok2->str() != "||")) break; } if (tok2) { eraseTokens(tok, tok2); ret = true; } continue; } if (tok->str() == "0" && validTokenStart(bounded, tok, frontToken, -1)) { if (validTokenEnd(bounded, tok, backToken, 1) && ((Token::Match(tok->previous(), "[+-] 0 %cop%|;") && isLowerThanMulDiv(tok->next())) || (Token::Match(tok->previous(), "%or% 0 %cop%|;") && isLowerThanXor(tok->next())))) { tok = tok->previous(); if (Token::Match(tok->tokAt(-4), "[;{}] %name% = %name% [+-|] 0 ;") && tok->strAt(-3) == tok->previous()->str()) { tok = tok->tokAt(-4); tok->deleteNext(5); } else { tok = tok->previous(); tok->deleteNext(2); } ret = true; } else if (validTokenEnd(bounded, tok, backToken, 1) && (Token::Match(tok->previous(), "[=([,] 0 [+|]") || Token::Match(tok->previous(), "return|case 0 [+|]"))) { tok = tok->previous(); tok->deleteNext(2); ret = true; } else if ((((Token::Match(tok->previous(), "[=[(,] 0 * %name%|%num% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 0 *|&& %name%|%num% ,|:|;|=|%cop%")) && validTokenEnd(bounded, tok, backToken, 3)) || (((Token::Match(tok->previous(), "[=[(,] 0 * (") || Token::Match(tok->previous(), "return|case 0 *|&& (")) && validTokenEnd(bounded, tok, backToken, 2))))) { tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } else if (validTokenEnd(bounded, tok, backToken, 4) && (Token::Match(tok->previous(), "[=[(,] 0 && *|& %any% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 0 && *|& %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } } if (tok->str() == "1" && validTokenStart(bounded, tok, frontToken, -1)) { if (validTokenEnd(bounded, tok, backToken, 3) && (Token::Match(tok->previous(), "[=[(,] 1 %oror% %any% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 1 %oror% %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } else if (validTokenEnd(bounded, tok, backToken, 4) && (Token::Match(tok->previous(), "[=[(,] 1 %oror% *|& %any% ,|]|)|;|=|%cop%") || Token::Match(tok->previous(), "return|case 1 %oror% *|& %any% ,|:|;|=|%cop%"))) { tok->deleteNext(); tok->deleteNext(); if (tok->next()->str() == "(") eraseTokens(tok, tok->next()->link()); tok->deleteNext(); ret = true; } } if ((Token::Match(tok->tokAt(-2), "%any% * 1") && validTokenStart(bounded, tok, frontToken, -2)) || (Token::Match(tok->previous(), "%any% 1 *") && validTokenStart(bounded, tok, frontToken, -1))) { tok = tok->previous(); if (tok->str() == "*") tok = tok->previous(); tok->deleteNext(2); ret = true; } // Remove parentheses around number.. if (validTokenStart(bounded, tok, frontToken, -2) && Token::Match(tok->tokAt(-2), "%op%|< ( %num% )") && tok->strAt(-2) != ">") { tok = tok->previous(); tok->deleteThis(); tok->deleteNext(); ret = true; } if (validTokenStart(bounded, tok, frontToken, -1) && validTokenEnd(bounded, tok, backToken, 1) && (Token::Match(tok->previous(), "( 0 [|+]") || Token::Match(tok->previous(), "[|+-] 0 )"))) { tok = tok->previous(); if (Token::Match(tok, "[|+-]")) tok = tok->previous(); tok->deleteNext(2); ret = true; } if (validTokenEnd(bounded, tok, backToken, 2) && Token::Match(tok, "%num% %comp% %num%") && MathLib::isInt(tok->str()) && MathLib::isInt(tok->strAt(2))) { if (validTokenStart(bounded, tok, frontToken, -1) && Token::Match(tok->previous(), "(|&&|%oror%") && Token::Match(tok->tokAt(3), ")|&&|%oror%|?")) { const MathLib::bigint op1(MathLib::toLongNumber(tok->str())); const std::string &cmp(tok->next()->str()); const MathLib::bigint op2(MathLib::toLongNumber(tok->strAt(2))); std::string result; if (cmp == "==") result = (op1 == op2) ? "1" : "0"; else if (cmp == "!=") result = (op1 != op2) ? "1" : "0"; else if (cmp == "<=") result = (op1 <= op2) ? "1" : "0"; else if (cmp == ">=") result = (op1 >= op2) ? "1" : "0"; else if (cmp == "<") result = (op1 < op2) ? "1" : "0"; else result = (op1 > op2) ? "1" : "0"; tok->str(result); tok->deleteNext(2); ret = true; tok = tok->previous(); } } } } return ret; } const Token * TemplateSimplifier::getTemplateParametersInDeclaration( const Token * tok, std::vector & typeParametersInDeclaration) { assert(tok->strAt(-1) == "<"); typeParametersInDeclaration.clear(); const Token *end = tok->previous()->findClosingBracket(); for (; tok && tok!= end; tok = tok->next()) { if (Token::simpleMatch(tok, "template <")) { const Token *closing = tok->next()->findClosingBracket(); if (closing) tok = closing->next(); } else if (tok->link() && Token::Match(tok, "{|(|[")) tok = tok->link(); else if (Token::Match(tok, "%name% ,|>|=")) typeParametersInDeclaration.push_back(tok); } return tok; } bool TemplateSimplifier::matchSpecialization( const Token *templateDeclarationNameToken, const Token *templateInstantiationNameToken, const std::list & specializations) { // Is there a matching specialization? for (std::list::const_iterator it = specializations.begin(); it != specializations.end(); ++it) { if (!Token::Match(*it, "%name% <")) continue; const Token *startToken = (*it); while (startToken->previous() && !Token::Match(startToken->previous(), "[;{}]")) startToken = startToken->previous(); if (!Token::simpleMatch(startToken, "template <")) continue; std::vector templateParameters; getTemplateParametersInDeclaration(startToken->tokAt(2), templateParameters); const Token *instToken = templateInstantiationNameToken->tokAt(2); const Token *declToken = (*it)->tokAt(2); const Token * const endToken = (*it)->next()->findClosingBracket(); if (!endToken) continue; while (declToken != endToken) { if (declToken->str() != instToken->str()) { int nr = 0; while (nr < templateParameters.size() && templateParameters[nr]->str() != declToken->str()) ++nr; if (nr == templateParameters.size()) break; } declToken = declToken->next(); instToken = instToken->next(); } if (declToken && instToken && declToken == endToken && instToken->str() == ">") { // specialization matches. return templateDeclarationNameToken == *it; } } // No specialization matches. Return true if the declaration is not a specialization. return Token::Match(templateDeclarationNameToken, "%name% !!<") && (templateDeclarationNameToken->str().find('<') == std::string::npos); } std::string TemplateSimplifier::getNewName( Token *tok2, std::list &typeStringsUsedInTemplateInstantiation) { std::string typeForNewName; unsigned int indentlevel = 0; const Token * endToken = tok2->next()->findClosingBracket(); for (Token *tok3 = tok2->tokAt(2); tok3 != endToken && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) { // #2721 - unhandled [ => bail out if (tok3->str() == "[") { typeForNewName.clear(); break; } if (!tok3->next()) { typeForNewName.clear(); break; } if (Token::Match(tok3->tokAt(-2), "<|,|:: %name% <") && (tok3->strAt(1) == ">" || templateParameters(tok3))) ++indentlevel; else if (indentlevel > 0 && Token::Match(tok3, "> [,>]")) --indentlevel; if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) { mTypesUsedInTemplateInstantiation.emplace_back(tok3, ""); } if (tok3->str() == "(") ++indentlevel; else if (tok3->str() == ")") --indentlevel; const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const"; if (!constconst) { typeStringsUsedInTemplateInstantiation.push_back(tok3->str()); } // add additional type information if (!constconst && !Token::Match(tok3, "class|struct|enum")) { if (tok3->isUnsigned()) typeForNewName += "unsigned"; else if (tok3->isSigned()) typeForNewName += "signed"; if (tok3->isLong()) typeForNewName += "long"; if (!typeForNewName.empty()) typeForNewName += ' '; typeForNewName += tok3->str(); } } return typeForNewName; } bool TemplateSimplifier::simplifyTemplateInstantiations( const TokenAndName &templateDeclaration, const std::list &specializations, const std::time_t maxtime, std::set &expandedtemplates) { // this variable is not used at the moment. The intention was to // allow continuous instantiations until all templates has been expanded //bool done = false; // Contains tokens such as "T" std::vector typeParametersInDeclaration; getTemplateParametersInDeclaration(templateDeclaration.token()->tokAt(2), typeParametersInDeclaration); const bool printDebug = mSettings->debugwarnings; const bool specialized = templateDeclaration.isSpecialization(); const bool isfunc = templateDeclaration.isFunction(); const bool isVar = templateDeclaration.isVariable(); // locate template usage.. std::string::size_type numberOfTemplateInstantiations = mTemplateInstantiations.size(); unsigned int recursiveCount = 0; bool instantiated = false; for (const TokenAndName &instantiation : mTemplateInstantiations) { if (numberOfTemplateInstantiations != mTemplateInstantiations.size()) { numberOfTemplateInstantiations = mTemplateInstantiations.size(); ++recursiveCount; if (recursiveCount > 100) { // bail out.. break; } } // already simplified if (!Token::Match(instantiation.token(), "%name% <")) continue; if (instantiation.fullName() != templateDeclaration.fullName()) { // FIXME: fallback to not matching scopes until type deduction works // names must match if (instantiation.name() != templateDeclaration.name()) continue; // scopes must match when present if (!instantiation.scope().empty() && !templateDeclaration.scope().empty()) continue; } // A global function can't be called through a pointer. if (templateDeclaration.isFunction() && templateDeclaration.scope().empty() && (instantiation.token()->strAt(-1) == "." || Token::simpleMatch(instantiation.token()->tokAt(-2), ". template"))) continue; if (!matchSpecialization(templateDeclaration.nameToken(), instantiation.token(), specializations)) continue; Token * const tok2 = instantiation.token(); if (mErrorLogger && !mTokenList.getFiles().empty()) mErrorLogger->reportProgress(mTokenList.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); #ifdef MAXTIME if (std::time(0) > maxtime) return false; #else (void)maxtime; #endif assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 const Token *startToken = tok2; while (Token::Match(startToken->tokAt(-2), "%name% :: %name%") || Token::Match(startToken->tokAt(-2), "> :: %name%")) { if (startToken->strAt(-2) == ">") { const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); if (tok3) startToken = tok3->previous(); else break; } else startToken = startToken->tokAt(-2); } if (Token::Match(startToken->previous(), ";|{|}|=|const") && (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%"))) continue; // New type.. mTypesUsedInTemplateInstantiation.clear(); std::list typeStringsUsedInTemplateInstantiation; std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); if ((typeForNewName.empty() && !templateDeclaration.isVariadic()) || (!typeParametersInDeclaration.empty() && typeParametersInDeclaration.size() != mTypesUsedInTemplateInstantiation.size())) { if (printDebug && mErrorLogger) { std::list callstack(1, tok2); mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", "Failed to instantiate template \"" + instantiation.name() + "\". The checking continues anyway.", false)); } if (typeForNewName.empty()) continue; break; } // New classname/funcname.. const std::string newName(templateDeclaration.name() + " < " + typeForNewName + " >"); const std::string newFullName(templateDeclaration.scope() + (templateDeclaration.scope().empty() ? "" : " :: ") + newName); if (expandedtemplates.insert(newFullName).second) { expandTemplate(templateDeclaration, instantiation, typeParametersInDeclaration, newName, !specialized && !isVar); instantiated = true; mChanged = true; } // Replace all these template usages.. replaceTemplateUsage(instantiation, typeStringsUsedInTemplateInstantiation, newName); } // process uninstantiated templates // TODO: remove the specialized check and handle all uninstantiated templates someday. if (!instantiated && specialized) { Token * tok2 = const_cast(templateDeclaration.nameToken()); if (mErrorLogger && !mTokenList.getFiles().empty()) mErrorLogger->reportProgress(mTokenList.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); #ifdef MAXTIME if (std::time(0) > maxtime) return false; #else (void)maxtime; #endif assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 Token *startToken = tok2; while (Token::Match(startToken->tokAt(-2), "%name% :: %name%") || Token::Match(startToken->tokAt(-2), "> :: %name%")) { if (startToken->strAt(-2) == ">") { const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); if (tok3) startToken = tok3->previous(); else break; } else startToken = startToken->tokAt(-2); } if (Token::Match(startToken->previous(), ";|{|}|=|const") && (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%"))) return false; // already simplified if (!Token::Match(tok2, "%name% <")) return false; if (!matchSpecialization(templateDeclaration.nameToken(), tok2, specializations)) return false; // New type.. mTypesUsedInTemplateInstantiation.clear(); std::list typeStringsUsedInTemplateInstantiation; std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); if (typeForNewName.empty()) { if (printDebug && mErrorLogger) { std::list callstack(1, tok2); mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &mTokenList, Severity::debug, "debug", "Failed to instantiate template \"" + templateDeclaration.name() + "\". The checking continues anyway.", false)); } return false; } // New classname/funcname.. const std::string newName(templateDeclaration.name() + " < " + typeForNewName + " >"); const std::string newFullName(templateDeclaration.scope() + (templateDeclaration.scope().empty() ? "" : " :: ") + newName); if (expandedtemplates.insert(newFullName).second) { expandTemplate(templateDeclaration, templateDeclaration, typeParametersInDeclaration, newName, !specialized && !isVar); instantiated = true; mChanged = true; } // Replace all these template usages.. replaceTemplateUsage(templateDeclaration, typeStringsUsedInTemplateInstantiation, newName); } // Template has been instantiated .. then remove the template declaration return instantiated; } static bool matchTemplateParameters(const Token *nameTok, const std::list &strings) { std::list::const_iterator it = strings.begin(); const Token *tok = nameTok->tokAt(2); while (tok && it != strings.end() && *it == tok->str()) { tok = tok->next(); ++it; } return it == strings.end() && tok && tok->str() == ">"; } void TemplateSimplifier::replaceTemplateUsage( const TokenAndName &instantiation, const std::list &typeStringsUsedInTemplateInstantiation, const std::string &newName) { std::list< std::pair > removeTokens; for (Token *nameTok = mTokenList.front(); nameTok; nameTok = nameTok->next()) { if (!Token::Match(nameTok, "%name% <") || Token::Match(nameTok, "template|const_cast|dynamic_cast|reinterpret_cast|static_cast")) continue; std::set & pointers = nameTok->templateSimplifierPointers(); // check if instantiation matches token instantiation from pointer if (pointers.size()) { // check full name if (instantiation.fullName() != (*pointers.begin())->fullName()) { // FIXME: fallback to just matching name if (nameTok->str() != instantiation.name()) continue; } } // no pointer available look at tokens directly else { // FIXME: fallback to just matching name if (nameTok->str() != instantiation.name()) continue; } if (!matchTemplateParameters(nameTok, typeStringsUsedInTemplateInstantiation)) continue; // match parameters Token * tok2 = nameTok->tokAt(2); const Token * endToken = nameTok->next()->findClosingBracket(); unsigned int typeCountInInstantiation = tok2->str() == ">" ? 0U : 1U; const Token *typetok = (!mTypesUsedInTemplateInstantiation.empty()) ? mTypesUsedInTemplateInstantiation[0].token() : nullptr; unsigned int indentlevel2 = 0; // indentlevel for tokgt while (tok2 != endToken && (indentlevel2 > 0 || tok2->str() != ">")) { if (tok2->str() == "<" && (tok2->strAt(1) == ">" || templateParameters(tok2))) ++indentlevel2; else if (tok2->str() == "(") ++indentlevel2; else if (tok2->str() == ")") --indentlevel2; else if (indentlevel2 > 0 && Token::Match(tok2, "> [,>]")) --indentlevel2; else if (indentlevel2 == 0) { if (tok2->str() != ",") { if (!typetok || tok2->isUnsigned() != typetok->isUnsigned() || tok2->isSigned() != typetok->isSigned() || tok2->isLong() != typetok->isLong()) { break; } typetok = typetok->next(); } else { if (typeCountInInstantiation < mTypesUsedInTemplateInstantiation.size()) typetok = mTypesUsedInTemplateInstantiation[typeCountInInstantiation++].token(); else typetok = nullptr; } } tok2 = tok2->next(); } if (!tok2) break; // matching template usage => replace tokens.. // Foo < int > => Foo if (tok2->str() == ">" && typeCountInInstantiation == mTypesUsedInTemplateInstantiation.size()) { const Token * const nameTok1 = nameTok; nameTok->str(newName); for (Token *tok = nameTok1->next(); tok != tok2; tok = tok->next()) { if (tok->isName() && !tok->templateSimplifierPointers().empty()) { std::list::iterator ti; for (ti = mTemplateInstantiations.begin(); ti != mTemplateInstantiations.end();) { if (ti->token() == tok) { mTemplateInstantiations.erase(ti++); break; } else { ++ti; } } } } // Fix crash in #9007 if (Token::simpleMatch(nameTok->previous(), ">")) mTemplateNamePos.erase(nameTok->previous()); removeTokens.emplace_back(nameTok, tok2->next()); } nameTok = tok2; } while (!removeTokens.empty()) { eraseTokens(removeTokens.back().first, removeTokens.back().second); removeTokens.pop_back(); } } static bool specMatch( const TemplateSimplifier::TokenAndName &spec, const TemplateSimplifier::TokenAndName &decl) { // make sure decl is really a declaration if (decl.isPartialSpecialization() || decl.isSpecialization() || decl.isAlias() || decl.isFriend()) return false; return spec.isSameFamily(decl); } void TemplateSimplifier::getSpecializations() { // try to locate a matching declaration for each user defined specialization for (auto & spec : mTemplateDeclarations) { if (spec.isSpecialization()) { bool found = false; for (auto & decl : mTemplateDeclarations) { if (!specMatch(spec, decl)) continue; // make sure the scopes and names match if (spec.fullName() == decl.fullName()) { // @todo make sure function parameters also match mTemplateSpecializationMap[spec.token()] = decl.token(); found = true; } } if (!found) { for (auto & decl : mTemplateForwardDeclarations) { if (!specMatch(spec, decl)) continue; // make sure the scopes and names match if (spec.fullName() == decl.fullName()) { // @todo make sure function parameters also match mTemplateSpecializationMap[spec.token()] = decl.token(); } } } } } } void TemplateSimplifier::getPartialSpecializations() { // try to locate a matching declaration for each user defined partial specialization for (auto & spec : mTemplateDeclarations) { if (spec.isPartialSpecialization()) { bool found = false; for (auto & decl : mTemplateDeclarations) { if (!specMatch(spec, decl)) continue; // make sure the scopes and names match if (spec.fullName() == decl.fullName()) { // @todo make sure function parameters also match mTemplatePartialSpecializationMap[spec.token()] = decl.token(); found = true; } } if (!found) { for (auto & decl : mTemplateForwardDeclarations) { if (!specMatch(spec, decl)) continue; // make sure the scopes and names match if (spec.fullName() == decl.fullName()) { // @todo make sure function parameters also match mTemplatePartialSpecializationMap[spec.token()] = decl.token(); } } } } } } void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() { // try to locate a matching declaration for each forward declaration for (const auto & forwardDecl : mTemplateForwardDeclarations) { std::vector params1; getTemplateParametersInDeclaration(forwardDecl.token()->tokAt(2), params1); for (auto & decl : mTemplateDeclarations) { // skip partializations, type aliases and friends if (decl.isPartialSpecialization() || decl.isAlias() || decl.isFriend()) continue; std::vector params2; getTemplateParametersInDeclaration(decl.token()->tokAt(2), params2); // make sure the number of arguments match if (params1.size() == params2.size()) { // make sure the scopes and names match if (forwardDecl.fullName() == decl.fullName()) { // save forward declaration for lookup later if ((decl.nameToken()->strAt(1) == "(" && forwardDecl.nameToken()->strAt(1) == "(") || (decl.nameToken()->strAt(1) == "{" && forwardDecl.nameToken()->strAt(1) == ";")) { mTemplateForwardDeclarationsMap[decl.token()] = forwardDecl.token(); } for (size_t k = 0; k < params1.size(); k++) { // copy default value to declaration if not present if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") { int level = 0; const Token *end = params1[k]->next(); while (end && !(level == 0 && Token::Match(end, ",|>"))) { if (Token::Match(end, "{|(|<")) level++; else if (Token::Match(end, "}|)|>")) level--; end = end->next(); } if (end) TokenList::copyTokens(const_cast(params2[k]), params1[k]->next(), end->previous()); break; } } // update parameter end pointer decl.paramEnd(decl.token()->next()->findClosingBracket()); } } } } } void TemplateSimplifier::printOut(const TokenAndName &tokenAndName, const std::string &indent) const { std::cout << indent << "token: "; if (tokenAndName.token()) std::cout << "\"" << tokenAndName.token()->str() << "\" " << mTokenList.fileLine(tokenAndName.token()); else std::cout << "nullptr"; std::cout << std::endl; std::cout << indent << "scope: \"" << tokenAndName.scope() << "\"" << std::endl; std::cout << indent << "name: \"" << tokenAndName.name() << "\"" << std::endl; std::cout << indent << "fullName: \"" << tokenAndName.fullName() << "\"" << std::endl; std::cout << indent << "nameToken: "; if (tokenAndName.nameToken()) std::cout << "\"" << tokenAndName.nameToken()->str() << "\" " << mTokenList.fileLine(tokenAndName.nameToken()); else std::cout << "nullptr"; std::cout << std::endl; std::cout << indent << "paramEnd: "; if (tokenAndName.paramEnd()) std::cout << "\"" << tokenAndName.paramEnd()->str() << "\" " << mTokenList.fileLine(tokenAndName.paramEnd()); else std::cout << "nullptr"; std::cout << std::endl; std::cout << indent << "flags: "; if (tokenAndName.isClass()) std::cout << " isClass"; if (tokenAndName.isFunction()) std::cout << " isFunction"; if (tokenAndName.isVariable()) std::cout << " isVariable"; if (tokenAndName.isAlias()) std::cout << " isAlias"; if (tokenAndName.isSpecialization()) std::cout << " isSpecialization"; if (tokenAndName.isPartialSpecialization()) std::cout << " isPartialSpecialization"; if (tokenAndName.isForwardDeclaration()) std::cout << " isForwardDeclaration"; if (tokenAndName.isVariadic()) std::cout << " isVariadic"; if (tokenAndName.isFriend()) std::cout << " isFriend"; std::cout << std::endl; if (tokenAndName.token() && !tokenAndName.paramEnd() && tokenAndName.token()->strAt(1) == "<") { const Token *end = tokenAndName.token()->next()->findClosingBracket(); if (end) { const Token *start = tokenAndName.token()->next(); std::cout << indent << "type: "; while (start && start != end) { std::cout << start->str(); start = start->next(); } std::cout << end->str() << std::endl; } } else if (tokenAndName.isAlias() && tokenAndName.paramEnd()) { if (tokenAndName.aliasStartToken()) { std::cout << indent << "aliasStartToken: \"" << tokenAndName.aliasStartToken()->str() << "\" " << mTokenList.fileLine(tokenAndName.aliasStartToken()) << std::endl; } if (tokenAndName.aliasEndToken()) { std::cout << indent << "aliasEndToken: \"" << tokenAndName.aliasEndToken()->str() << "\" " << mTokenList.fileLine(tokenAndName.aliasEndToken()) << std::endl; } } } void TemplateSimplifier::printOut(const std::string & text) const { std::cout << std::endl; std::cout << text << std::endl; std::cout << std::endl; std::cout << "mTemplateDeclarations: " << mTemplateDeclarations.size() << std::endl; int count = 0; for (const auto & decl : mTemplateDeclarations) { std::cout << "mTemplateDeclarations[" << count++ << "]:" << std::endl; printOut(decl); } std::cout << "mTemplateForwardDeclarations: " << mTemplateForwardDeclarations.size() << std::endl; count = 0; for (const auto & decl : mTemplateForwardDeclarations) { std::cout << "mTemplateForwardDeclarations[" << count++ << "]:" << std::endl; printOut(decl); } std::cout << "mTemplateForwardDeclarationsMap: " << mTemplateForwardDeclarationsMap.size() << std::endl; unsigned int mapIndex = 0; for (const auto & mapItem : mTemplateForwardDeclarationsMap) { unsigned int declIndex = 0; for (const auto & decl : mTemplateDeclarations) { if (mapItem.first == decl.token()) { unsigned int forwardIndex = 0; for (const auto & forwardDecl : mTemplateForwardDeclarations) { if (mapItem.second == forwardDecl.token()) { std::cout << "mTemplateForwardDeclarationsMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << declIndex << "] => mTemplateForwardDeclarations[" << forwardIndex << "]" << std::endl; break; } forwardIndex++; } break; } declIndex++; } mapIndex++; } std::cout << "mTemplateSpecializationMap: " << mTemplateSpecializationMap.size() << std::endl; for (const auto & mapItem : mTemplateSpecializationMap) { unsigned int decl1Index = 0; for (const auto & decl1 : mTemplateDeclarations) { if (decl1.isSpecialization() && mapItem.first == decl1.token()) { bool found = false; unsigned int decl2Index = 0; for (const auto & decl2 : mTemplateDeclarations) { if (mapItem.second == decl2.token()) { std::cout << "mTemplateSpecializationMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << decl1Index << "] => mTemplateDeclarations[" << decl2Index << "]" << std::endl; found = true; break; } decl2Index++; } if (!found) { decl2Index = 0; for (const auto & decl2 : mTemplateForwardDeclarations) { if (mapItem.second == decl2.token()) { std::cout << "mTemplateSpecializationMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << decl1Index << "] => mTemplateForwardDeclarations[" << decl2Index << "]" << std::endl; break; } decl2Index++; } } break; } decl1Index++; } mapIndex++; } std::cout << "mTemplatePartialSpecializationMap: " << mTemplatePartialSpecializationMap.size() << std::endl; for (const auto & mapItem : mTemplatePartialSpecializationMap) { unsigned int decl1Index = 0; for (const auto & decl1 : mTemplateDeclarations) { if (mapItem.first == decl1.token()) { bool found = false; unsigned int decl2Index = 0; for (const auto & decl2 : mTemplateDeclarations) { if (mapItem.second == decl2.token()) { std::cout << "mTemplatePartialSpecializationMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << decl1Index << "] => mTemplateDeclarations[" << decl2Index << "]" << std::endl; found = true; break; } decl2Index++; } if (!found) { decl2Index = 0; for (const auto & decl2 : mTemplateForwardDeclarations) { if (mapItem.second == decl2.token()) { std::cout << "mTemplatePartialSpecializationMap[" << mapIndex << "]:" << std::endl; std::cout << " mTemplateDeclarations[" << decl1Index << "] => mTemplateForwardDeclarations[" << decl2Index << "]" << std::endl; break; } decl2Index++; } } break; } decl1Index++; } mapIndex++; } std::cout << "mTemplateInstantiations: " << mTemplateInstantiations.size() << std::endl; count = 0; for (const auto & decl : mTemplateInstantiations) { std::cout << "mTemplateInstantiations[" << count++ << "]:" << std::endl; printOut(decl); } } void TemplateSimplifier::simplifyTemplates( const std::time_t maxtime, bool &codeWithTemplates) { // convert "sizeof ..." to "sizeof..." for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "sizeof ...")) { tok->str("sizeof..."); tok->deleteNext(); } } // Remove "typename" unless used in template arguments or using type alias.. for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { if (Token::Match(tok, "typename %name%") && !Token::Match(tok->tokAt(-3), "using %name% =")) tok->deleteThis(); if (Token::simpleMatch(tok, "template <")) { tok = tok->next()->findClosingBracket(); if (!tok) break; } } mTokenizer->calculateScopes(); unsigned int passCount = 0; const unsigned int passCountMax = 10; for (; passCount < passCountMax; ++passCount) { if (passCount) { // it may take more than one pass to simplify type aliases bool usingChanged = false; while (mTokenizer->simplifyUsing()) usingChanged = true; if (!usingChanged && !mChanged) break; mChanged = usingChanged; mTemplateDeclarations.clear(); mTemplateForwardDeclarations.clear(); mTemplateForwardDeclarationsMap.clear(); mTemplateSpecializationMap.clear(); mTemplatePartialSpecializationMap.clear(); mTemplateInstantiations.clear(); mInstantiatedTemplates.clear(); mExplicitInstantiationsToDelete.clear(); mTemplateNamePos.clear(); } bool hasTemplates = getTemplateDeclarations(); if (passCount == 0) codeWithTemplates = hasTemplates; // Make sure there is something to simplify. if (mTemplateDeclarations.empty() && mTemplateForwardDeclarations.empty()) return; if (passCount != 0 && mSettings->debugtemplate && mSettings->debugnormal) { std::string title("Template Simplifier pass " + std::to_string(passCount + 1)); mTokenList.front()->printOut(title.c_str(), mTokenList.getFiles()); } // Copy default argument values from forward declaration to declaration fixForwardDeclaredDefaultArgumentValues(); // Locate user defined specializations. getSpecializations(); // Locate user defined partial specializations. getPartialSpecializations(); // Locate possible instantiations of templates.. getTemplateInstantiations(); // Template arguments with default values useDefaultArgumentValues(); simplifyTemplateAliases(); if (mSettings->debugtemplate) printOut("### Template Simplifier pass " + std::to_string(passCount + 1) + " ###"); std::set expandedtemplates; for (std::list::reverse_iterator iter1 = mTemplateDeclarations.rbegin(); iter1 != mTemplateDeclarations.rend(); ++iter1) { if (iter1->isAlias() || iter1->isFriend()) continue; // get specializations.. std::list specializations; for (std::list::const_iterator iter2 = mTemplateDeclarations.begin(); iter2 != mTemplateDeclarations.end(); ++iter2) { if (iter2->isAlias() || iter2->isFriend()) continue; if (iter1->fullName() == iter2->fullName()) specializations.push_back(iter2->nameToken()); } const bool instantiated = simplifyTemplateInstantiations( *iter1, specializations, maxtime, expandedtemplates); if (instantiated) mInstantiatedTemplates.push_back(*iter1); } for (std::list::const_iterator it = mInstantiatedTemplates.begin(); it != mInstantiatedTemplates.end(); ++it) { std::list::iterator decl; for (decl = mTemplateDeclarations.begin(); decl != mTemplateDeclarations.end(); ++decl) { if (decl->token() == it->token()) break; } if (decl != mTemplateDeclarations.end()) { if (it->isSpecialization()) { // delete the "template < >" Token * tok = it->token(); tok->deleteNext(2); tok->deleteThis(); } else { // remove forward declaration if found auto it1 = mTemplateForwardDeclarationsMap.find(it->token()); if (it1 != mTemplateForwardDeclarationsMap.end()) removeTemplate(it1->second); removeTemplate(it->token()); } mTemplateDeclarations.erase(decl); } } // remove out of line member functions while (!mMemberFunctionsToDelete.empty()) { const std::list::iterator it = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), FindToken(mMemberFunctionsToDelete.begin()->token())); // multiple functions can share the same declaration so make sure it hasn't already been deleted if (it != mTemplateDeclarations.end()) { removeTemplate(it->token()); mTemplateDeclarations.erase(it); } mMemberFunctionsToDelete.erase(mMemberFunctionsToDelete.begin()); } // remove explicit instantiations for (TokenAndName & j : mExplicitInstantiationsToDelete) { Token * start = j.token(); if (start) { Token * end = start->next(); while (end && end->str() != ";") end = end->next(); if (start->previous()) start = start->previous(); if (end && end->next()) end = end->next(); eraseTokens(start, end); } } } if (passCount == passCountMax) { if (mSettings->debugwarnings) { const std::list locationList(1, mTokenList.front()); const ErrorLogger::ErrorMessage errmsg(locationList, &mTokenizer->list, Severity::debug, "debug", "TemplateSimplifier: pass count limit hit before simplifications were finished.", false); if (mErrorLogger) mErrorLogger->reportErr(errmsg); } } } void TemplateSimplifier::syntaxError(const Token *tok) { throw InternalError(tok, "syntax error", InternalError::SYNTAX); } cppcheck-1.90/lib/templatesimplifier.h000066400000000000000000000434351357737443600200750ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef templatesimplifierH #define templatesimplifierH //--------------------------------------------------------------------------- #include "config.h" #include #include #include #include #include #include #include class ErrorLogger; class Settings; class Token; class Tokenizer; class TokenList; /// @addtogroup Core /// @{ /** @brief Simplify templates from the preprocessed and partially simplified code. */ class CPPCHECKLIB TemplateSimplifier { public: explicit TemplateSimplifier(Tokenizer *tokenizer); ~TemplateSimplifier(); /** * Used after simplifyTemplates to perform a little cleanup. * Sometimes the simplifyTemplates isn't fully successful and then * there are function calls etc with "wrong" syntax. */ void cleanupAfterSimplify(); /** */ void checkComplicatedSyntaxErrorsInTemplates(); /** * is the token pointing at a template parameters block * < int , 3 > => yes * \param tok start token that must point at "<" * \return number of parameters (invalid parameters => 0) */ static unsigned int templateParameters(const Token *tok); /** * Token and its full scopename */ class TokenAndName { Token *mToken; std::string mScope; std::string mName; std::string mFullName; const Token *mNameToken; const Token *mParamEnd; unsigned int mFlags; enum { fIsClass = (1 << 0), // class template fIsFunction = (1 << 1), // function template fIsVariable = (1 << 2), // variable template fIsAlias = (1 << 3), // alias template fIsSpecialization = (1 << 4), // user specialized template fIsPartialSpecialization = (1 << 5), // user partial specialized template fIsForwardDeclaration = (1 << 6), // forward declaration fIsVariadic = (1 << 7), // variadic template fIsFriend = (1 << 8), // friend template fFamilyMask = (fIsClass | fIsFunction | fIsVariable) }; void isClass(bool state) { setFlag(fIsClass, state); } void isFunction(bool state) { setFlag(fIsFunction, state); } void isVariable(bool state) { setFlag(fIsVariable, state); } void isAlias(bool state) { setFlag(fIsAlias, state); } void isSpecialization(bool state) { setFlag(fIsSpecialization, state); } void isPartialSpecialization(bool state) { setFlag(fIsPartialSpecialization, state); } void isForwardDeclaration(bool state) { setFlag(fIsForwardDeclaration, state); } void isVariadic(bool state) { setFlag(fIsVariadic, state); } void isFriend(bool state) { setFlag(fIsFriend, state); } /** * Get specified flag state. * @param flag flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(unsigned int flag) const { return ((mFlags & flag) != 0); } /** * Set specified flag state. * @param flag flag to set state * @param state new state of flag */ void setFlag(unsigned int flag, bool state) { mFlags = state ? mFlags | flag : mFlags & ~flag; } public: /** * Constructor used for instantiations. * \param tok template instantiation name token "name<...>" * \param s full qualification of template(scope) */ TokenAndName(Token *token, const std::string &scope); /** * Constructor used for declarations. * \param tok template declaration token "template < ... >" * \param s full qualification of template(scope) * \param nt template name token "template < ... > class name" * \param pe template parameter end token ">" */ TokenAndName(Token *token, const std::string &scope, const Token *nameToken, const Token *paramEnd); TokenAndName(const TokenAndName& other); ~TokenAndName(); bool operator == (const TokenAndName & rhs) const { return mToken == rhs.mToken && mScope == rhs.mScope && mName == rhs.mName && mFullName == rhs.mFullName && mNameToken == rhs.mNameToken && mParamEnd == rhs.mParamEnd && mFlags == rhs.mFlags; } Token * token() const { return mToken; } void token(Token * token) { mToken = token; } const std::string & scope() const { return mScope; } const std::string & name() const { return mName; } const std::string & fullName() const { return mFullName; } const Token * nameToken() const { return mNameToken; } const Token * paramEnd() const { return mParamEnd; } void paramEnd(const Token *end) { mParamEnd = end; } bool isClass() const { return getFlag(fIsClass); } bool isFunction() const { return getFlag(fIsFunction); } bool isVariable() const { return getFlag(fIsVariable); } bool isAlias() const { return getFlag(fIsAlias); } bool isSpecialization() const { return getFlag(fIsSpecialization); } bool isPartialSpecialization() const { return getFlag(fIsPartialSpecialization); } bool isForwardDeclaration() const { return getFlag(fIsForwardDeclaration); } bool isVariadic() const { return getFlag(fIsVariadic); } bool isFriend() const { return getFlag(fIsFriend); } /** * Get alias start token. * template < ... > using X = foo < ... >; * ^ * @return alias start token */ const Token * aliasStartToken() const; /** * Get alias end token. * template < ... > using X = foo < ... >; * ^ * @return alias end token */ const Token * aliasEndToken() const; /** * Is token an alias token? * template < ... > using X = foo < ... >; * ^ * @param tok token to check * @return true if alias token, false if not */ bool isAliasToken(const Token *tok) const; /** * Is declaration the same family (class, function or variable). * * @param decl declaration to compare to * @return true if same family, false if different family */ bool isSameFamily(const TemplateSimplifier::TokenAndName &decl) const { // Make sure a family flag is set and matches. // This works because at most only one flag will be set. return ((mFlags & fFamilyMask) & (decl.mFlags & fFamilyMask)) != 0; } }; /** * Find last token of a template declaration. * @param tok start token of declaration "template" or token after "template < ... >" * @return last token of declaration or nullptr if syntax error */ static Token *findTemplateDeclarationEnd(Token *tok); static const Token *findTemplateDeclarationEnd(const Token *tok); /** * Match template declaration/instantiation * @param instance template instantiation * @param numberOfArguments number of template arguments * @param patternAfter pattern that must match the tokens after the ">" * @return match => true */ static bool instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[]); /** * Match template declaration/instantiation * @param tok The ">" token e.g. before "class" * @return -1 to bail out or positive integer to identity the position * of the template name. */ int getTemplateNamePosition(const Token *tok); /** * Get class template name position * @param tok The ">" token e.g. before "class" * @param namepos return offset to name * @return true if name found, false if not * */ static bool getTemplateNamePositionTemplateClass(const Token *tok, int &namepos); /** * Get function template name position * @param tok The ">" token * @param namepos return offset to name * @return true if name found, false if not * */ static bool getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos); /** * Get variable template name position * @param tok The ">" token * @param namepos return offset to name * @return true if name found, false if not * */ static bool getTemplateNamePositionTemplateVariable(const Token *tok, int &namepos); /** * Simplify templates * @param maxtime time when the simplification should be stopped * @param codeWithTemplates output parameter that is set if code contains templates */ void simplifyTemplates( const std::time_t maxtime, bool &codeWithTemplates); /** * Simplify constant calculations such as "1+2" => "3" * @param tok start token * @return true if modifications to token-list are done. * false if no modifications are done. */ static bool simplifyNumericCalculations(Token *tok, bool isTemplate = true); /** * Simplify constant calculations such as "1+2" => "3". * This also performs simple cleanup of parentheses etc. * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifyCalculations(Token* frontToken = nullptr, Token *backToken = nullptr, bool isTemplate = true); /** Simplify template instantiation arguments. * @param start first token of arguments * @param end token following last argument token */ void simplifyTemplateArgs(Token *start, Token *end); /** Fix angle brackets. * foo < bar < >> => foo < bar < > > */ void fixAngleBrackets(); private: /** * Get template declarations * @return true if code has templates. */ bool getTemplateDeclarations(); /** Add template instantiation. * @param token first token of instantiation * @param scope scope of instantiation */ void addInstantiation(Token *token, const std::string &scope); /** * Get template instantiations */ void getTemplateInstantiations(); /** * Fix forward declared default argument values by copying them * when they are not present in the declaration. */ void fixForwardDeclaredDefaultArgumentValues(); /** * simplify template instantiations (use default argument values) */ void useDefaultArgumentValues(); /** * simplify template instantiations (use default argument values) * @param declaration template declaration or forward declaration */ void useDefaultArgumentValues(TokenAndName &declaration); /** * Try to locate a matching declaration for each user defined * specialization. */ void getSpecializations(); /** * Try to locate a matching declaration for each user defined * partial specialization. */ void getPartialSpecializations(); /** * simplify template aliases */ void simplifyTemplateAliases(); /** * Simplify templates : expand all instantiations for a template * @todo It seems that inner templates should be instantiated recursively * @param templateDeclaration template declaration * @param specializations template specializations (list each template name token) * @param maxtime time when the simplification will stop * @param expandedtemplates all templates that has been expanded so far. The full names are stored. * @return true if the template was instantiated */ bool simplifyTemplateInstantiations( const TokenAndName &templateDeclaration, const std::list &specializations, const std::time_t maxtime, std::set &expandedtemplates); /** * Simplify templates : add namespace to template name * @param templateDeclaration template declaration * @param tok place to insert namespace */ void addNamespace(const TokenAndName &templateDeclaration, const Token *tok); /** * Simplify templates : check if namespace already present * @param templateDeclaration template declaration * @param tok place to start looking for namespace * @return true if namespace already present */ static bool alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok); /** * Expand a template. Create "expanded" class/function at end of tokenlist. * @param templateDeclaration Template declaration information * @param templateInstantiation Full name of template * @param typeParametersInDeclaration The type parameters of the template * @param newName New name of class/function. * @param copy copy or expand in place */ void expandTemplate( const TokenAndName &templateDeclaration, const TokenAndName &templateInstantiation, const std::vector &typeParametersInDeclaration, const std::string &newName, bool copy); /** * Replace all matching template usages 'Foo < int >' => 'Foo' * @param instantiation Template instantiation information. * @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings. * @param newName The new type name */ void replaceTemplateUsage(const TokenAndName &instantiation, const std::list &typeStringsUsedInTemplateInstantiation, const std::string &newName); /** * @brief TemplateParametersInDeclaration * @param tok template < typename T, typename S > * ^ tok * @param typeParametersInDeclaration template < typename T, typename S > * ^ [0] ^ [1] * @return template < typename T, typename S > * ^ return */ static const Token * getTemplateParametersInDeclaration( const Token * tok, std::vector & typeParametersInDeclaration); /** * Remove a specific "template < ..." template class/function */ static bool removeTemplate(Token *tok); /** Syntax error */ static void syntaxError(const Token *tok); static bool matchSpecialization( const Token *templateDeclarationNameToken, const Token *templateInstantiationNameToken, const std::list & specializations); /* * Same as Token::eraseTokens() but tries to fix up lists with pointers to the deleted tokens. * @param begin Tokens after this will be erased. * @param end Tokens before this will be erased. */ static void eraseTokens(Token *begin, const Token *end); /** * Delete specified token without invalidating pointer to following token. * tok will be invalidated. * @param tok token to delete */ static void deleteToken(Token *tok); /** * Get the new token name. * @param tok2 name token * @param typeStringsUsedInTemplateInstantiation type strings use in template instantiation * @return new token name */ std::string getNewName( Token *tok2, std::list &typeStringsUsedInTemplateInstantiation); void printOut( const TokenAndName &tokenAndName, const std::string &indent = " ") const; void printOut(const std::string &text = "") const; Tokenizer *mTokenizer; TokenList &mTokenList; const Settings *mSettings; ErrorLogger *mErrorLogger; bool mChanged; std::list mTemplateDeclarations; std::list mTemplateForwardDeclarations; std::map mTemplateForwardDeclarationsMap; std::map mTemplateSpecializationMap; std::map mTemplatePartialSpecializationMap; std::list mTemplateInstantiations; std::list mInstantiatedTemplates; std::list mMemberFunctionsToDelete; std::vector mExplicitInstantiationsToDelete; std::vector mTypesUsedInTemplateInstantiation; std::unordered_map mTemplateNamePos; }; /// @} //--------------------------------------------------------------------------- #endif // templatesimplifierH cppcheck-1.90/lib/timer.cpp000066400000000000000000000065171357737443600156510ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "timer.h" #include #include #include #include /* TODO: - rename "file" to "single" - synchronise map access in multithreaded mode or disable timing - add unit tests - for --showtime (needs input file) - for Timer* classes */ namespace { typedef std::pair dataElementType; bool more_second_sec(const dataElementType& lhs, const dataElementType& rhs) { return lhs.second.seconds() > rhs.second.seconds(); } } void TimerResults::showResults(SHOWTIME_MODES mode) const { if (mode == SHOWTIME_MODES::SHOWTIME_NONE) return; std::cout << std::endl; TimerResultsData overallData; std::vector data(mResults.begin(), mResults.end()); std::sort(data.begin(), data.end(), more_second_sec); size_t ordinal = 1; // maybe it would be nice to have an ordinal in output later! for (std::vector::const_iterator iter=data.begin() ; iter!=data.end(); ++iter) { const double sec = iter->second.seconds(); const double secAverage = sec / (double)(iter->second.mNumberOfResults); overallData.mClocks += iter->second.mClocks; if ((mode != SHOWTIME_MODES::SHOWTIME_TOP5) || (ordinal<=5)) { std::cout << iter->first << ": " << sec << "s (avg. " << secAverage << "s - " << iter->second.mNumberOfResults << " result(s))" << std::endl; } ++ordinal; } const double secOverall = overallData.seconds(); std::cout << "Overall time: " << secOverall << "s" << std::endl; } void TimerResults::addResults(const std::string& str, std::clock_t clocks) { mResults[str].mClocks += clocks; mResults[str].mNumberOfResults++; } Timer::Timer(const std::string& str, SHOWTIME_MODES showtimeMode, TimerResultsIntf* timerResults) : mStr(str) , mTimerResults(timerResults) , mStart(0) , mShowTimeMode(showtimeMode) , mStopped(false) { if (showtimeMode != SHOWTIME_MODES::SHOWTIME_NONE) mStart = std::clock(); } Timer::~Timer() { stop(); } void Timer::stop() { if ((mShowTimeMode != SHOWTIME_MODES::SHOWTIME_NONE) && !mStopped) { const std::clock_t end = std::clock(); const std::clock_t diff = end - mStart; if (mShowTimeMode == SHOWTIME_MODES::SHOWTIME_FILE) { const double sec = (double)diff / CLOCKS_PER_SEC; std::cout << mStr << ": " << sec << "s" << std::endl; } else { if (mTimerResults) mTimerResults->addResults(mStr, diff); } } mStopped = true; } cppcheck-1.90/lib/timer.h000066400000000000000000000045751357737443600153200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef timerH #define timerH //--------------------------------------------------------------------------- #include "config.h" #include #include #include enum class SHOWTIME_MODES { SHOWTIME_NONE = 0, SHOWTIME_FILE, SHOWTIME_SUMMARY, SHOWTIME_TOP5 }; class CPPCHECKLIB TimerResultsIntf { public: virtual ~TimerResultsIntf() { } virtual void addResults(const std::string& str, std::clock_t clocks) = 0; }; struct TimerResultsData { std::clock_t mClocks; long mNumberOfResults; TimerResultsData() : mClocks(0) , mNumberOfResults(0) { } double seconds() const { const double ret = (double)((unsigned long)mClocks) / (double)CLOCKS_PER_SEC; return ret; } }; class CPPCHECKLIB TimerResults : public TimerResultsIntf { public: TimerResults() { } void showResults(SHOWTIME_MODES mode) const; void addResults(const std::string& str, std::clock_t clocks) OVERRIDE; private: std::map mResults; }; class CPPCHECKLIB Timer { public: Timer(const std::string& str, SHOWTIME_MODES showtimeMode, TimerResultsIntf* timerResults = nullptr); ~Timer(); void stop(); private: Timer(const Timer& other); // disallow copying Timer& operator=(const Timer&); // disallow assignments const std::string mStr; TimerResultsIntf* mTimerResults; std::clock_t mStart; const SHOWTIME_MODES mShowTimeMode; bool mStopped; }; //--------------------------------------------------------------------------- #endif // timerH cppcheck-1.90/lib/token.cpp000066400000000000000000002040351357737443600156440ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "token.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "utils.h" #include #include #include #include #include #include #include #include const std::list TokenImpl::mEmptyValueList; Token::Token(TokensFrontBack *tokensFrontBack) : mTokensFrontBack(tokensFrontBack), mNext(nullptr), mPrevious(nullptr), mLink(nullptr), mTokType(eNone), mFlags(0) { mImpl = new TokenImpl(); } Token::~Token() { delete mImpl; } static const std::set controlFlowKeywords = { "goto", "do", "if", "else", "for", "while", "switch", "case", "break", "continue", "return" }; void Token::update_property_info() { setFlag(fIsControlFlowKeyword, controlFlowKeywords.find(mStr) != controlFlowKeywords.end()); if (!mStr.empty()) { if (mStr == "true" || mStr == "false") tokType(eBoolean); else if (isStringLiteral(mStr)) tokType(eString); else if (isCharLiteral(mStr)) tokType(eChar); else if (std::isalpha((unsigned char)mStr[0]) || mStr[0] == '_' || mStr[0] == '$') { // Name if (mImpl->mVarId) tokType(eVariable); else if (mTokType != eVariable && mTokType != eFunction && mTokType != eType && mTokType != eKeyword) tokType(eName); } else if (std::isdigit((unsigned char)mStr[0]) || (mStr.length() > 1 && mStr[0] == '-' && std::isdigit((unsigned char)mStr[1]))) tokType(eNumber); else if (mStr == "=" || mStr == "<<=" || mStr == ">>=" || (mStr.size() == 2U && mStr[1] == '=' && std::strchr("+-*/%&^|", mStr[0]))) tokType(eAssignmentOp); else if (mStr.size() == 1 && mStr.find_first_of(",[]()?:") != std::string::npos) tokType(eExtendedOp); else if (mStr=="<<" || mStr==">>" || (mStr.size()==1 && mStr.find_first_of("+-*/%") != std::string::npos)) tokType(eArithmeticalOp); else if (mStr.size() == 1 && mStr.find_first_of("&|^~") != std::string::npos) tokType(eBitOp); else if (mStr.size() <= 2 && (mStr == "&&" || mStr == "||" || mStr == "!")) tokType(eLogicalOp); else if (mStr.size() <= 2 && !mLink && (mStr == "==" || mStr == "!=" || mStr == "<" || mStr == "<=" || mStr == ">" || mStr == ">=")) tokType(eComparisonOp); else if (mStr.size() == 2 && (mStr == "++" || mStr == "--")) tokType(eIncDecOp); else if (mStr.size() == 1 && (mStr.find_first_of("{}") != std::string::npos || (mLink && mStr.find_first_of("<>") != std::string::npos))) tokType(eBracket); else if (mStr == "...") tokType(eEllipsis); else tokType(eOther); } else { tokType(eNone); } update_property_char_string_literal(); update_property_isStandardType(); } static const std::set stdTypes = { "bool" , "_Bool" , "char" , "double" , "float" , "int" , "long" , "short" , "size_t" , "void" , "wchar_t" }; void Token::update_property_isStandardType() { isStandardType(false); if (mStr.size() < 3) return; if (stdTypes.find(mStr)!=stdTypes.end()) { isStandardType(true); tokType(eType); } } void Token::update_property_char_string_literal() { if (mTokType != Token::eString && mTokType != Token::eChar) return; isLong(((mTokType == Token::eString) && isPrefixStringCharLiteral(mStr, '"', "L")) || ((mTokType == Token::eChar) && isPrefixStringCharLiteral(mStr, '\'', "L"))); } bool Token::isUpperCaseName() const { if (!isName()) return false; for (char i : mStr) { if (std::islower(i)) return false; } return true; } void Token::concatStr(std::string const& b) { mStr.erase(mStr.length() - 1); mStr.append(getStringLiteral(b) + "\""); if (isCChar() && isStringLiteral(b) && b[0] != '"') { mStr.insert(0, b.substr(0, b.find('"'))); } update_property_info(); } std::string Token::strValue() const { assert(mTokType == eString); std::string ret(getStringLiteral(mStr)); std::string::size_type pos = 0U; while ((pos = ret.find('\\', pos)) != std::string::npos) { ret.erase(pos,1U); if (ret[pos] >= 'a') { if (ret[pos] == 'n') ret[pos] = '\n'; else if (ret[pos] == 'r') ret[pos] = '\r'; else if (ret[pos] == 't') ret[pos] = '\t'; } if (ret[pos] == '0') return ret.substr(0,pos); pos++; } return ret; } void Token::deleteNext(nonneg int count) { while (mNext && count > 0) { Token *n = mNext; // #8154 we are about to be unknown -> destroy the link to us if (n->mLink && n->mLink->mLink == n) n->mLink->link(nullptr); mNext = n->next(); delete n; --count; } if (mNext) mNext->previous(this); else if (mTokensFrontBack) mTokensFrontBack->back = this; } void Token::deletePrevious(nonneg int count) { while (mPrevious && count > 0) { Token *p = mPrevious; // #8154 we are about to be unknown -> destroy the link to us if (p->mLink && p->mLink->mLink == p) p->mLink->link(nullptr); mPrevious = p->previous(); delete p; --count; } if (mPrevious) mPrevious->next(this); else if (mTokensFrontBack) mTokensFrontBack->front = this; } void Token::swapWithNext() { if (mNext) { std::swap(mStr, mNext->mStr); std::swap(mTokType, mNext->mTokType); std::swap(mFlags, mNext->mFlags); std::swap(mImpl, mNext->mImpl); for (auto templateSimplifierPointer : mImpl->mTemplateSimplifierPointers) { templateSimplifierPointer->token(this); } for (auto templateSimplifierPointer : mNext->mImpl->mTemplateSimplifierPointers) { templateSimplifierPointer->token(mNext); } if (mNext->mLink) mNext->mLink->mLink = this; if (this->mLink) this->mLink->mLink = mNext; std::swap(mLink, mNext->mLink); } } void Token::takeData(Token *fromToken) { mStr = fromToken->mStr; tokType(fromToken->mTokType); mFlags = fromToken->mFlags; delete mImpl; mImpl = fromToken->mImpl; fromToken->mImpl = nullptr; for (auto templateSimplifierPointer : mImpl->mTemplateSimplifierPointers) { templateSimplifierPointer->token(this); } mLink = fromToken->mLink; if (mLink) mLink->link(this); } void Token::deleteThis() { if (mNext) { // Copy next to this and delete next takeData(mNext); mNext->link(nullptr); // mark as unlinked deleteNext(); } else if (mPrevious && mPrevious->mPrevious) { // Copy previous to this and delete previous takeData(mPrevious); Token* toDelete = mPrevious; mPrevious = mPrevious->mPrevious; mPrevious->mNext = this; delete toDelete; } else { // We are the last token in the list, we can't delete // ourselves, so just make us empty str(""); } } void Token::replace(Token *replaceThis, Token *start, Token *end) { // Fix the whole in the old location of start and end if (start->previous()) start->previous()->next(end->next()); if (end->next()) end->next()->previous(start->previous()); // Move start and end to their new location if (replaceThis->previous()) replaceThis->previous()->next(start); if (replaceThis->next()) replaceThis->next()->previous(end); start->previous(replaceThis->previous()); end->next(replaceThis->next()); if (end->mTokensFrontBack && end->mTokensFrontBack->back == end) { while (end->next()) end = end->next(); end->mTokensFrontBack->back = end; } // Update mProgressValue, fileIndex and linenr for (Token *tok = start; tok != end->next(); tok = tok->next()) tok->mImpl->mProgressValue = replaceThis->mImpl->mProgressValue; // Delete old token, which is replaced delete replaceThis; } const Token *Token::tokAt(int index) const { const Token *tok = this; while (index > 0 && tok) { tok = tok->next(); --index; } while (index < 0 && tok) { tok = tok->previous(); ++index; } return tok; } const Token *Token::linkAt(int index) const { const Token *tok = this->tokAt(index); if (!tok) { throw InternalError(this, "Internal error. Token::linkAt called with index outside the tokens range."); } return tok->link(); } const std::string &Token::strAt(int index) const { const Token *tok = this->tokAt(index); return tok ? tok->mStr : emptyString; } static int multiComparePercent(const Token *tok, const char*& haystack, nonneg int varid) { ++haystack; // Compare only the first character of the string for optimization reasons switch (haystack[0]) { case '\0': case ' ': case '|': //simple '%' character haystack += 1; if (tok->isArithmeticalOp() && tok->str() == "%") return 1; break; case 'v': if (haystack[3] == '%') { // %var% haystack += 4; if (tok->varId() != 0) return 1; } else { // %varid% if (varid == 0) { throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); } haystack += 6; if (tok->varId() == varid) return 1; } break; case 't': // Type (%type%) { haystack += 5; if (tok->isName() && tok->varId() == 0 && !tok->isKeyword()) return 1; } break; case 'a': // Accept any token (%any%) or assign (%assign%) { if (haystack[3] == '%') { // %any% haystack += 4; return 1; } else { // %assign% haystack += 7; if (tok->isAssignmentOp()) return 1; } } break; case 'n': // Number (%num%) or name (%name%) { if (haystack[4] == '%') { // %name% haystack += 5; if (tok->isName()) return 1; } else { haystack += 4; if (tok->isNumber()) return 1; } } break; case 'c': { haystack += 1; // Character (%char%) if (haystack[0] == 'h') { haystack += 4; if (tok->tokType() == Token::eChar) return 1; } // Const operator (%cop%) else if (haystack[1] == 'p') { haystack += 3; if (tok->isConstOp()) return 1; } // Comparison operator (%comp%) else { haystack += 4; if (tok->isComparisonOp()) return 1; } } break; case 's': // String (%str%) { haystack += 4; if (tok->tokType() == Token::eString) return 1; } break; case 'b': // Bool (%bool%) { haystack += 5; if (tok->isBoolean()) return 1; } break; case 'o': { ++haystack; if (haystack[1] == '%') { // Op (%op%) if (haystack[0] == 'p') { haystack += 2; if (tok->isOp()) return 1; } // Or (%or%) else { haystack += 2; if (tok->tokType() == Token::eBitOp && tok->str() == "|") return 1; } } // Oror (%oror%) else { haystack += 4; if (tok->tokType() == Token::eLogicalOp && tok->str() == "||") return 1; } } break; default: //unknown %cmd%, abort throw InternalError(tok, "Unexpected command"); } if (*haystack == '|') haystack += 1; else return -1; return 0xFFFF; } int Token::multiCompare(const Token *tok, const char *haystack, nonneg int varid) { const char *needle = tok->str().c_str(); const char *needlePointer = needle; for (;;) { if (needlePointer == needle && haystack[0] == '%' && haystack[1] != '|' && haystack[1] != '\0' && haystack[1] != ' ') { const int ret = multiComparePercent(tok, haystack, varid); if (ret < 2) return ret; } else if (*haystack == '|') { if (*needlePointer == 0) { // If needle is at the end, we have a match. return 1; } needlePointer = needle; ++haystack; } else if (*needlePointer == *haystack) { if (*needlePointer == '\0') return 1; ++needlePointer; ++haystack; } else if (*haystack == ' ' || *haystack == '\0') { if (needlePointer == needle) return 0; break; } // If haystack and needle don't share the same character, // find next '|' character. else { needlePointer = needle; do { ++haystack; } while (*haystack != ' ' && *haystack != '|' && *haystack); if (*haystack == ' ' || *haystack == '\0') { return -1; } ++haystack; } } if (*needlePointer == '\0') return 1; return -1; } bool Token::simpleMatch(const Token *tok, const char pattern[]) { if (!tok) return false; // shortcut const char *current = pattern; const char *next = std::strchr(pattern, ' '); if (!next) next = pattern + std::strlen(pattern); while (*current) { const std::size_t length = next - current; if (!tok || length != tok->mStr.length() || std::strncmp(current, tok->mStr.c_str(), length)) return false; current = next; if (*next) { next = std::strchr(++current, ' '); if (!next) next = current + std::strlen(current); } tok = tok->next(); } return true; } bool Token::firstWordEquals(const char *str, const char *word) { for (;;) { if (*str != *word) { return (*str == ' ' && *word == 0); } else if (*str == 0) break; ++str; ++word; } return true; } const char *Token::chrInFirstWord(const char *str, char c) { for (;;) { if (*str == ' ' || *str == 0) return nullptr; if (*str == c) return str; ++str; } } bool Token::Match(const Token *tok, const char pattern[], nonneg int varid) { const char *p = pattern; while (*p) { // Skip spaces in pattern.. while (*p == ' ') ++p; // No token => Success! if (*p == '\0') break; if (!tok) { // If we have no tokens, pattern "!!else" should return true if (p[0] == '!' && p[1] == '!' && p[2] != '\0') { while (*p && *p != ' ') ++p; continue; } else return false; } // [.. => search for a one-character token.. if (p[0] == '[' && chrInFirstWord(p, ']')) { if (tok->str().length() != 1) return false; const char *temp = p+1; bool chrFound = false; int count = 0; while (*temp && *temp != ' ') { if (*temp == ']') { ++count; } else if (*temp == tok->str()[0]) { chrFound = true; break; } ++temp; } if (count > 1 && tok->str()[0] == ']') chrFound = true; if (!chrFound) return false; p = temp; while (*p && *p != ' ') ++p; } // Parse "not" options. Token can be anything except the given one else if (p[0] == '!' && p[1] == '!' && p[2] != '\0') { p += 2; if (firstWordEquals(p, tok->str().c_str())) return false; while (*p && *p != ' ') ++p; } // Parse multi options, such as void|int|char (accept token which is one of these 3) else { const int res = multiCompare(tok, p, varid); if (res == 0) { // Empty alternative matches, use the same token on next round while (*p && *p != ' ') ++p; continue; } else if (res == -1) { // No match return false; } } while (*p && *p != ' ') ++p; tok = tok->next(); } // The end of the pattern has been reached and nothing wrong has been found return true; } nonneg int Token::getStrLength(const Token *tok) { assert(tok != nullptr); assert(tok->mTokType == eString); int len = 0; const std::string str(getStringLiteral(tok->str())); std::string::const_iterator it = str.begin(); const std::string::const_iterator end = str.end(); while (it != end) { if (*it == '\\') { ++it; // string ends at '\0' if (*it == '0') return len; } if (*it == '\0') return len; ++it; ++len; } return len; } nonneg int Token::getStrArraySize(const Token *tok) { assert(tok != nullptr); assert(tok->tokType() == eString); const std::string str(getStringLiteral(tok->str())); int sizeofstring = 1; for (int i = 0; i < (int)str.size(); i++) { if (str[i] == '\\') ++i; ++sizeofstring; } return sizeofstring; } nonneg int Token::getStrSize(const Token *tok, const Settings *settings) { assert(tok != nullptr && tok->tokType() == eString); nonneg int sizeofType = 1; if (tok->valueType()) { ValueType vt(*tok->valueType()); vt.pointer = 0; sizeofType = ValueFlow::getSizeOf(vt, settings); } return getStrArraySize(tok) * sizeofType; } std::string Token::getCharAt(const Token *tok, MathLib::bigint index) { assert(tok != nullptr); std::string str(getStringLiteral(tok->str())); std::string::const_iterator it = str.begin(); const std::string::const_iterator end = str.end(); while (it != end) { if (index == 0) { if (*it == '\0') return "\\0"; std::string ret(1, *it); if (*it == '\\') { ++it; ret += *it; } return ret; } if (*it == '\\') ++it; ++it; --index; } assert(index == 0); return "\\0"; } void Token::move(Token *srcStart, Token *srcEnd, Token *newLocation) { /**[newLocation] -> b -> c -> [srcStart] -> [srcEnd] -> f */ // Fix the gap, which tokens to be moved will leave srcStart->previous()->next(srcEnd->next()); srcEnd->next()->previous(srcStart->previous()); // Fix the tokens to be moved srcEnd->next(newLocation->next()); srcStart->previous(newLocation); // Fix the tokens at newLocation newLocation->next()->previous(srcEnd); newLocation->next(srcStart); // Update _progressValue for (Token *tok = srcStart; tok != srcEnd->next(); tok = tok->next()) tok->mImpl->mProgressValue = newLocation->mImpl->mProgressValue; } Token* Token::nextArgument() const { for (const Token* tok = this; tok; tok = tok->next()) { if (tok->str() == ",") return tok->next(); else if (tok->link() && Token::Match(tok, "(|{|[|<")) tok = tok->link(); else if (Token::Match(tok, ")|;")) return nullptr; } return nullptr; } Token* Token::nextArgumentBeforeCreateLinks2() const { for (const Token* tok = this; tok; tok = tok->next()) { if (tok->str() == ",") return tok->next(); else if (tok->link() && Token::Match(tok, "(|{|[")) tok = tok->link(); else if (tok->str() == "<") { const Token* temp = tok->findClosingBracket(); if (temp) tok = temp; } else if (Token::Match(tok, ")|;")) return nullptr; } return nullptr; } Token* Token::nextTemplateArgument() const { for (const Token* tok = this; tok; tok = tok->next()) { if (tok->str() == ",") return tok->next(); else if (tok->link() && Token::Match(tok, "(|{|[|<")) tok = tok->link(); else if (Token::Match(tok, ">|;")) return nullptr; } return nullptr; } static bool isOperator(const Token *tok) { if (tok->link()) tok = tok->link(); // TODO handle multi token operators return tok->strAt(-1) == "operator"; } const Token * Token::findClosingBracket() const { if (mStr != "<") return nullptr; const Token *closing = nullptr; const bool templateParameter(strAt(-1) == "template"); std::set templateParameters; unsigned int depth = 0; for (closing = this; closing != nullptr; closing = closing->next()) { if (Token::Match(closing, "{|[|(")) { closing = closing->link(); if (!closing) return nullptr; // #6803 } else if (Token::Match(closing, "}|]|)|;")) return nullptr; // we can make some guesses for template parameters else if (closing->str() == "<" && closing->previous() && (closing->previous()->isName() || isOperator(closing->previous())) && (templateParameter ? templateParameters.find(closing->strAt(-1)) == templateParameters.end() : true)) ++depth; else if (closing->str() == ">") { if (--depth == 0) return closing; } else if (closing->str() == ">>") { if (depth <= 2) return closing; depth -= 2; } // save named template parameter else if (templateParameter && depth == 1 && closing->str() == "," && closing->previous()->isName() && !Match(closing->previous(), "class|typename|.")) templateParameters.insert(closing->strAt(-1)); } return closing; } Token * Token::findClosingBracket() { // return value of const function return const_cast(const_cast(this)->findClosingBracket()); } const Token * Token::findOpeningBracket() const { if (mStr != ">") return nullptr; const Token *opening = nullptr; unsigned int depth = 0; for (opening = this; opening != nullptr; opening = opening->previous()) { if (Token::Match(opening, "}|]|)")) { opening = opening->link(); if (!opening) return nullptr; } else if (Token::Match(opening, "{|{|(|;")) return nullptr; else if (opening->str() == ">") ++depth; else if (opening->str() == "<") { if (--depth == 0) return opening; } } return opening; } Token * Token::findOpeningBracket() { // return value of const function return const_cast(const_cast(this)->findOpeningBracket()); } //--------------------------------------------------------------------------- const Token *Token::findsimplematch(const Token * const startTok, const char pattern[]) { for (const Token* tok = startTok; tok; tok = tok->next()) { if (Token::simpleMatch(tok, pattern)) return tok; } return nullptr; } const Token *Token::findsimplematch(const Token * const startTok, const char pattern[], const Token * const end) { for (const Token* tok = startTok; tok && tok != end; tok = tok->next()) { if (Token::simpleMatch(tok, pattern)) return tok; } return nullptr; } const Token *Token::findmatch(const Token * const startTok, const char pattern[], const nonneg int varId) { for (const Token* tok = startTok; tok; tok = tok->next()) { if (Token::Match(tok, pattern, varId)) return tok; } return nullptr; } const Token *Token::findmatch(const Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId) { for (const Token* tok = startTok; tok && tok != end; tok = tok->next()) { if (Token::Match(tok, pattern, varId)) return tok; } return nullptr; } void Token::function(const Function *f) { mImpl->mFunction = f; if (f) { if (f->isLambda()) tokType(eLambda); else tokType(eFunction); } else if (mTokType == eFunction) tokType(eName); } void Token::insertToken(const std::string &tokenStr, const std::string &originalNameStr, bool prepend) { Token *newToken; if (mStr.empty()) newToken = this; else newToken = new Token(mTokensFrontBack); newToken->str(tokenStr); if (!originalNameStr.empty()) newToken->originalName(originalNameStr); if (newToken != this) { newToken->mImpl->mLineNumber = mImpl->mLineNumber; newToken->mImpl->mFileIndex = mImpl->mFileIndex; newToken->mImpl->mProgressValue = mImpl->mProgressValue; if (prepend) { if (this->previous()) { newToken->previous(this->previous()); newToken->previous()->next(newToken); } else if (mTokensFrontBack) { mTokensFrontBack->front = newToken; } this->previous(newToken); newToken->next(this); } else { if (this->next()) { newToken->next(this->next()); newToken->next()->previous(newToken); } else if (mTokensFrontBack) { mTokensFrontBack->back = newToken; } this->next(newToken); newToken->previous(this); } if (mImpl->mScopeInfo) { // If the brace is immediately closed there is no point opening a new scope for it if (tokenStr == "{") { std::string nextScopeNameAddition; // This might be the opening of a member function Token *tok1 = newToken; while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1 && tok1->previous() && tok1->strAt(-1) == ")") { tok1 = tok1->linkAt(-1); if (Token::Match(tok1->previous(), "throw|noexcept")) { tok1 = tok1->previous(); while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->strAt(-1) != ")") return; } else if (Token::Match(newToken->tokAt(-2), ":|, %name%")) { tok1 = tok1->tokAt(-2); if (tok1->strAt(-1) != ")") return; } if (tok1->strAt(-1) == ">") tok1 = tok1->previous()->findOpeningBracket(); if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) { tok1 = tok1->tokAt(-2); std::string scope = tok1->strAt(-1); while (Token::Match(tok1->tokAt(-2), ":: %name%")) { scope = tok1->strAt(-3) + " :: " + scope; tok1 = tok1->tokAt(-2); } if (!nextScopeNameAddition.empty() && !scope.empty()) nextScopeNameAddition += " :: "; nextScopeNameAddition += scope; } } // Or it might be a namespace/class/struct if (Token::Match(newToken->previous(), "%name%|>")) { Token* nameTok = newToken->previous(); while (nameTok && !Token::Match(nameTok, "namespace|class|struct|union %name% {|::|:|<")) { nameTok = nameTok->previous(); } if (nameTok) { for (nameTok = nameTok->next(); nameTok && !Token::Match(nameTok, "{|:|<"); nameTok = nameTok->next()) { nextScopeNameAddition.append(nameTok->str()); nextScopeNameAddition.append(" "); } if (nextScopeNameAddition.length() > 0) nextScopeNameAddition = nextScopeNameAddition.substr(0, nextScopeNameAddition.length() - 1); } } // New scope is opening, record it here std::shared_ptr newScopeInfo = std::make_shared(mImpl->mScopeInfo->name, nullptr, mImpl->mScopeInfo->usingNamespaces); if (!newScopeInfo->name.empty() && !nextScopeNameAddition.empty()) newScopeInfo->name.append(" :: "); newScopeInfo->name.append(nextScopeNameAddition); nextScopeNameAddition = ""; newToken->scopeInfo(newScopeInfo); } else if (tokenStr == "}") { Token* matchingTok = newToken->previous(); int depth = 0; while (matchingTok && (depth != 0 || !Token::simpleMatch(matchingTok, "{"))) { if (Token::simpleMatch(matchingTok, "}")) depth++; if (Token::simpleMatch(matchingTok, "{")) depth--; matchingTok = matchingTok->previous(); } if (matchingTok && matchingTok->previous()) { newToken->mImpl->mScopeInfo = matchingTok->previous()->scopeInfo(); } } else { if (prepend && newToken->previous()) { newToken->mImpl->mScopeInfo = newToken->previous()->scopeInfo(); } else { newToken->mImpl->mScopeInfo = mImpl->mScopeInfo; } if (tokenStr == ";") { const Token* statementStart; for (statementStart = newToken; statementStart->previous() && !Token::Match(statementStart->previous(), ";|{"); statementStart = statementStart->previous()); if (Token::Match(statementStart, "using namespace %name% ::|;")) { const Token * tok1 = statementStart->tokAt(2); std::string nameSpace; while (tok1 && tok1->str() != ";") { if (!nameSpace.empty()) nameSpace += " "; nameSpace += tok1->str(); tok1 = tok1->next(); } mImpl->mScopeInfo->usingNamespaces.insert(nameSpace); } } } } } } void Token::eraseTokens(Token *begin, const Token *end) { if (!begin || begin == end) return; while (begin->next() && begin->next() != end) { begin->deleteNext(); } } void Token::createMutualLinks(Token *begin, Token *end) { assert(begin != nullptr); assert(end != nullptr); assert(begin != end); begin->link(end); end->link(begin); } void Token::printOut(const char *title) const { if (title && title[0]) std::cout << "\n### " << title << " ###\n"; std::cout << stringifyList(true, true, true, true, true, nullptr, nullptr) << std::endl; } void Token::printOut(const char *title, const std::vector &fileNames) const { if (title && title[0]) std::cout << "\n### " << title << " ###\n"; std::cout << stringifyList(true, true, true, true, true, &fileNames, nullptr) << std::endl; } void Token::stringify(std::ostream& os, bool varid, bool attributes, bool macro) const { if (attributes) { if (isUnsigned()) os << "unsigned "; else if (isSigned()) os << "signed "; if (isComplex()) os << "_Complex "; if (isLong()) { if (!(mTokType == eString || mTokType == eChar)) os << "long "; } } if (macro && isExpandedMacro()) os << "$"; if (isName() && mStr.find(' ') != std::string::npos) { for (char i : mStr) { if (i != ' ') os << i; } } else if (mStr[0] != '\"' || mStr.find('\0') == std::string::npos) os << mStr; else { for (char i : mStr) { if (i == '\0') os << "\\0"; else os << i; } } if (varid && mImpl->mVarId != 0) os << '@' << mImpl->mVarId; } std::string Token::stringifyList(bool varid, bool attributes, bool linenumbers, bool linebreaks, bool files, const std::vector* fileNames, const Token* end) const { if (this == end) return ""; std::ostringstream ret; unsigned int lineNumber = mImpl->mLineNumber - (linenumbers ? 1U : 0U); unsigned int fileIndex = files ? ~0U : mImpl->mFileIndex; std::map lineNumbers; for (const Token *tok = this; tok != end; tok = tok->next()) { bool fileChange = false; if (tok->mImpl->mFileIndex != fileIndex) { if (fileIndex != ~0U) { lineNumbers[fileIndex] = tok->mImpl->mFileIndex; } fileIndex = tok->mImpl->mFileIndex; if (files) { ret << "\n\n##file "; if (fileNames && fileNames->size() > tok->mImpl->mFileIndex) ret << fileNames->at(tok->mImpl->mFileIndex); else ret << fileIndex; ret << '\n'; } lineNumber = lineNumbers[fileIndex]; fileChange = true; } if (linebreaks && (lineNumber != tok->linenr() || fileChange)) { if (lineNumber+4 < tok->linenr() && fileIndex == tok->mImpl->mFileIndex) { ret << '\n' << lineNumber+1 << ":\n|\n"; ret << tok->linenr()-1 << ":\n"; ret << tok->linenr() << ": "; } else if (this == tok && linenumbers) { ret << tok->linenr() << ": "; } else { while (lineNumber < tok->linenr()) { ++lineNumber; ret << '\n'; if (linenumbers) { ret << lineNumber << ':'; if (lineNumber == tok->linenr()) ret << ' '; } } } lineNumber = tok->linenr(); } tok->stringify(ret, varid, attributes, attributes); // print token if (tok->next() != end && (!linebreaks || (tok->next()->linenr() <= tok->linenr() && tok->next()->fileIndex() == tok->fileIndex()))) ret << ' '; } if (linebreaks && (files || linenumbers)) ret << '\n'; return ret.str(); } std::string Token::stringifyList(const Token* end, bool attributes) const { return stringifyList(false, attributes, false, false, false, nullptr, end); } std::string Token::stringifyList(bool varid) const { return stringifyList(varid, false, true, true, true, nullptr, nullptr); } void Token::astOperand1(Token *tok) { if (mImpl->mAstOperand1) mImpl->mAstOperand1->mImpl->mAstParent = nullptr; // goto parent operator if (tok) { std::set visitedParents; while (tok->mImpl->mAstParent) { if (!visitedParents.insert(tok->mImpl->mAstParent).second) // #6838/#6726/#8352 avoid hang on garbage code throw InternalError(this, "Internal error. Token::astOperand1() cyclic dependency."); tok = tok->mImpl->mAstParent; } tok->mImpl->mAstParent = this; } mImpl->mAstOperand1 = tok; } void Token::astOperand2(Token *tok) { if (mImpl->mAstOperand2) mImpl->mAstOperand2->mImpl->mAstParent = nullptr; // goto parent operator if (tok) { std::set visitedParents; while (tok->mImpl->mAstParent) { //std::cout << tok << " -> " << tok->mAstParent ; if (!visitedParents.insert(tok->mImpl->mAstParent).second) // #6838/#6726 avoid hang on garbage code throw InternalError(this, "Internal error. Token::astOperand2() cyclic dependency."); tok = tok->mImpl->mAstParent; } tok->mImpl->mAstParent = this; } mImpl->mAstOperand2 = tok; } static const Token* goToLeftParenthesis(const Token* start, const Token* end) { // move start to lpar in such expression: '(*it).x' int par = 0; for (const Token *tok = start; tok && tok != end; tok = tok->next()) { if (tok->str() == "(") ++par; else if (tok->str() == ")") { if (par == 0) start = tok->link(); else --par; } } return start; } static const Token* goToRightParenthesis(const Token* start, const Token* end) { // move end to rpar in such expression: '2>(x+1)' int par = 0; for (const Token *tok = end; tok && tok != start; tok = tok->previous()) { if (tok->str() == ")") ++par; else if (tok->str() == "(") { if (par == 0) end = tok->link(); else --par; } } return end; } std::pair Token::findExpressionStartEndTokens() const { const Token * const top = this; // find start node in AST tree const Token *start = top; while (start->astOperand1() && (start->astOperand2() || !start->isUnaryPreOp() || Token::simpleMatch(start, "( )") || start->str() == "{")) start = start->astOperand1(); // find end node in AST tree const Token *end = top; while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) { // lambda.. if (end->str() == "[") { const Token *lambdaEnd = findLambdaEndToken(end); if (lambdaEnd) { end = lambdaEnd; break; } } if (Token::Match(end,"(|[") && !(Token::Match(end, "( %type%") && !end->astOperand2())) { end = end->link(); break; } end = end->astOperand2() ? end->astOperand2() : end->astOperand1(); } // skip parentheses start = goToLeftParenthesis(start, end); end = goToRightParenthesis(start, end); if (Token::simpleMatch(end, "{")) end = end->link(); return std::pair(start,end); } bool Token::isCalculation() const { if (!Token::Match(this, "%cop%|++|--")) return false; if (Token::Match(this, "*|&")) { // dereference or address-of? if (!this->astOperand2()) return false; if (this->astOperand2()->str() == "[") return false; // type specification? std::stack operands; operands.push(this); while (!operands.empty()) { const Token *op = operands.top(); operands.pop(); if (op->isNumber() || op->varId() > 0) return true; if (op->astOperand1()) operands.push(op->astOperand1()); if (op->astOperand2()) operands.push(op->astOperand2()); else if (Token::Match(op, "*|&")) return false; } // type specification => return false return false; } return true; } bool Token::isUnaryPreOp() const { if (!astOperand1() || astOperand2()) return false; if (!Token::Match(this, "++|--")) return true; const Token *tokbefore = mPrevious; const Token *tokafter = mNext; for (int distance = 1; distance < 10 && tokbefore; distance++) { if (tokbefore == mImpl->mAstOperand1) return false; if (tokafter == mImpl->mAstOperand1) return true; tokbefore = tokbefore->mPrevious; tokafter = tokafter->mPrevious; } return false; // <- guess } static std::string stringFromTokenRange(const Token* start, const Token* end) { std::ostringstream ret; if (end) end = end->next(); for (const Token *tok = start; tok && tok != end; tok = tok->next()) { if (tok->isUnsigned()) ret << "unsigned "; if (tok->isLong() && !tok->isLiteral()) ret << "long "; if (tok->originalName().empty() || tok->isUnsigned() || tok->isLong()) { ret << tok->str(); } else ret << tok->originalName(); if (Token::Match(tok, "%name%|%num% %name%|%num%")) ret << ' '; } return ret.str(); } std::string Token::expressionString() const { const auto tokens = findExpressionStartEndTokens(); return stringFromTokenRange(tokens.first, tokens.second); } static void astStringXml(const Token *tok, nonneg int indent, std::ostream &out) { const std::string strindent(indent, ' '); out << strindent << "str() << '\"'; if (tok->varId() > 0U) out << " varId=\"" << MathLib::toString(tok->varId()) << '\"'; if (tok->variable()) out << " variable=\"" << tok->variable() << '\"'; if (tok->function()) out << " function=\"" << tok->function() << '\"'; if (!tok->values().empty()) out << " values=\"" << &tok->values() << '\"'; if (!tok->astOperand1() && !tok->astOperand2()) { out << "/>" << std::endl; } else { out << '>' << std::endl; if (tok->astOperand1()) astStringXml(tok->astOperand1(), indent+2U, out); if (tok->astOperand2()) astStringXml(tok->astOperand2(), indent+2U, out); out << strindent << "" << std::endl; } } void Token::printAst(bool verbose, bool xml, std::ostream &out) const { std::set printed; for (const Token *tok = this; tok; tok = tok->next()) { if (!tok->mImpl->mAstParent && tok->mImpl->mAstOperand1) { if (printed.empty() && !xml) out << "\n\n##AST" << std::endl; else if (printed.find(tok) != printed.end()) continue; printed.insert(tok); if (xml) { out << "scope() << "\" fileIndex=\"" << tok->fileIndex() << "\" linenr=\"" << tok->linenr() << "\" column=\"" << tok->column() << "\">" << std::endl; astStringXml(tok, 2U, out); out << "" << std::endl; } else if (verbose) out << tok->astStringVerbose() << std::endl; else out << tok->astString(" ") << std::endl; if (tok->str() == "(") tok = tok->link(); } } } static void indent(std::string &str, const nonneg int indent1, const nonneg int indent2) { for (int i = 0; i < indent1; ++i) str += ' '; for (int i = indent1; i < indent2; i += 2) str += "| "; } void Token::astStringVerboseRecursive(std::string& ret, const nonneg int indent1, const nonneg int indent2) const { if (isExpandedMacro()) ret += '$'; ret += mStr; if (mImpl->mValueType) ret += " \'" + mImpl->mValueType->str() + '\''; ret += '\n'; if (mImpl->mAstOperand1) { int i1 = indent1, i2 = indent2 + 2; if (indent1 == indent2 && !mImpl->mAstOperand2) i1 += 2; indent(ret, indent1, indent2); ret += mImpl->mAstOperand2 ? "|-" : "`-"; mImpl->mAstOperand1->astStringVerboseRecursive(ret, i1, i2); } if (mImpl->mAstOperand2) { int i1 = indent1, i2 = indent2 + 2; if (indent1 == indent2) i1 += 2; indent(ret, indent1, indent2); ret += "`-"; mImpl->mAstOperand2->astStringVerboseRecursive(ret, i1, i2); } } std::string Token::astStringVerbose() const { std::string ret; astStringVerboseRecursive(ret); return ret; } void Token::printValueFlow(bool xml, std::ostream &out) const { int line = 0; if (xml) out << " " << std::endl; else out << "\n\n##Value flow" << std::endl; for (const Token *tok = this; tok; tok = tok->next()) { if (!tok->mImpl->mValues) continue; if (xml) out << " mImpl->mValues << "\">" << std::endl; else if (line != tok->linenr()) out << "Line " << tok->linenr() << std::endl; line = tok->linenr(); if (!xml) { out << " " << tok->str() << (tok->mImpl->mValues->front().isKnown() ? " always " : " possible "); if (tok->mImpl->mValues->size() > 1U) out << '{'; } for (const ValueFlow::Value &value : *tok->mImpl->mValues) { if (xml) { out << " valueType() && tok->valueType()->sign == ValueType::UNSIGNED) out << "intvalue=\"" << (MathLib::biguint)value.intvalue << '\"'; else out << "intvalue=\"" << value.intvalue << '\"'; break; case ValueFlow::Value::TOK: out << "tokvalue=\"" << value.tokvalue << '\"'; break; case ValueFlow::Value::FLOAT: out << "floatvalue=\"" << value.floatValue << '\"'; break; case ValueFlow::Value::MOVED: out << "movedvalue=\"" << ValueFlow::Value::toString(value.moveKind) << '\"'; break; case ValueFlow::Value::UNINIT: out << "uninit=\"1\""; break; case ValueFlow::Value::BUFFER_SIZE: out << "buffer-size=\"" << value.intvalue << "\""; break; case ValueFlow::Value::CONTAINER_SIZE: out << "container-size=\"" << value.intvalue << '\"'; break; case ValueFlow::Value::LIFETIME: out << "lifetime=\"" << value.tokvalue << '\"'; break; } if (value.condition) out << " condition-line=\"" << value.condition->linenr() << '\"'; if (value.isKnown()) out << " known=\"true\""; else if (value.isPossible()) out << " possible=\"true\""; else if (value.isImpossible()) out << " impossible=\"true\""; else if (value.isInconclusive()) out << " inconclusive=\"true\""; out << "/>" << std::endl; } else { if (&value != &tok->mImpl->mValues->front()) out << ","; if (value.isImpossible()) out << "!"; if (value.bound == ValueFlow::Value::Bound::Lower) out << ">"; if (value.bound == ValueFlow::Value::Bound::Upper) out << "<"; switch (value.valueType) { case ValueFlow::Value::INT: if (tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED) out << (MathLib::biguint)value.intvalue; else out << value.intvalue; break; case ValueFlow::Value::TOK: out << value.tokvalue->str(); break; case ValueFlow::Value::FLOAT: out << value.floatValue; break; case ValueFlow::Value::MOVED: out << ValueFlow::Value::toString(value.moveKind); break; case ValueFlow::Value::UNINIT: out << "Uninit"; break; case ValueFlow::Value::BUFFER_SIZE: case ValueFlow::Value::CONTAINER_SIZE: out << "size=" << value.intvalue; break; case ValueFlow::Value::LIFETIME: out << "lifetime=" << value.tokvalue->str(); break; } if (value.indirect > 0) for (int i=0; i" << std::endl; else if (tok->mImpl->mValues->size() > 1U) out << '}' << std::endl; else out << std::endl; } if (xml) out << " " << std::endl; } const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Settings *settings) const { if (!mImpl->mValues) return nullptr; const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { if (it->isImpossible()) continue; if (it->isIntValue() && it->intvalue <= val) { if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive())) ret = &(*it); if (!ret->isInconclusive() && !ret->condition) break; } } if (settings && ret) { if (ret->isInconclusive() && !settings->inconclusive) return nullptr; if (ret->condition && !settings->isEnabled(Settings::WARNING)) return nullptr; } return ret; } const ValueFlow::Value * Token::getValueGE(const MathLib::bigint val, const Settings *settings) const { if (!mImpl->mValues) return nullptr; const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { if (it->isImpossible()) continue; if (it->isIntValue() && it->intvalue >= val) { if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive())) ret = &(*it); if (!ret->isInconclusive() && !ret->condition) break; } } if (settings && ret) { if (ret->isInconclusive() && !settings->inconclusive) return nullptr; if (ret->condition && !settings->isEnabled(Settings::WARNING)) return nullptr; } return ret; } const ValueFlow::Value * Token::getInvalidValue(const Token *ftok, nonneg int argnr, const Settings *settings) const { if (!mImpl->mValues || !settings) return nullptr; const ValueFlow::Value *ret = nullptr; std::list::const_iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { if (it->isImpossible()) continue; if ((it->isIntValue() && !settings->library.isIntArgValid(ftok, argnr, it->intvalue)) || (it->isFloatValue() && !settings->library.isFloatArgValid(ftok, argnr, it->floatValue))) { if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive())) ret = &(*it); if (!ret->isInconclusive() && !ret->condition) break; } } if (ret) { if (ret->isInconclusive() && !settings->inconclusive) return nullptr; if (ret->condition && !settings->isEnabled(Settings::WARNING)) return nullptr; } return ret; } const Token *Token::getValueTokenMinStrSize(const Settings *settings) const { if (!mImpl->mValues) return nullptr; const Token *ret = nullptr; int minsize = INT_MAX; std::list::const_iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) { const int size = getStrSize(it->tokvalue, settings); if (!ret || size < minsize) { minsize = size; ret = it->tokvalue; } } } return ret; } const Token *Token::getValueTokenMaxStrLength() const { if (!mImpl->mValues) return nullptr; const Token *ret = nullptr; int maxlength = 0; std::list::const_iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) { const int length = getStrLength(it->tokvalue); if (!ret || length > maxlength) { maxlength = length; ret = it->tokvalue; } } } return ret; } static const Scope *getfunctionscope(const Scope *s) { while (s && s->type != Scope::eFunction) s = s->nestedIn; return s; } const Token *Token::getValueTokenDeadPointer() const { const Scope * const functionscope = getfunctionscope(this->scope()); std::list::const_iterator it; for (it = values().begin(); it != values().end(); ++it) { // Is this a pointer alias? if (!it->isTokValue() || (it->tokvalue && it->tokvalue->str() != "&")) continue; // Get variable const Token *vartok = it->tokvalue->astOperand1(); if (!vartok || !vartok->isName() || !vartok->variable()) continue; const Variable * const var = vartok->variable(); if (var->isStatic() || var->isReference()) continue; if (!var->scope()) return nullptr; // #6804 if (var->scope()->type == Scope::eUnion && var->scope()->nestedIn == this->scope()) continue; // variable must be in same function (not in subfunction) if (functionscope != getfunctionscope(var->scope())) continue; // Is variable defined in this scope or upper scope? const Scope *s = this->scope(); while ((s != nullptr) && (s != var->scope())) s = s->nestedIn; if (!s) return it->tokvalue; } return nullptr; } static bool removeContradiction(std::list& values) { bool result = false; for (ValueFlow::Value& x : values) { if (x.isNonValue()) continue; for (ValueFlow::Value& y : values) { if (y.isNonValue()) continue; if (x == y) continue; if (x.valueType != y.valueType) continue; if (x.isImpossible() == y.isImpossible()) continue; if (!x.equalValue(y)) continue; if (x.bound == y.bound || (x.bound != ValueFlow::Value::Bound::Point && y.bound != ValueFlow::Value::Bound::Point)) { const bool removex = !x.isImpossible() || y.isKnown(); const bool removey = !y.isImpossible() || x.isKnown(); if (removex) values.remove(x); if (removey) values.remove(y); return true; } else if (x.bound == ValueFlow::Value::Bound::Point) { y.decreaseRange(); result = true; } } } return result; } static void removeOverlaps(std::list& values) { for (ValueFlow::Value& x : values) { if (x.isNonValue()) continue; values.remove_if([&](ValueFlow::Value& y) { if (y.isNonValue()) return false; if (&x == &y) return false; if (x.valueType != y.valueType) return false; if (x.valueKind != y.valueKind) return false; // TODO: Remove points coverd in a lower or upper bound // TODO: Remove lower or upper bound already covered by a lower and upper bound if (!x.equalValue(y)) return false; if (x.bound != y.bound) return false; return true; }); } } // Removing contradictions is an NP-hard problem. Instead we run multiple // passes to try to catch most contradictions static void removeContradictions(std::list& values) { for (int i = 0; i < 4; i++) { if (!removeContradiction(values)) return; removeOverlaps(values); } } bool Token::addValue(const ValueFlow::Value &value) { if (value.isKnown() && mImpl->mValues) { // Clear all other values of the same type since value is known mImpl->mValues->remove_if([&](const ValueFlow::Value & x) { return x.valueType == value.valueType; }); } if (mImpl->mValues) { // Don't handle more than 10 values for performance reasons // TODO: add setting? if (mImpl->mValues->size() >= 10U) return false; // if value already exists, don't add it again std::list::iterator it; for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { // different types => continue if (it->valueType != value.valueType) continue; if (it->isImpossible() != value.isImpossible()) continue; // different value => continue bool differentValue = true; switch (it->valueType) { case ValueFlow::Value::ValueType::INT: case ValueFlow::Value::ValueType::CONTAINER_SIZE: case ValueFlow::Value::ValueType::BUFFER_SIZE: differentValue = (it->intvalue != value.intvalue); break; case ValueFlow::Value::ValueType::TOK: case ValueFlow::Value::ValueType::LIFETIME: differentValue = (it->tokvalue != value.tokvalue); break; case ValueFlow::Value::ValueType::FLOAT: // TODO: Write some better comparison differentValue = (it->floatValue > value.floatValue || it->floatValue < value.floatValue); break; case ValueFlow::Value::ValueType::MOVED: differentValue = (it->moveKind != value.moveKind); break; case ValueFlow::Value::ValueType::UNINIT: differentValue = false; break; } if (differentValue) continue; if ((value.isTokValue() || value.isLifetimeValue()) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str())) continue; // same value, but old value is inconclusive so replace it if (it->isInconclusive() && !value.isInconclusive() && !value.isImpossible()) { *it = value; if (it->varId == 0) it->varId = mImpl->mVarId; break; } // Same value already exists, don't add new value return false; } // Add value if (it == mImpl->mValues->end()) { ValueFlow::Value v(value); if (v.varId == 0) v.varId = mImpl->mVarId; if (v.isKnown() && v.isIntValue()) mImpl->mValues->push_front(v); else mImpl->mValues->push_back(v); } } else { ValueFlow::Value v(value); if (v.varId == 0) v.varId = mImpl->mVarId; mImpl->mValues = new std::list(1, v); } removeContradictions(*mImpl->mValues); return true; } void Token::assignProgressValues(Token *tok) { int total_count = 0; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) ++total_count; int count = 0; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) tok2->mImpl->mProgressValue = count++ * 100 / total_count; } void Token::assignIndexes() { int index = (mPrevious ? mPrevious->mImpl->mIndex : 0) + 1; for (Token *tok = this; tok; tok = tok->next()) tok->mImpl->mIndex = index++; } void Token::setValueType(ValueType *vt) { if (vt != mImpl->mValueType) { delete mImpl->mValueType; mImpl->mValueType = vt; } } void Token::type(const ::Type *t) { mImpl->mType = t; if (t) { tokType(eType); isEnumType(mImpl->mType->isEnumType()); } else if (mTokType == eType) tokType(eName); } const ::Type *Token::typeOf(const Token *tok) { if (Token::simpleMatch(tok, "return")) { const Scope *scope = tok->scope(); if (!scope) return nullptr; const Function *function = scope->function; if (!function) return nullptr; return function->retType; } else if (Token::Match(tok, "%type%")) { return tok->type(); } else if (Token::Match(tok, "%var%")) { const Variable *var = tok->variable(); if (!var) return nullptr; return var->type(); } else if (Token::Match(tok, "%name%")) { const Function *function = tok->function(); if (!function) return nullptr; return function->retType; } else if (Token::simpleMatch(tok, "=")) { return Token::typeOf(tok->astOperand1()); } else if (Token::simpleMatch(tok, ".")) { return Token::typeOf(tok->astOperand2()); } else if (Token::simpleMatch(tok, "[")) { return Token::typeOf(tok->astOperand1()); } return nullptr; } std::pair Token::typeDecl(const Token * tok) { if (Token::simpleMatch(tok, "return")) { const Scope *scope = tok->scope(); if (!scope) return {}; const Function *function = scope->function; if (!function) return {}; return {function->retDef, function->returnDefEnd()}; } else if (Token::Match(tok, "%type%")) { return {tok, tok->next()}; } else if (Token::Match(tok, "%var%")) { const Variable *var = tok->variable(); if (!var) return {}; if (!var->typeStartToken() || !var->typeEndToken()) return {}; return {var->typeStartToken(), var->typeEndToken()->next()}; } else if (Token::Match(tok, "%name%")) { const Function *function = tok->function(); if (!function) return {}; return {function->retDef, function->returnDefEnd()}; } else if (Token::simpleMatch(tok, "=")) { return Token::typeDecl(tok->astOperand1()); } else if (Token::simpleMatch(tok, ".")) { return Token::typeDecl(tok->astOperand2()); } else { const ::Type * t = typeOf(tok); if (!t || !t->classDef) return {}; return {t->classDef->next(), t->classDef->tokAt(2)}; } } std::string Token::typeStr(const Token* tok) { if (tok->valueType()) { const ValueType * vt = tok->valueType(); std::string ret = vt->str(); if (!ret.empty()) return ret; } std::pair r = Token::typeDecl(tok); if (!r.first || !r.second) return ""; return r.first->stringifyList(r.second, false); } void Token::scopeInfo(std::shared_ptr newScopeInfo) { mImpl->mScopeInfo = newScopeInfo; } std::shared_ptr Token::scopeInfo() const { return mImpl->mScopeInfo; } TokenImpl::~TokenImpl() { delete mOriginalName; delete mValueType; delete mValues; for (auto templateSimplifierPointer : mTemplateSimplifierPointers) { templateSimplifierPointer->token(nullptr); } while (mCppcheckAttributes) { struct CppcheckAttributes *c = mCppcheckAttributes; mCppcheckAttributes = mCppcheckAttributes->next; delete c; } } void TokenImpl::setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) { struct CppcheckAttributes *attr = mCppcheckAttributes; while (attr && attr->type != type) attr = attr->next; if (attr) attr->value = value; else { attr = new CppcheckAttributes; attr->type = type; attr->value = value; attr->next = mCppcheckAttributes; mCppcheckAttributes = attr; } } bool TokenImpl::getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const { struct CppcheckAttributes *attr = mCppcheckAttributes; while (attr && attr->type != type) attr = attr->next; if (attr) *value = attr->value; return attr != nullptr; } cppcheck-1.90/lib/token.h000066400000000000000000001231751357737443600153160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef tokenH #define tokenH //--------------------------------------------------------------------------- #include "config.h" #include "mathlib.h" #include "valueflow.h" #include "templatesimplifier.h" #include "utils.h" #include #include #include #include #include #include #include #include class Enumerator; class Function; class Scope; class Settings; class Type; class ValueType; class Variable; /** * @brief This struct stores pointers to the front and back tokens of the list this token is in. */ struct TokensFrontBack { Token *front; Token *back; }; struct ScopeInfo2 { ScopeInfo2(const std::string &name_, const Token *bodyEnd_, const std::set &usingNamespaces_ = std::set()) : name(name_), bodyEnd(bodyEnd_), usingNamespaces(usingNamespaces_) {} std::string name; const Token * const bodyEnd; std::set usingNamespaces; }; struct TokenImpl { nonneg int mVarId; nonneg int mFileIndex; nonneg int mLineNumber; nonneg int mColumn; // AST.. Token *mAstOperand1; Token *mAstOperand2; Token *mAstParent; // symbol database information const Scope *mScope; union { const Function *mFunction; const Variable *mVariable; const ::Type* mType; const Enumerator *mEnumerator; }; /** * A value from 0-100 that provides a rough idea about where in the token * list this token is located. */ nonneg int mProgressValue; /** * Token index. Position in token list */ nonneg int mIndex; // original name like size_t std::string* mOriginalName; // ValueType ValueType *mValueType; // ValueFlow std::list* mValues; static const std::list mEmptyValueList; /** Bitfield bit count. */ unsigned char mBits; // Pointer to a template in the template simplifier std::set mTemplateSimplifierPointers; // Pointer to the object representing this token's scope std::shared_ptr mScopeInfo; // __cppcheck_in_range__ struct CppcheckAttributes { enum Type {LOW,HIGH} type; MathLib::bigint value; struct CppcheckAttributes *next; }; struct CppcheckAttributes *mCppcheckAttributes; // For memoization, to speed up parsing of huge arrays #8897 enum class Cpp11init {UNKNOWN, CPP11INIT, NOINIT} mCpp11init; void setCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint value); bool getCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint *value) const; TokenImpl() : mVarId(0) , mFileIndex(0) , mLineNumber(0) , mColumn(0) , mAstOperand1(nullptr) , mAstOperand2(nullptr) , mAstParent(nullptr) , mScope(nullptr) , mFunction(nullptr) // Initialize whole union , mProgressValue(0) , mIndex(0) , mOriginalName(nullptr) , mValueType(nullptr) , mValues(nullptr) , mBits(0) , mTemplateSimplifierPointers() , mScopeInfo(nullptr) , mCppcheckAttributes(nullptr) , mCpp11init(Cpp11init::UNKNOWN) {} ~TokenImpl(); }; /// @addtogroup Core /// @{ /** * @brief The token list that the TokenList generates is a linked-list of this class. * * Tokens are stored as strings. The "if", "while", etc are stored in plain text. * The reason the Token class is needed (instead of using the string class) is that some extra functionality is also needed for tokens: * - location of the token is stored (fileIndex, linenr, column) * - functions for classifying the token (isName, isNumber, isBoolean, isStandardType) * * The Token class also has other functions for management of token list, matching tokens, etc. */ class CPPCHECKLIB Token { private: TokensFrontBack* mTokensFrontBack; // Not implemented.. Token(const Token &); Token operator=(const Token &); public: enum Type { eVariable, eType, eFunction, eKeyword, eName, // Names: Variable (varId), Type (typeId, later), Function (FuncId, later), Language keyword, Name (unknown identifier) eNumber, eString, eChar, eBoolean, eLiteral, eEnumerator, // Literals: Number, String, Character, Boolean, User defined literal (C++11), Enumerator eArithmeticalOp, eComparisonOp, eAssignmentOp, eLogicalOp, eBitOp, eIncDecOp, eExtendedOp, // Operators: Arithmetical, Comparison, Assignment, Logical, Bitwise, ++/--, Extended eBracket, // {, }, <, >: < and > only if link() is set. Otherwise they are comparison operators. eLambda, // A function without a name eEllipsis, // "..." eOther, eNone }; explicit Token(TokensFrontBack *tokensFrontBack = nullptr); ~Token(); template void str(T&& s) { mStr = s; mImpl->mVarId = 0; update_property_info(); } /** * Concatenate two (quoted) strings. Automatically cuts of the last/first character. * Example: "hello ""world" -> "hello world". Used by the token simplifier. */ void concatStr(std::string const& b); const std::string &str() const { return mStr; } /** * Unlink and delete the next 'count' tokens. */ void deleteNext(nonneg int count = 1); /** * Unlink and delete the previous 'count' tokens. */ void deletePrevious(nonneg int count = 1); /** * Swap the contents of this token with the next token. */ void swapWithNext(); /** * @return token in given index, related to this token. * For example index 1 would return next token, and 2 * would return next from that one. */ const Token *tokAt(int index) const; Token *tokAt(int index) { return const_cast(const_cast(this)->tokAt(index)); } /** * @return the link to the token in given index, related to this token. * For example index 1 would return the link to next token. */ const Token *linkAt(int index) const; Token *linkAt(int index) { return const_cast(const_cast(this)->linkAt(index)); } /** * @return String of the token in given index, related to this token. * If that token does not exist, an empty string is being returned. */ const std::string &strAt(int index) const; /** * Match given token (or list of tokens) to a pattern list. * * Possible patterns * "someRandomText" If token contains "someRandomText". * @note Use Match() if you want to use flags in patterns * * The patterns can be also combined to compare to multiple tokens at once * by separating tokens with a space, e.g. * ") void {" will return true if first token is ')' next token * is "void" and token after that is '{'. If even one of the tokens does * not match its pattern, false is returned. * * @param tok List of tokens to be compared to the pattern * @param pattern The pattern against which the tokens are compared, * e.g. "const" or ") void {". * @return true if given token matches with given pattern * false if given token does not match with given pattern */ static bool simpleMatch(const Token *tok, const char pattern[]); /** * Match given token (or list of tokens) to a pattern list. * * Possible patterns * - "%any%" any token * - "%assign%" a assignment operand * - "%bool%" true or false * - "%char%" Any token enclosed in '-character. * - "%comp%" Any token such that isComparisonOp() returns true. * - "%cop%" Any token such that isConstOp() returns true. * - "%name%" any token which is a name, variable or type e.g. "hello" or "int" * - "%num%" Any numeric token, e.g. "23" * - "%op%" Any token such that isOp() returns true. * - "%or%" A bitwise-or operator '|' * - "%oror%" A logical-or operator '||' * - "%type%" Anything that can be a variable type, e.g. "int", but not "delete". * - "%str%" Any token starting with "-character (C-string). * - "%var%" Match with token with varId > 0 * - "%varid%" Match with parameter varid * - "[abc]" Any of the characters 'a' or 'b' or 'c' * - "int|void|char" Any of the strings, int, void or char * - "int|void|char|" Any of the strings, int, void or char or empty string * - "!!else" No tokens or any token that is not "else". * - "someRandomText" If token contains "someRandomText". * * multi-compare patterns such as "int|void|char" can contain %%or%, %%oror% and %%op% * it is recommended to put such an %%cmd% as the first pattern. * For example: "%var%|%num%|)" means yes to a variable, a number or ')'. * * The patterns can be also combined to compare to multiple tokens at once * by separating tokens with a space, e.g. * ") const|void {" will return true if first token is ')' next token is either * "const" or "void" and token after that is '{'. If even one of the tokens does not * match its pattern, false is returned. * * @param tok List of tokens to be compared to the pattern * @param pattern The pattern against which the tokens are compared, * e.g. "const" or ") const|volatile| {". * @param varid if %%varid% is given in the pattern the Token::varId * will be matched against this argument * @return true if given token matches with given pattern * false if given token does not match with given pattern */ static bool Match(const Token *tok, const char pattern[], nonneg int varid = 0); /** * @return length of C-string. * * Should be called for %%str%% tokens only. * * @param tok token with C-string **/ static nonneg int getStrLength(const Token *tok); /** * @return array length of C-string. * * Should be called for %%str%% tokens only. * * @param tok token with C-string **/ static nonneg int getStrArraySize(const Token *tok); /** * @return sizeof of C-string. * * Should be called for %%str%% tokens only. * * @param tok token with C-string * @param settings Settings **/ static nonneg int getStrSize(const Token *tok, const Settings *const); /** * @return char of C-string at index (possible escaped "\\n") * * Should be called for %%str%% tokens only. * * @param tok token with C-string * @param index position of character **/ static std::string getCharAt(const Token *tok, MathLib::bigint index); const ValueType *valueType() const { return mImpl->mValueType; } void setValueType(ValueType *vt); const ValueType *argumentType() const { const Token *top = this; while (top && !Token::Match(top->astParent(), ",|(")) top = top->astParent(); return top ? top->mImpl->mValueType : nullptr; } Token::Type tokType() const { return mTokType; } void tokType(Token::Type t) { mTokType = t; const bool memoizedIsName = (mTokType == eName || mTokType == eType || mTokType == eVariable || mTokType == eFunction || mTokType == eKeyword || mTokType == eBoolean || mTokType == eEnumerator); // TODO: "true"/"false" aren't really a name... setFlag(fIsName, memoizedIsName); const bool memoizedIsLiteral = (mTokType == eNumber || mTokType == eString || mTokType == eChar || mTokType == eBoolean || mTokType == eLiteral || mTokType == eEnumerator); setFlag(fIsLiteral, memoizedIsLiteral); } void isKeyword(const bool kwd) { if (kwd) tokType(eKeyword); else if (mTokType == eKeyword) tokType(eName); } bool isKeyword() const { return mTokType == eKeyword; } bool isName() const { return getFlag(fIsName); } bool isNameOnly() const { return mFlags == fIsName && mTokType == eName; } bool isUpperCaseName() const; bool isLiteral() const { return getFlag(fIsLiteral); } bool isNumber() const { return mTokType == eNumber; } bool isEnumerator() const { return mTokType == eEnumerator; } bool isOp() const { return (isConstOp() || isAssignmentOp() || mTokType == eIncDecOp); } bool isConstOp() const { return (isArithmeticalOp() || mTokType == eLogicalOp || mTokType == eComparisonOp || mTokType == eBitOp); } bool isExtendedOp() const { return isConstOp() || mTokType == eExtendedOp; } bool isArithmeticalOp() const { return mTokType == eArithmeticalOp; } bool isComparisonOp() const { return mTokType == eComparisonOp; } bool isAssignmentOp() const { return mTokType == eAssignmentOp; } bool isBoolean() const { return mTokType == eBoolean; } bool isBinaryOp() const { return astOperand1() != nullptr && astOperand2() != nullptr; } bool isUnaryOp(const std::string &s) const { return s == mStr && astOperand1() != nullptr && astOperand2() == nullptr; } bool isUnaryPreOp() const; unsigned int flags() const { return mFlags; } void flags(const unsigned int flags_) { mFlags = flags_; } bool isUnsigned() const { return getFlag(fIsUnsigned); } void isUnsigned(const bool sign) { setFlag(fIsUnsigned, sign); } bool isSigned() const { return getFlag(fIsSigned); } void isSigned(const bool sign) { setFlag(fIsSigned, sign); } bool isPointerCompare() const { return getFlag(fIsPointerCompare); } void isPointerCompare(const bool b) { setFlag(fIsPointerCompare, b); } bool isLong() const { return getFlag(fIsLong); } void isLong(bool size) { setFlag(fIsLong, size); } bool isStandardType() const { return getFlag(fIsStandardType); } void isStandardType(const bool b) { setFlag(fIsStandardType, b); } bool isExpandedMacro() const { return getFlag(fIsExpandedMacro); } void isExpandedMacro(const bool m) { setFlag(fIsExpandedMacro, m); } bool isCast() const { return getFlag(fIsCast); } void isCast(bool c) { setFlag(fIsCast, c); } bool isAttributeConstructor() const { return getFlag(fIsAttributeConstructor); } void isAttributeConstructor(const bool ac) { setFlag(fIsAttributeConstructor, ac); } bool isAttributeDestructor() const { return getFlag(fIsAttributeDestructor); } void isAttributeDestructor(const bool value) { setFlag(fIsAttributeDestructor, value); } bool isAttributeUnused() const { return getFlag(fIsAttributeUnused); } void isAttributeUnused(bool unused) { setFlag(fIsAttributeUnused, unused); } bool isAttributeUsed() const { return getFlag(fIsAttributeUsed); } void isAttributeUsed(const bool unused) { setFlag(fIsAttributeUsed, unused); } bool isAttributePure() const { return getFlag(fIsAttributePure); } void isAttributePure(const bool value) { setFlag(fIsAttributePure, value); } bool isAttributeConst() const { return getFlag(fIsAttributeConst); } void isAttributeConst(bool value) { setFlag(fIsAttributeConst, value); } bool isAttributeNoreturn() const { return getFlag(fIsAttributeNoreturn); } void isAttributeNoreturn(const bool value) { setFlag(fIsAttributeNoreturn, value); } bool isAttributeNothrow() const { return getFlag(fIsAttributeNothrow); } void isAttributeNothrow(const bool value) { setFlag(fIsAttributeNothrow, value); } bool isAttributePacked() const { return getFlag(fIsAttributePacked); } void isAttributePacked(const bool value) { setFlag(fIsAttributePacked, value); } bool isAttributeNodiscard() const { return getFlag(fIsAttributeNodiscard); } void isAttributeNodiscard(const bool value) { setFlag(fIsAttributeNodiscard, value); } void setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) { mImpl->setCppcheckAttribute(type, value); } bool getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const { return mImpl->getCppcheckAttribute(type, value); } bool isControlFlowKeyword() const { return getFlag(fIsControlFlowKeyword); } bool isOperatorKeyword() const { return getFlag(fIsOperatorKeyword); } void isOperatorKeyword(const bool value) { setFlag(fIsOperatorKeyword, value); } bool isComplex() const { return getFlag(fIsComplex); } void isComplex(const bool value) { setFlag(fIsComplex, value); } bool isEnumType() const { return getFlag(fIsEnumType); } void isEnumType(const bool value) { setFlag(fIsEnumType, value); } bool isAtAddress() const { return getFlag(fAtAddress); } void isAtAddress(bool b) { setFlag(fAtAddress, b); } bool isIncompleteVar() const { return getFlag(fIncompleteVar); } void isIncompleteVar(bool b) { setFlag(fIncompleteVar, b); } bool isConstexpr() const { return getFlag(fConstexpr); } void isConstexpr(bool b) { setFlag(fConstexpr, b); } bool isExternC() const { return getFlag(fExternC); } void isExternC(bool b) { setFlag(fExternC, b); } bool isBitfield() const { return mImpl->mBits > 0; } unsigned char bits() const { return mImpl->mBits; } std::set &templateSimplifierPointers() const { return mImpl->mTemplateSimplifierPointers; } void templateSimplifierPointer(TemplateSimplifier::TokenAndName* tokenAndName) { mImpl->mTemplateSimplifierPointers.insert(tokenAndName); } void setBits(const unsigned char b) { mImpl->mBits = b; } bool isUtf8() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "u8")) || ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "u8"))); } bool isUtf16() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "u")) || ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "u"))); } bool isUtf32() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "U")) || ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "U"))); } bool isCChar() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "")) || ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "") && mStr.length() == 3)); } bool isCMultiChar() const { return (((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "")) && (mStr.length() > 3)); } /** * @brief Is current token a template argument? * * Original code: * * template struct S { * C x; * }; * S s; * * Resulting code: * * struct S { * int x ; // <- "int" is a template argument * } * S s; */ bool isTemplateArg() const { return getFlag(fIsTemplateArg); } void isTemplateArg(const bool value) { setFlag(fIsTemplateArg, value); } static const Token *findsimplematch(const Token * const startTok, const char pattern[]); static const Token *findsimplematch(const Token * const startTok, const char pattern[], const Token * const end); static const Token *findmatch(const Token * const startTok, const char pattern[], const nonneg int varId = 0); static const Token *findmatch(const Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId = 0); static Token *findsimplematch(Token * const startTok, const char pattern[]) { return const_cast(findsimplematch(const_cast(startTok), pattern)); } static Token *findsimplematch(Token * const startTok, const char pattern[], const Token * const end) { return const_cast(findsimplematch(const_cast(startTok), pattern, end)); } static Token *findmatch(Token * const startTok, const char pattern[], const nonneg int varId = 0) { return const_cast(findmatch(const_cast(startTok), pattern, varId)); } static Token *findmatch(Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId = 0) { return const_cast(findmatch(const_cast(startTok), pattern, end, varId)); } /** * Needle is build from multiple alternatives. If one of * them is equal to haystack, return value is 1. If there * are no matches, but one alternative to needle is empty * string, return value is 0. If needle was not found, return * value is -1. * * @param tok Current token (needle) * @param haystack e.g. "one|two" or "|one|two" * @param varid optional varid of token * @return 1 if needle is found from the haystack * 0 if needle was empty string * -1 if needle was not found */ static int multiCompare(const Token *tok, const char *haystack, nonneg int varid); nonneg int fileIndex() const { return mImpl->mFileIndex; } void fileIndex(nonneg int indexOfFile) { mImpl->mFileIndex = indexOfFile; } nonneg int linenr() const { return mImpl->mLineNumber; } void linenr(nonneg int lineNumber) { mImpl->mLineNumber = lineNumber; } nonneg int column() const { return mImpl->mColumn; } void column(nonneg int c) { mImpl->mColumn = c; } Token *next() const { return mNext; } /** * Delete tokens between begin and end. E.g. if begin = 1 * and end = 5, tokens 2,3 and 4 would be erased. * * @param begin Tokens after this will be erased. * @param end Tokens before this will be erased. */ static void eraseTokens(Token *begin, const Token *end); /** * Insert new token after this token. This function will handle * relations between next and previous token also. * @param tokenStr String for the new token. * @param originalNameStr String used for Token::originalName(). * @param prepend Insert the new token before this token when it's not * the first one on the tokens list. */ void insertToken(const std::string &tokenStr, const std::string &originalNameStr=emptyString, bool prepend=false); Token *previous() const { return mPrevious; } nonneg int varId() const { return mImpl->mVarId; } void varId(nonneg int id) { mImpl->mVarId = id; if (id != 0) { tokType(eVariable); isStandardType(false); } else { update_property_info(); } } /** * For debugging purposes, prints token and all tokens * followed by it. * @param title Title for the printout or use default parameter or 0 * for no title. */ void printOut(const char *title = nullptr) const; /** * For debugging purposes, prints token and all tokens * followed by it. * @param title Title for the printout or use default parameter or 0 * for no title. * @param fileNames Prints out file name instead of file index. * File index should match the index of the string in this vector. */ void printOut(const char *title, const std::vector &fileNames) const; /** * Replace token replaceThis with tokens between start and end, * including start and end. The replaceThis token is deleted. * @param replaceThis This token will be deleted. * @param start This will be in the place of replaceThis * @param end This is also in the place of replaceThis */ static void replace(Token *replaceThis, Token *start, Token *end); /** * Stringify a token * @param os The result is shifted into that output stream * @param varid Print varids. (Style: "varname@id") * @param attributes Print attributes of tokens like "unsigned" in front of it. * @param macro Prints $ in front of the token if it was expanded from a macro. */ void stringify(std::ostream& os, bool varid, bool attributes, bool macro) const; /** * Stringify a list of token, from current instance on. * @param varid Print varids. (Style: "varname@id") * @param attributes Print attributes of tokens like "unsigned" in front of it. * @param linenumbers Print line number in front of each line * @param linebreaks Insert "\\n" into string when line number changes * @param files print Files as numbers or as names (if fileNames is given) * @param fileNames Vector of filenames. Used (if given) to print filenames as strings instead of numbers. * @param end Stringification ends before this token is reached. 0 to stringify until end of list. * @return Stringified token list as a string */ std::string stringifyList(bool varid, bool attributes, bool linenumbers, bool linebreaks, bool files, const std::vector* fileNames = nullptr, const Token* end = nullptr) const; std::string stringifyList(const Token* end, bool attributes = true) const; std::string stringifyList(bool varid = false) const; /** * Remove the contents for this token from the token list. * * The contents are replaced with the contents of the next token and * the next token is unlinked and deleted from the token list. * * So this token will still be valid after the 'deleteThis()'. */ void deleteThis(); /** * Create link to given token * @param linkToToken The token where this token should link * to. */ void link(Token *linkToToken) { mLink = linkToToken; if (mStr == "<" || mStr == ">") update_property_info(); } /** * Return token where this token links to. * Supported links are: * "{" <-> "}" * "(" <-> ")" * "[" <-> "]" * * @return The token where this token links to. */ Token *link() const { return mLink; } /** * Associate this token with given scope * @param s Scope to be associated */ void scope(const Scope *s) { mImpl->mScope = s; } /** * @return a pointer to the scope containing this token. */ const Scope *scope() const { return mImpl->mScope; } /** * Associate this token with given function * @param f Function to be associated */ void function(const Function *f); /** * @return a pointer to the Function associated with this token. */ const Function *function() const { return mTokType == eFunction || mTokType == eLambda ? mImpl->mFunction : nullptr; } /** * Associate this token with given variable * @param v Variable to be associated */ void variable(const Variable *v) { mImpl->mVariable = v; if (v || mImpl->mVarId) tokType(eVariable); else if (mTokType == eVariable) tokType(eName); } /** * @return a pointer to the variable associated with this token. */ const Variable *variable() const { return mTokType == eVariable ? mImpl->mVariable : nullptr; } /** * Associate this token with given type * @param t Type to be associated */ void type(const ::Type *t); /** * @return a pointer to the type associated with this token. */ const ::Type *type() const { return mTokType == eType ? mImpl->mType : nullptr; } static const ::Type *typeOf(const Token *tok); static std::pair typeDecl(const Token * tok); static std::string typeStr(const Token* tok); /** * @return a pointer to the Enumerator associated with this token. */ const Enumerator *enumerator() const { return mTokType == eEnumerator ? mImpl->mEnumerator : nullptr; } /** * Associate this token with given enumerator * @param e Enumerator to be associated */ void enumerator(const Enumerator *e) { mImpl->mEnumerator = e; if (e) tokType(eEnumerator); else if (mTokType == eEnumerator) tokType(eName); } /** * Links two elements against each other. **/ static void createMutualLinks(Token *begin, Token *end); /** * This can be called only for tokens that are strings, else * the assert() is called. If Token is e.g. '"hello"', this will return * 'hello' (removing the double quotes). * @return String value */ std::string strValue() const; /** * Move srcStart and srcEnd tokens and all tokens between them * into new a location. Only links between tokens are changed. * @param srcStart This is the first token to be moved * @param srcEnd The last token to be moved * @param newLocation srcStart will be placed after this token. */ static void move(Token *srcStart, Token *srcEnd, Token *newLocation); /** Get progressValue (0 - 100) */ nonneg int progressValue() const { return mImpl->mProgressValue; } /** Calculate progress values for all tokens */ static void assignProgressValues(Token *tok); /** * @return the first token of the next argument. Does only work on argument * lists. Requires that Tokenizer::createLinks2() has been called before. * Returns 0, if there is no next argument. */ Token* nextArgument() const; /** * @return the first token of the next argument. Does only work on argument * lists. Should be used only before Tokenizer::createLinks2() was called. * Returns 0, if there is no next argument. */ Token* nextArgumentBeforeCreateLinks2() const; /** * @return the first token of the next template argument. Does only work on template argument * lists. Requires that Tokenizer::createLinks2() has been called before. * Returns 0, if there is no next argument. */ Token* nextTemplateArgument() const; /** * Returns the closing bracket of opening '<'. Should only be used if link() * is unavailable. * @return closing '>', ')', ']' or '}'. if no closing bracket is found, NULL is returned */ const Token* findClosingBracket() const; Token* findClosingBracket(); const Token* findOpeningBracket() const; Token* findOpeningBracket(); /** * @return the original name. */ const std::string & originalName() const { return mImpl->mOriginalName ? *mImpl->mOriginalName : emptyString; } const std::list& values() const { return mImpl->mValues ? *mImpl->mValues : TokenImpl::mEmptyValueList; } /** * Sets the original name. */ template void originalName(T&& name) { if (!mImpl->mOriginalName) mImpl->mOriginalName = new std::string(name); else *mImpl->mOriginalName = name; } bool hasKnownIntValue() const { if (!mImpl->mValues) return false; return std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [](const ValueFlow::Value &value) { return value.isKnown() && value.isIntValue(); }); } bool hasKnownValue() const { return mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), std::mem_fn(&ValueFlow::Value::isKnown)); } MathLib::bigint getKnownIntValue() const { return mImpl->mValues->front().intvalue; } const ValueFlow::Value * getValue(const MathLib::bigint val) const { if (!mImpl->mValues) return nullptr; const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [=](const ValueFlow::Value& value) { return value.isIntValue() && !value.isImpossible() && value.intvalue == val; }); return it == mImpl->mValues->end() ? nullptr : &*it;; } const ValueFlow::Value * getMaxValue(bool condition) const { if (!mImpl->mValues) return nullptr; const ValueFlow::Value *ret = nullptr; for (const ValueFlow::Value &value : *mImpl->mValues) { if (!value.isIntValue()) continue; if (value.isImpossible()) continue; if ((!ret || value.intvalue > ret->intvalue) && ((value.condition != nullptr) == condition)) ret = &value; } return ret; } const ValueFlow::Value * getMovedValue() const { if (!mImpl->mValues) return nullptr; const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [](const ValueFlow::Value& value) { return value.isMovedValue() && !value.isImpossible() && value.moveKind != ValueFlow::Value::MoveKind::NonMovedVariable; }); return it == mImpl->mValues->end() ? nullptr : &*it;; } const ValueFlow::Value * getValueLE(const MathLib::bigint val, const Settings *settings) const; const ValueFlow::Value * getValueGE(const MathLib::bigint val, const Settings *settings) const; const ValueFlow::Value * getInvalidValue(const Token *ftok, nonneg int argnr, const Settings *settings) const; const ValueFlow::Value * getContainerSizeValue(const MathLib::bigint val) const { if (!mImpl->mValues) return nullptr; const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [=](const ValueFlow::Value& value) { return value.isContainerSizeValue() && !value.isImpossible() && value.intvalue == val; }); return it == mImpl->mValues->end() ? nullptr : &*it; } const Token *getValueTokenMaxStrLength() const; const Token *getValueTokenMinStrSize(const Settings *settings) const; const Token *getValueTokenDeadPointer() const; /** Add token value. Return true if value is added. */ bool addValue(const ValueFlow::Value &value); template void removeValues(Predicate pred) { if (mImpl->mValues) mImpl->mValues->remove_if(pred); } nonneg int index() const { return mImpl->mIndex; } void assignIndexes(); private: void next(Token *nextToken) { mNext = nextToken; } void previous(Token *previousToken) { mPrevious = previousToken; } /** used by deleteThis() to take data from token to delete */ void takeData(Token *fromToken); /** * Works almost like strcmp() except returns only true or false and * if str has empty space ' ' character, that character is handled * as if it were '\\0' */ static bool firstWordEquals(const char *str, const char *word); /** * Works almost like strchr() except * if str has empty space ' ' character, that character is handled * as if it were '\\0' */ static const char *chrInFirstWord(const char *str, char c); std::string mStr; Token *mNext; Token *mPrevious; Token *mLink; enum { fIsUnsigned = (1 << 0), fIsSigned = (1 << 1), fIsPointerCompare = (1 << 2), fIsLong = (1 << 3), fIsStandardType = (1 << 4), fIsExpandedMacro = (1 << 5), fIsCast = (1 << 6), fIsAttributeConstructor = (1 << 7), // __attribute__((constructor)) __attribute__((constructor(priority))) fIsAttributeDestructor = (1 << 8), // __attribute__((destructor)) __attribute__((destructor(priority))) fIsAttributeUnused = (1 << 9), // __attribute__((unused)) fIsAttributePure = (1 << 10), // __attribute__((pure)) fIsAttributeConst = (1 << 11), // __attribute__((const)) fIsAttributeNoreturn = (1 << 12), // __attribute__((noreturn)), __declspec(noreturn) fIsAttributeNothrow = (1 << 13), // __attribute__((nothrow)), __declspec(nothrow) fIsAttributeUsed = (1 << 14), // __attribute__((used)) fIsAttributePacked = (1 << 15), // __attribute__((packed)) 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), }; Token::Type mTokType; unsigned int mFlags; TokenImpl *mImpl; /** * Get specified flag state. * @param flag_ flag to get state of * @return true if flag set or false in flag not set */ bool getFlag(unsigned int flag_) const { return ((mFlags & flag_) != 0); } /** * Set specified flag state. * @param flag_ flag to set state * @param state_ new state of flag */ void setFlag(unsigned int flag_, bool state_) { mFlags = state_ ? mFlags | flag_ : mFlags & ~flag_; } /** Updates internal property cache like _isName or _isBoolean. Called after any mStr() modification. */ void update_property_info(); /** Update internal property cache about isStandardType() */ void update_property_isStandardType(); /** Update internal property cache about string and char literals */ void update_property_char_string_literal(); /** Internal helper function to avoid excessive string allocations */ void astStringVerboseRecursive(std::string& ret, const nonneg int indent1 = 0, const nonneg int indent2 = 0) const; public: void astOperand1(Token *tok); void astOperand2(Token *tok); Token * astOperand1() { return mImpl->mAstOperand1; } const Token * astOperand1() const { return mImpl->mAstOperand1; } Token * astOperand2() { return mImpl->mAstOperand2; } const Token * astOperand2() const { return mImpl->mAstOperand2; } Token * astParent() { return mImpl->mAstParent; } const Token * astParent() const { return mImpl->mAstParent; } Token *astTop() { Token *ret = this; while (ret->mImpl->mAstParent) ret = ret->mImpl->mAstParent; return ret; } const Token *astTop() const { const Token *ret = this; while (ret->mImpl->mAstParent) ret = ret->mImpl->mAstParent; return ret; } std::pair findExpressionStartEndTokens() const; /** * Is current token a calculation? Only true for operands. * For '*' and '&' tokens it is looked up if this is a * dereference or address-of. A dereference or address-of is not * counted as a calculation. * @return returns true if current token is a calculation */ bool isCalculation() const; void clearAst() { mImpl->mAstOperand1 = mImpl->mAstOperand2 = mImpl->mAstParent = nullptr; } void clearValueFlow() { delete mImpl->mValues; mImpl->mValues = nullptr; } std::string astString(const char *sep = "") const { std::string ret; if (mImpl->mAstOperand1) ret = mImpl->mAstOperand1->astString(sep); if (mImpl->mAstOperand2) ret += mImpl->mAstOperand2->astString(sep); return ret + sep + mStr; } std::string astStringVerbose() const; std::string expressionString() const; void printAst(bool verbose, bool xml, std::ostream &out) const; void printValueFlow(bool xml, std::ostream &out) const; void scopeInfo(std::shared_ptr newScopeInfo); std::shared_ptr scopeInfo() const; void setCpp11init(bool cpp11init) const { mImpl->mCpp11init=cpp11init ? TokenImpl::Cpp11init::CPP11INIT : TokenImpl::Cpp11init::NOINIT; } TokenImpl::Cpp11init isCpp11init() const { return mImpl->mCpp11init; } }; /// @} //--------------------------------------------------------------------------- #endif // tokenH cppcheck-1.90/lib/tokenize.cpp000066400000000000000000015301351357737443600163600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "tokenize.h" #include "check.h" #include "library.h" #include "mathlib.h" #include "path.h" #include "platform.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "templatesimplifier.h" #include "timer.h" #include "token.h" #include "utils.h" #include "valueflow.h" #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- namespace { // local struct used in setVarId // in order to store information about the scope struct VarIdScopeInfo { VarIdScopeInfo() :isExecutable(false), isStructInit(false), isEnum(false), startVarid(0) { } VarIdScopeInfo(bool isExecutable, bool isStructInit, bool isEnum, nonneg int startVarid) :isExecutable(isExecutable), isStructInit(isStructInit), isEnum(isEnum), startVarid(startVarid) { } const bool isExecutable; const bool isStructInit; const bool isEnum; const nonneg int startVarid; }; } /** Return whether tok is the "{" that starts an enumerator list */ static bool isEnumStart(const Token* tok) { if (!tok || tok->str() != "{") return false; return (tok->strAt(-1) == "enum") || (tok->strAt(-2) == "enum"); } template static void skipEnumBody(T **tok) { T *defStart = *tok; while (Token::Match(defStart, "%name%|::|:")) defStart = defStart->next(); if (defStart && defStart->str() == "{") *tok = defStart->link()->next(); } const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &endsWith) const { return Tokenizer::isFunctionHead(tok, endsWith, isCPP()); } const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &endsWith, bool cpp) { if (!tok) return nullptr; if (tok->str() == "(") tok = tok->link(); if (Token::Match(tok, ") ;|{|[")) { tok = tok->next(); while (tok && tok->str() == "[" && tok->link()) { if (endsWith.find(tok->str()) != std::string::npos) return tok; tok = tok->link()->next(); } return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; } if (cpp && tok->str() == ")") { tok = tok->next(); while (Token::Match(tok, "const|noexcept|override|final|volatile|&|&& !!(") || (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) tok = tok->next(); if (tok && tok->str() == ")") tok = tok->next(); while (tok && tok->str() == "[") tok = tok->link()->next(); if (Token::Match(tok, "throw|noexcept (")) tok = tok->linkAt(1)->next(); if (Token::Match(tok, "%name% (") && tok->isUpperCaseName()) tok = tok->linkAt(1)->next(); if (tok && tok->originalName() == "->") { // trailing return type for (tok = tok->next(); tok && !Token::Match(tok, ";|{|override|final"); tok = tok->next()) if (tok->link() && Token::Match(tok, "<|[|(")) tok = tok->link(); } while (Token::Match(tok, "override|final !!(") || (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) tok = tok->next(); if (Token::Match(tok, "= 0|default|delete ;")) tok = tok->tokAt(2); return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; } return nullptr; } /** * is tok the start brace { of a class, struct, union, or enum */ static bool isClassStructUnionEnumStart(const Token * tok) { if (!Token::Match(tok->previous(), "class|struct|union|enum|%name%|>|>> {")) return false; const Token * tok2 = tok->previous(); while (tok2 && !Token::Match(tok2, "class|struct|union|enum|{|}|;")) tok2 = tok2->previous(); return Token::Match(tok2, "class|struct|union|enum"); } //--------------------------------------------------------------------------- Tokenizer::Tokenizer() : list(nullptr), mSettings(nullptr), mErrorLogger(nullptr), mSymbolDatabase(nullptr), mTemplateSimplifier(nullptr), mVarId(0), mUnnamedCount(0), mCodeWithTemplates(false), //is there any templates? mTimerResults(nullptr) #ifdef MAXTIME ,mMaxTime(std::time(0) + MAXTIME) #endif { } Tokenizer::Tokenizer(const Settings *settings, ErrorLogger *errorLogger) : list(settings), mSettings(settings), mErrorLogger(errorLogger), mSymbolDatabase(nullptr), mTemplateSimplifier(nullptr), mVarId(0), mUnnamedCount(0), mCodeWithTemplates(false), //is there any templates? mTimerResults(nullptr) #ifdef MAXTIME ,mMaxTime(std::time(0) + MAXTIME) #endif { // make sure settings are specified assert(mSettings); mTemplateSimplifier = new TemplateSimplifier(this); } Tokenizer::~Tokenizer() { delete mSymbolDatabase; delete mTemplateSimplifier; } //--------------------------------------------------------------------------- // SizeOfType - gives the size of a type //--------------------------------------------------------------------------- nonneg int Tokenizer::sizeOfType(const Token *type) const { if (!type || type->str().empty()) return 0; if (type->tokType() == Token::eString) return Token::getStrLength(type) + 1U; const std::map::const_iterator it = mTypeSize.find(type->str()); if (it == mTypeSize.end()) { const Library::PodType* podtype = mSettings->library.podtype(type->str()); if (!podtype) return 0; return podtype->size; } else if (type->isLong()) { if (type->str() == "double") return mSettings->sizeof_long_double; else if (type->str() == "long") return mSettings->sizeof_long_long; } return it->second; } //--------------------------------------------------------------------------- // check if this statement is a duplicate definition bool Tokenizer::duplicateTypedef(Token **tokPtr, const Token *name, const Token *typeDef) const { // check for an end of definition const Token * tok = *tokPtr; if (tok && Token::Match(tok->next(), ";|,|[|=|)|>|(|{")) { const Token * end = tok->next(); if (end->str() == "[") { if (!end->link()) syntaxError(end); // invalid code end = end->link()->next(); } else if (end->str() == ",") { // check for derived class if (Token::Match(tok->previous(), "public|private|protected")) return false; // find end of definition while (end && end->next() && !Token::Match(end->next(), ";|)|>")) { if (end->next()->str() == "(") end = end->linkAt(1); end = (end)?end->next():nullptr; } if (end) end = end->next(); } else if (end->str() == "(") { if (tok->previous()->str().compare(0, 8, "operator") == 0) { // conversion operator return false; } else if (tok->previous()->str() == "typedef") { // typedef of function returning this type return false; } else if (Token::Match(tok->previous(), "public:|private:|protected:")) { return false; } else if (tok->previous()->str() == ">") { if (!Token::Match(tok->tokAt(-2), "%type%")) return false; if (!Token::Match(tok->tokAt(-3), ",|<")) return false; *tokPtr = end->link(); return true; } } if (end) { if (Token::simpleMatch(end, ") {")) { // function parameter ? // look backwards if (Token::Match(tok->previous(), "%type%") && !Token::Match(tok->previous(), "return|new|const|struct")) { // duplicate definition so skip entire function *tokPtr = end->next()->link(); return true; } } else if (end->str() == ">") { // template parameter ? // look backwards if (Token::Match(tok->previous(), "%type%") && !Token::Match(tok->previous(), "return|new|const|volatile")) { // duplicate definition so skip entire template while (end && end->str() != "{") end = end->next(); if (end) { *tokPtr = end->link(); return true; } } } else { // look backwards if (Token::Match(tok->previous(), "typedef|}|>") || (end->str() == ";" && tok->previous()->str() == ",") || (tok->previous()->str() == "*" && tok->next()->str() != "(") || (Token::Match(tok->previous(), "%type%") && (!Token::Match(tok->previous(), "return|new|const|friend|public|private|protected|throw|extern") && !Token::simpleMatch(tok->tokAt(-2), "friend class")))) { // scan backwards for the end of the previous statement while (tok && tok->previous() && !Token::Match(tok->previous(), ";|{")) { if (tok->previous()->str() == "}") { tok = tok->previous()->link(); } else if (tok->previous()->str() == "typedef") { return true; } else if (tok->previous()->str() == "enum") { return true; } else if (tok->previous()->str() == "struct") { if (tok->strAt(-2) == "typedef" && tok->next()->str() == "{" && typeDef->strAt(3) != "{") { // declaration after forward declaration return true; } else if (tok->next()->str() == "{") { return true; } else if (Token::Match(tok->next(), ")|*")) { return true; } else if (tok->next()->str() == name->str()) { return true; } else if (tok->next()->str() != ";") { return true; } else { return false; } } else if (tok->previous()->str() == "union") { if (tok->next()->str() != ";") { return true; } else { return false; } } else if (isCPP() && tok->previous()->str() == "class") { if (tok->next()->str() != ";") { return true; } else { return false; } } if (tok) tok = tok->previous(); } if ((*tokPtr)->strAt(1) != "(" || !Token::Match((*tokPtr)->linkAt(1), ") .|(|[")) return true; } } } } return false; } void Tokenizer::unsupportedTypedef(const Token *tok) const { if (!mSettings->debugwarnings) return; std::ostringstream str; const Token *tok1 = tok; int level = 0; while (tok) { if (level == 0 && tok->str() == ";") break; else if (tok->str() == "{") ++level; else if (tok->str() == "}") { if (level == 0) break; --level; } if (tok != tok1) str << " "; str << tok->str(); tok = tok->next(); } if (tok) str << " ;"; reportError(tok1, Severity::debug, "debug", "Failed to parse \'" + str.str() + "\'. The checking continues anyway."); } Token * Tokenizer::deleteInvalidTypedef(Token *typeDef) { Token *tok = nullptr; // remove typedef but leave ; while (typeDef->next()) { if (typeDef->next()->str() == ";") { typeDef->deleteNext(); break; } else if (typeDef->next()->str() == "{") Token::eraseTokens(typeDef, typeDef->linkAt(1)); else if (typeDef->next()->str() == "}") break; typeDef->deleteNext(); } if (typeDef != list.front()) { tok = typeDef->previous(); tok->deleteNext(); } else { list.front()->deleteThis(); tok = list.front(); } return tok; } namespace { struct Space { Space() : bodyEnd(nullptr), isNamespace(false) { } std::string className; const Token * bodyEnd; bool isNamespace; }; } static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount) { Token *tok1; std::string name; bool isConst = false; if (tok->next()->str() == "const") { tok->deleteNext(); isConst = true; } if (tok->strAt(2) == "{") { // unnamed tok1 = tok->linkAt(2); if (tok1 && tok1->next()) { // use typedef name if available if (Token::Match(tok1->next(), "%type%")) name = tok1->next()->str(); else // create a unique name name = "Unnamed" + MathLib::toString((*unnamedCount)++); tok->next()->insertToken(name); } else return nullptr; } else if (tok->strAt(3) == ":") { tok1 = tok->tokAt(4); while (tok1 && tok1->str() != "{") tok1 = tok1->next(); if (!tok1) return nullptr; tok1 = tok1->link(); name = tok->strAt(2); } else { // has a name tok1 = tok->linkAt(3); if (!tok1) return nullptr; name = tok->strAt(2); } tok1->insertToken(";"); tok1 = tok1->next(); if (tok1->next() && tok1->next()->str() == ";" && tok1->previous()->str() == "}") { tok->deleteThis(); tok1->deleteThis(); return nullptr; } else { tok1->insertToken("typedef"); tok1 = tok1->next(); Token * tok3 = tok1; if (isConst) { tok1->insertToken("const"); tok1 = tok1->next(); } tok1->insertToken(tok->next()->str()); // struct, union or enum tok1 = tok1->next(); tok1->insertToken(name); tok->deleteThis(); tok = tok3; } return tok; } /* This function is called when processing function related typedefs. * If simplifyTypedef generates an "Internal Error" message and the * code that generated it deals in some way with functions, then this * function will probably need to be extended to handle a new function * related pattern */ Token *Tokenizer::processFunc(Token *tok2, bool inOperator) const { if (tok2->next() && tok2->next()->str() != ")" && tok2->next()->str() != ",") { // skip over tokens for some types of canonicalization if (Token::Match(tok2->next(), "( * %type% ) (")) tok2 = tok2->linkAt(5); else if (Token::Match(tok2->next(), "* ( * %type% ) (")) tok2 = tok2->linkAt(6); else if (Token::Match(tok2->next(), "* ( * %type% ) ;")) tok2 = tok2->tokAt(5); else if (Token::Match(tok2->next(), "* ( %type% [") && Token::Match(tok2->linkAt(4), "] ) ;|=")) tok2 = tok2->linkAt(4)->next(); else if (Token::Match(tok2->next(), "* ( * %type% (")) tok2 = tok2->linkAt(5)->next(); else if (Token::simpleMatch(tok2->next(), "* [") && Token::simpleMatch(tok2->linkAt(2), "] ;")) tok2 = tok2->next(); else { if (tok2->next()->str() == "(") tok2 = tok2->next()->link(); else if (!inOperator && !Token::Match(tok2->next(), "[|>|;")) { tok2 = tok2->next(); while (Token::Match(tok2, "*|&") && !Token::Match(tok2->next(), ")|>")) tok2 = tok2->next(); // skip over namespace while (Token::Match(tok2, "%name% ::")) tok2 = tok2->tokAt(2); if (!tok2) return nullptr; if (tok2->str() == "(" && tok2->link()->next() && tok2->link()->next()->str() == "(") { tok2 = tok2->link(); if (tok2->next()->str() == "(") tok2 = tok2->next()->link(); } // skip over typedef parameter if (tok2->next() && tok2->next()->str() == "(") { tok2 = tok2->next()->link(); if (!tok2->next()) syntaxError(tok2); if (tok2->next()->str() == "(") tok2 = tok2->next()->link(); } } } } return tok2; } void Tokenizer::simplifyTypedef() { std::vector spaceInfo; bool isNamespace = false; std::string className; bool hasClass = false; bool goback = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (mErrorLogger && !list.getFiles().empty()) mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (typedef)", tok->progressValue()); if (Settings::terminated()) return; if (isMaxTime()) return; if (goback) { //jump back once, see the comment at the end of the function goback = false; tok = tok->previous(); } if (tok->str() != "typedef") { if (tok->str() == "(" && tok->strAt(1) == "typedef") { // Skip typedefs inside parentheses (#2453 and #4002) tok = tok->next(); } else if (Token::Match(tok, "class|struct|namespace %any%") && (!tok->previous() || tok->previous()->str() != "enum")) { isNamespace = (tok->str() == "namespace"); hasClass = true; className = tok->next()->str(); } else if (hasClass && tok->str() == ";") { hasClass = false; } else if (hasClass && tok->str() == "{") { Space info; info.isNamespace = isNamespace; info.className = className; info.bodyEnd = tok->link(); spaceInfo.push_back(info); hasClass = false; } else if (!spaceInfo.empty() && tok->str() == "}" && spaceInfo.back().bodyEnd == tok) { spaceInfo.pop_back(); } continue; } // pull struct, union, enum or class definition out of typedef // use typedef name for unnamed struct, union, enum or class if (Token::Match(tok->next(), "const| struct|enum|union|class %type%| {")) { Token *tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount); if (!tok1) continue; tok = tok1; } else if (Token::Match(tok->next(), "const| struct|class %type% :")) { Token *tok1 = tok; while (tok1 && tok1->str() != ";" && tok1->str() != "{") tok1 = tok1->next(); if (tok1 && tok1->str() == "{") { tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount); if (!tok1) continue; tok = tok1; } } /** @todo add support for union */ if (Token::Match(tok->next(), "enum %type% %type% ;") && tok->strAt(2) == tok->strAt(3)) { tok->deleteNext(3); tok->deleteThis(); if (tok->next()) tok->deleteThis(); //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } Token *typeName; Token *typeStart = nullptr; Token *typeEnd = nullptr; Token *argStart = nullptr; Token *argEnd = nullptr; Token *arrayStart = nullptr; Token *arrayEnd = nullptr; Token *specStart = nullptr; Token *specEnd = nullptr; Token *typeDef = tok; Token *argFuncRetStart = nullptr; Token *argFuncRetEnd = nullptr; Token *funcStart = nullptr; Token *funcEnd = nullptr; Token *tokOffset = tok->next(); bool function = false; bool functionPtr = false; bool functionRetFuncPtr = false; bool functionPtrRetFuncPtr = false; bool ptrToArray = false; bool refToArray = false; bool ptrMember = false; bool typeOf = false; Token *namespaceStart = nullptr; Token *namespaceEnd = nullptr; // check for invalid input if (!tokOffset) syntaxError(tok); if (tokOffset->str() == "::") { typeStart = tokOffset; tokOffset = tokOffset->next(); while (Token::Match(tokOffset, "%type% ::")) tokOffset = tokOffset->tokAt(2); typeEnd = tokOffset; if (Token::Match(tokOffset, "%type%")) tokOffset = tokOffset->next(); } else if (Token::Match(tokOffset, "%type% ::")) { typeStart = tokOffset; do { tokOffset = tokOffset->tokAt(2); } while (Token::Match(tokOffset, "%type% ::")); typeEnd = tokOffset; if (Token::Match(tokOffset, "%type%")) tokOffset = tokOffset->next(); } else if (Token::Match(tokOffset, "%type%")) { typeStart = tokOffset; while (Token::Match(tokOffset, "const|struct|enum %type%") || (tokOffset->next() && tokOffset->next()->isStandardType())) tokOffset = tokOffset->next(); typeEnd = tokOffset; tokOffset = tokOffset->next(); while (Token::Match(tokOffset, "%type%") && (tokOffset->isStandardType() || Token::Match(tokOffset, "unsigned|signed"))) { typeEnd = tokOffset; tokOffset = tokOffset->next(); } bool atEnd = false; while (!atEnd) { if (tokOffset && tokOffset->str() == "::") { typeEnd = tokOffset; tokOffset = tokOffset->next(); } if (Token::Match(tokOffset, "%type%") && tokOffset->next() && !Token::Match(tokOffset->next(), "[|;|,|(")) { typeEnd = tokOffset; tokOffset = tokOffset->next(); } else if (Token::simpleMatch(tokOffset, "const (")) { typeEnd = tokOffset; tokOffset = tokOffset->next(); atEnd = true; } else atEnd = true; } } else continue; // invalid input // check for invalid input if (!tokOffset) syntaxError(tok); // check for template if (!isC() && tokOffset->str() == "<") { typeEnd = tokOffset->findClosingBracket(); while (typeEnd && Token::Match(typeEnd->next(), ":: %type%")) typeEnd = typeEnd->tokAt(2); if (!typeEnd) { // internal error return; } while (Token::Match(typeEnd->next(), "const|volatile")) typeEnd = typeEnd->next(); tok = typeEnd; tokOffset = tok->next(); } std::list pointers; // check for pointers and references while (Token::Match(tokOffset, "*|&|&&|const")) { pointers.push_back(tokOffset->str()); tokOffset = tokOffset->next(); } // check for invalid input if (!tokOffset) syntaxError(tok); if (Token::Match(tokOffset, "%type%")) { // found the type name typeName = tokOffset; tokOffset = tokOffset->next(); // check for array if (tokOffset && tokOffset->str() == "[") { arrayStart = tokOffset; bool atEnd = false; while (!atEnd) { while (tokOffset->next() && !Token::Match(tokOffset->next(), ";|,")) { tokOffset = tokOffset->next(); } if (!tokOffset->next()) return; // invalid input else if (tokOffset->next()->str() == ";") atEnd = true; else if (tokOffset->str() == "]") atEnd = true; else tokOffset = tokOffset->next(); } arrayEnd = tokOffset; tokOffset = tokOffset->next(); } // check for end or another if (Token::Match(tokOffset, ";|,")) tok = tokOffset; // or a function typedef else if (tokOffset && tokOffset->str() == "(") { Token *tokOffset2 = nullptr; if (Token::Match(tokOffset, "( *|%name%")) { tokOffset2 = tokOffset->next(); if (tokOffset2->str() == "typename") tokOffset2 = tokOffset2->next(); while (Token::Match(tokOffset2, "%type% ::")) tokOffset2 = tokOffset2->tokAt(2); } // unhandled typedef, skip it and continue if (typeName->str() == "void") { unsupportedTypedef(typeDef); tok = deleteInvalidTypedef(typeDef); if (tok == list.front()) //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } // function pointer else if (Token::Match(tokOffset2, "* %name% ) (")) { // name token wasn't a name, it was part of the type typeEnd = typeEnd->next(); functionPtr = true; funcStart = funcEnd = tokOffset2; // * tokOffset = tokOffset2->tokAt(3); // ( typeName = tokOffset->tokAt(-2); argStart = tokOffset; argEnd = tokOffset->link(); tok = argEnd->next(); } // function else if (isFunctionHead(tokOffset->link(), ";,")) { function = true; if (tokOffset->link()->next()->str() == "const") { specStart = tokOffset->link()->next(); specEnd = specStart; } argStart = tokOffset; argEnd = tokOffset->link(); tok = argEnd->next(); if (specStart) tok = tok->next(); } // syntax error else syntaxError(tok); } // unhandled typedef, skip it and continue else { unsupportedTypedef(typeDef); tok = deleteInvalidTypedef(typeDef); if (tok == list.front()) //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } } // typeof: typedef typeof ( ... ) type; else if (Token::simpleMatch(tokOffset->previous(), "typeof (") && Token::Match(tokOffset->link(), ") %type% ;")) { argStart = tokOffset; argEnd = tokOffset->link(); typeName = tokOffset->link()->next(); tok = typeName->next(); typeOf = true; } // function: typedef ... ( ... type )( ... ); // typedef ... (( ... type )( ... )); // typedef ... ( * ( ... type )( ... )); else if (tokOffset->str() == "(" && ( (tokOffset->link() && Token::Match(tokOffset->link()->previous(), "%type% ) (") && Token::Match(tokOffset->link()->next()->link(), ") const|volatile|;")) || (Token::simpleMatch(tokOffset, "( (") && tokOffset->next() && Token::Match(tokOffset->next()->link()->previous(), "%type% ) (") && Token::Match(tokOffset->next()->link()->next()->link(), ") const|volatile| ) ;|,")) || (Token::simpleMatch(tokOffset, "( * (") && tokOffset->linkAt(2) && Token::Match(tokOffset->linkAt(2)->previous(), "%type% ) (") && Token::Match(tokOffset->linkAt(2)->next()->link(), ") const|volatile| ) ;|,")))) { if (tokOffset->next()->str() == "(") tokOffset = tokOffset->next(); else if (Token::simpleMatch(tokOffset, "( * (")) { pointers.emplace_back("*"); tokOffset = tokOffset->tokAt(2); } if (tokOffset->link()->strAt(-2) == "*") functionPtr = true; else function = true; funcStart = tokOffset->next(); tokOffset = tokOffset->link(); funcEnd = tokOffset->tokAt(-2); typeName = tokOffset->previous(); argStart = tokOffset->next(); argEnd = tokOffset->next()->link(); if (!argEnd) syntaxError(argStart); tok = argEnd->next(); Token *spec = tok; if (Token::Match(spec, "const|volatile")) { specStart = spec; specEnd = spec; while (Token::Match(spec->next(), "const|volatile")) { specEnd = spec->next(); spec = specEnd; } tok = specEnd->next(); } if (!tok) syntaxError(specEnd); if (tok->str() == ")") tok = tok->next(); } else if (Token::Match(tokOffset, "( %type% (")) { function = true; if (tokOffset->link()->next()) { tok = tokOffset->link()->next(); tokOffset = tokOffset->tokAt(2); typeName = tokOffset->previous(); argStart = tokOffset; argEnd = tokOffset->link(); } else { // internal error continue; } } // pointer to function returning pointer to function else if (Token::Match(tokOffset, "( * ( * %type% ) (") && Token::simpleMatch(tokOffset->linkAt(6), ") ) (") && Token::Match(tokOffset->linkAt(6)->linkAt(2), ") ;|,")) { functionPtrRetFuncPtr = true; tokOffset = tokOffset->tokAt(6); typeName = tokOffset->tokAt(-2); argStart = tokOffset; argEnd = tokOffset->link(); if (!argEnd) syntaxError(arrayStart); argFuncRetStart = argEnd->tokAt(2); argFuncRetEnd = argFuncRetStart->link(); if (!argFuncRetEnd) syntaxError(argFuncRetStart); tok = argFuncRetEnd->next(); } // function returning pointer to function else if (Token::Match(tokOffset, "( * %type% (") && Token::simpleMatch(tokOffset->linkAt(3), ") ) (") && Token::Match(tokOffset->linkAt(3)->linkAt(2), ") ;|,")) { functionRetFuncPtr = true; tokOffset = tokOffset->tokAt(3); typeName = tokOffset->previous(); argStart = tokOffset; argEnd = tokOffset->link(); argFuncRetStart = argEnd->tokAt(2); if (!argFuncRetStart) syntaxError(tokOffset); argFuncRetEnd = argFuncRetStart->link(); if (!argFuncRetEnd) syntaxError(tokOffset); tok = argFuncRetEnd->next(); } else if (Token::Match(tokOffset, "( * ( %type% ) (")) { functionRetFuncPtr = true; tokOffset = tokOffset->tokAt(5); typeName = tokOffset->tokAt(-2); argStart = tokOffset; argEnd = tokOffset->link(); if (!argEnd) syntaxError(arrayStart); argFuncRetStart = argEnd->tokAt(2); if (!argFuncRetStart) syntaxError(tokOffset); argFuncRetEnd = argFuncRetStart->link(); if (!argFuncRetEnd) syntaxError(tokOffset); tok = argFuncRetEnd->next(); } // pointer/reference to array else if (Token::Match(tokOffset, "( *|& %type% ) [")) { ptrToArray = (tokOffset->next()->str() == "*"); refToArray = !ptrToArray; tokOffset = tokOffset->tokAt(2); typeName = tokOffset; arrayStart = tokOffset->tokAt(2); arrayEnd = arrayStart->link(); if (!arrayEnd) syntaxError(arrayStart); tok = arrayEnd->next(); } // pointer to class member else if (Token::Match(tokOffset, "( %type% :: * %type% ) ;")) { tokOffset = tokOffset->tokAt(2); namespaceStart = tokOffset->previous(); namespaceEnd = tokOffset; ptrMember = true; tokOffset = tokOffset->tokAt(2); typeName = tokOffset; tok = tokOffset->tokAt(2); } // unhandled typedef, skip it and continue else { unsupportedTypedef(typeDef); tok = deleteInvalidTypedef(typeDef); if (tok == list.front()) //now the next token to process is 'tok', not 'tok->next()'; goback = true; continue; } bool done = false; bool ok = true; while (!done) { std::string pattern = typeName->str(); int scope = 0; bool simplifyType = false; bool inMemberFunc = false; int memberScope = 0; bool globalScope = false; int classLevel = spaceInfo.size(); for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (Settings::terminated()) return; if (tok2->link()) { // Pre-check for performance // check for end of scope if (tok2->str() == "}") { // check for end of member function if (inMemberFunc) { --memberScope; if (memberScope == 0) inMemberFunc = false; } if (classLevel > 0 && tok2 == spaceInfo[classLevel - 1].bodyEnd) { --classLevel; pattern.clear(); for (int i = classLevel; i < spaceInfo.size(); ++i) pattern += (spaceInfo[i].className + " :: "); pattern += typeName->str(); } else { if (scope == 0) break; --scope; } } // check for member functions else if (isCPP() && Token::Match(tok2, ")|] const| {")) { const Token *temp = tok2; while (temp && temp->str() == "]" && temp->link() && temp->link()->previous()) temp = temp->link()->previous(); if (!temp || !temp->link() || !temp->link()->previous()) continue; const Token *func = temp->link()->previous(); if (temp->str() != ")") continue; if (!func || !func->previous()) // Ticket #4239 continue; /** @todo add support for multi-token operators */ if (func->previous()->str() == "operator") func = func->previous(); if (!func->previous()) syntaxError(func); // check for qualifier if (func->previous()->str() == "::") { // check for available and matching class name if (!spaceInfo.empty() && classLevel < spaceInfo.size() && func->strAt(-2) == spaceInfo[classLevel].className) { memberScope = 0; inMemberFunc = true; } } } // check for entering a new scope else if (tok2->str() == "{") { // check for entering a new namespace if (isCPP() && tok2->strAt(-2) == "namespace") { if (classLevel < spaceInfo.size() && spaceInfo[classLevel].isNamespace && spaceInfo[classLevel].className == tok2->previous()->str()) { spaceInfo[classLevel].bodyEnd = tok2->link(); ++classLevel; pattern.clear(); for (int i = classLevel; i < spaceInfo.size(); ++i) pattern += spaceInfo[i].className + " :: "; pattern += typeName->str(); } ++scope; } // keep track of scopes within member function if (inMemberFunc) ++memberScope; ++scope; } } // check for operator typedef /** @todo add support for multi-token operators */ else if (isCPP() && tok2->str() == "operator" && tok2->next() && tok2->next()->str() == typeName->str() && tok2->linkAt(2) && tok2->strAt(2) == "(" && Token::Match(tok2->linkAt(2), ") const| {")) { // check for qualifier if (tok2->previous()->str() == "::") { // check for available and matching class name if (!spaceInfo.empty() && classLevel < spaceInfo.size() && tok2->strAt(-2) == spaceInfo[classLevel].className) { tok2 = tok2->next(); simplifyType = true; } } } // check for typedef that can be substituted else if (Token::simpleMatch(tok2, pattern.c_str()) || (inMemberFunc && tok2->str() == typeName->str())) { // member function class variables don't need qualification if (!(inMemberFunc && tok2->str() == typeName->str()) && pattern.find("::") != std::string::npos) { // has a "something ::" Token *start = tok2; int count = 0; int back = classLevel - 1; bool good = true; // check for extra qualification while (back >= 0) { Token *qualificationTok = start->tokAt(-2); if (!Token::Match(qualificationTok, "%type% ::")) break; if (qualificationTok->str() == spaceInfo[back].className) { start = qualificationTok; back--; count++; } else { good = false; break; } } // check global namespace if (good && back == 0 && start->strAt(-1) == "::") good = false; if (good) { // remove any extra qualification if present while (count) { tok2->tokAt(-3)->deleteNext(2); --count; } // remove global namespace if present if (tok2->strAt(-1) == "::") { tok2->tokAt(-2)->deleteNext(); globalScope = true; } // remove qualification if present for (int i = classLevel; i < spaceInfo.size(); ++i) { tok2->deleteNext(2); } simplifyType = true; } } else { if (tok2->strAt(-1) == "::") { int relativeSpaceInfoSize = spaceInfo.size(); Token * tokBeforeType = tok2->previous(); while (relativeSpaceInfoSize != 0 && tokBeforeType && tokBeforeType->str() == "::" && tokBeforeType->strAt(-1) == spaceInfo[relativeSpaceInfoSize-1].className) { tokBeforeType = tokBeforeType->tokAt(-2); --relativeSpaceInfoSize; } if (tokBeforeType && tokBeforeType->str() != "::") { Token::eraseTokens(tokBeforeType, tok2); simplifyType = true; } } else if (Token::Match(tok2->previous(), "case|;|{|} %type% :")) { tok2 = tok2->next(); } else if (duplicateTypedef(&tok2, typeName, typeDef)) { // skip to end of scope if not already there if (tok2->str() != "}") { while (tok2->next()) { if (tok2->next()->str() == "{") tok2 = tok2->linkAt(1)->previous(); else if (tok2->next()->str() == "}") break; tok2 = tok2->next(); } } } else if (Token::Match(tok2->tokAt(-2), "%type% *|&")) { // Ticket #5868: Don't substitute variable names } else if (tok2->previous()->str() != ".") { simplifyType = true; } } } if (simplifyType) { // can't simplify 'operator functionPtr ()' and 'functionPtr operator ... ()' if (functionPtr && (tok2->previous()->str() == "operator" || (tok2->next() && tok2->next()->str() == "operator"))) { simplifyType = false; tok2 = tok2->next(); continue; } // There are 2 categories of typedef substitutions: // 1. variable declarations that preserve the variable name like // global, local, and function parameters // 2. not variable declarations that have no name like derived // classes, casts, operators, and template parameters // try to determine which category this substitution is bool inCast = false; bool inTemplate = false; bool inOperator = false; bool inSizeof = false; const bool sameStartEnd = (typeStart == typeEnd); // check for derived class: class A : some_typedef { const bool isDerived = Token::Match(tok2->previous(), "public|protected|private %type% {|,"); // check for cast: (some_typedef) A or static_cast(A) // todo: check for more complicated casts like: (const some_typedef *)A if ((tok2->previous()->str() == "(" && tok2->next()->str() == ")" && tok2->strAt(-2) != "sizeof") || (tok2->previous()->str() == "<" && Token::simpleMatch(tok2->next(), "> (")) || Token::Match(tok2->tokAt(-2), "( const %name% )")) inCast = true; // check for template parameters: t t1 else if (Token::Match(tok2->previous(), "<|,") && Token::Match(tok2->next(), "&|*| &|*| >|,")) inTemplate = true; else if (Token::Match(tok2->tokAt(-2), "sizeof ( %type% )")) inSizeof = true; // check for operator if (tok2->strAt(-1) == "operator" || Token::simpleMatch(tok2->tokAt(-2), "operator const")) inOperator = true; if (typeStart->str() == "typename" && tok2->strAt(-1)=="typename") { // Remove one typename if it is already contained in the goal typeStart = typeStart->next(); } // skip over class or struct in derived class declaration bool structRemoved = false; if (isDerived && Token::Match(typeStart, "class|struct")) { if (typeStart->str() == "struct") structRemoved = true; typeStart = typeStart->next(); } if (Token::Match(typeStart, "struct|class") && Token::Match(tok2, "%name% ::")) typeStart = typeStart->next(); if (sameStartEnd) typeEnd = typeStart; // start substituting at the typedef name by replacing it with the type tok2->str(typeStart->str()); // restore qualification if it was removed if (typeStart->str() == "struct" || structRemoved) { if (structRemoved) tok2 = tok2->previous(); if (globalScope) { tok2->insertToken("::"); tok2 = tok2->next(); } for (int i = classLevel; i < spaceInfo.size(); ++i) { tok2->insertToken(spaceInfo[i].className); tok2 = tok2->next(); tok2->insertToken("::"); tok2 = tok2->next(); } } // add remainder of type tok2 = TokenList::copyTokens(tok2, typeStart->next(), typeEnd); if (!pointers.empty()) { for (const std::string &p : pointers) { tok2->insertToken(p); tok2 = tok2->next(); } } if (funcStart && funcEnd) { tok2->insertToken("("); tok2 = tok2->next(); Token *tok3 = tok2; tok2 = TokenList::copyTokens(tok2, funcStart, funcEnd); if (!inCast) tok2 = processFunc(tok2, inOperator); if (!tok2) break; tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); tok2 = TokenList::copyTokens(tok2, argStart, argEnd); if (specStart) { Token *spec = specStart; tok2->insertToken(spec->str()); tok2 = tok2->next(); while (spec != specEnd) { spec = spec->next(); tok2->insertToken(spec->str()); tok2 = tok2->next(); } } } else if (functionPtr || function) { // don't add parentheses around function names because it // confuses other simplifications bool needParen = true; if (!inTemplate && function && tok2->next() && tok2->next()->str() != "*") needParen = false; if (needParen) { tok2->insertToken("("); tok2 = tok2->next(); } Token *tok3 = tok2; if (namespaceStart) { const Token *tok4 = namespaceStart; while (tok4 != namespaceEnd) { tok2->insertToken(tok4->str()); tok2 = tok2->next(); tok4 = tok4->next(); } tok2->insertToken(namespaceEnd->str()); tok2 = tok2->next(); } if (functionPtr) { tok2->insertToken("*"); tok2 = tok2->next(); } if (!inCast) tok2 = processFunc(tok2, inOperator); if (needParen) { if (!tok2) syntaxError(nullptr); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); } if (!tok2) syntaxError(nullptr); tok2 = TokenList::copyTokens(tok2, argStart, argEnd); if (inTemplate) { if (!tok2) syntaxError(nullptr); tok2 = tok2->next(); } if (specStart) { Token *spec = specStart; tok2->insertToken(spec->str()); tok2 = tok2->next(); while (spec != specEnd) { spec = spec->next(); tok2->insertToken(spec->str()); tok2 = tok2->next(); } } } else if (functionRetFuncPtr || functionPtrRetFuncPtr) { tok2->insertToken("("); tok2 = tok2->next(); Token *tok3 = tok2; tok2->insertToken("*"); tok2 = tok2->next(); Token * tok4 = nullptr; if (functionPtrRetFuncPtr) { tok2->insertToken("("); tok2 = tok2->next(); tok4 = tok2; tok2->insertToken("*"); tok2 = tok2->next(); } // skip over variable name if there if (!inCast) { if (!tok2 || !tok2->next()) syntaxError(nullptr); if (tok2->next()->str() != ")") tok2 = tok2->next(); } if (tok4 && functionPtrRetFuncPtr) { tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok4); } tok2 = TokenList::copyTokens(tok2, argStart, argEnd); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); tok2 = TokenList::copyTokens(tok2, argFuncRetStart, argFuncRetEnd); } else if (ptrToArray || refToArray) { tok2->insertToken("("); tok2 = tok2->next(); Token *tok3 = tok2; if (ptrToArray) tok2->insertToken("*"); else tok2->insertToken("&"); tok2 = tok2->next(); bool hasName = false; // skip over name if (tok2->next() && tok2->next()->str() != ")" && tok2->next()->str() != "," && tok2->next()->str() != ">") { hasName = true; if (tok2->next()->str() != "(") tok2 = tok2->next(); // check for function and skip over args if (tok2 && tok2->next() && tok2->next()->str() == "(") tok2 = tok2->next()->link(); // check for array if (tok2 && tok2->next() && tok2->next()->str() == "[") tok2 = tok2->next()->link(); } tok2->insertToken(")"); Token::createMutualLinks(tok2->next(), tok3); if (!hasName) tok2 = tok2->next(); } else if (ptrMember) { if (Token::simpleMatch(tok2, "* (")) { tok2->insertToken("*"); tok2 = tok2->next(); } else { // This is the case of casting operator. // Name is not available, and () should not be // inserted const bool castOperator = inOperator && Token::Match(tok2, "%type% ("); Token *openParenthesis = nullptr; if (!castOperator) { tok2->insertToken("("); tok2 = tok2->next(); openParenthesis = tok2; } const Token *tok4 = namespaceStart; while (tok4 != namespaceEnd) { tok2->insertToken(tok4->str()); tok2 = tok2->next(); tok4 = tok4->next(); } tok2->insertToken(namespaceEnd->str()); tok2 = tok2->next(); tok2->insertToken("*"); tok2 = tok2->next(); if (openParenthesis) { // Skip over name, if any if (Token::Match(tok2->next(), "%name%")) tok2 = tok2->next(); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, openParenthesis); } } } else if (typeOf) { tok2 = TokenList::copyTokens(tok2, argStart, argEnd); } else if (Token::Match(tok2, "%name% [")) { while (Token::Match(tok2, "%name%|] [")) { tok2 = tok2->linkAt(1); } tok2 = tok2->previous(); } if (arrayStart && arrayEnd) { do { if (!tok2->next()) syntaxError(tok2); // can't recover so quit if (!inCast && !inSizeof && !inTemplate) tok2 = tok2->next(); if (tok2->str() == "const") tok2 = tok2->next(); // reference or pointer to array? if (tok2->str() == "&" || tok2->str() == "*") { tok2 = tok2->previous(); tok2->insertToken("("); Token *tok3 = tok2->next(); // handle missing variable name if (tok2->strAt(3) == ")" || tok2->strAt(3) == "," || tok2->strAt(3) == "(") tok2 = tok2->tokAt(2); else tok2 = tok2->tokAt(3); if (!tok2) syntaxError(nullptr); while (tok2->strAt(1) == "::") tok2 = tok2->tokAt(2); // skip over function parameters if (tok2->str() == "(") tok2 = tok2->link(); if (tok2->strAt(1) == "(") tok2 = tok2->linkAt(1); // skip over const/noexcept while (Token::Match(tok2->next(), "const|noexcept")) tok2 = tok2->next(); tok2->insertToken(")"); tok2 = tok2->next(); Token::createMutualLinks(tok2, tok3); } if (!tok2->next()) syntaxError(tok2); // can't recover so quit // skip over array dimensions while (tok2->next()->str() == "[") tok2 = tok2->linkAt(1); tok2 = TokenList::copyTokens(tok2, arrayStart, arrayEnd); if (!tok2->next()) syntaxError(tok2); if (tok2->str() == "=") { if (!tok2->next()) syntaxError(tok2); if (tok2->next()->str() == "{") tok2 = tok2->next()->link()->next(); else if (tok2->next()->str().at(0) == '\"') tok2 = tok2->tokAt(2); } } while (Token::Match(tok2, ", %name% ;|=|,")); } simplifyType = false; } if (!tok2) break; } if (!tok) syntaxError(nullptr); if (tok->str() == ";") done = true; else if (tok->str() == ",") { arrayStart = nullptr; arrayEnd = nullptr; tokOffset = tok->next(); pointers.clear(); while (Token::Match(tokOffset, "*|&")) { pointers.push_back(tokOffset->str()); tokOffset = tokOffset->next(); } if (Token::Match(tokOffset, "%type%")) { typeName = tokOffset; tokOffset = tokOffset->next(); if (tokOffset && tokOffset->str() == "[") { arrayStart = tokOffset; for (;;) { while (tokOffset->next() && !Token::Match(tokOffset->next(), ";|,")) tokOffset = tokOffset->next(); if (!tokOffset->next()) return; // invalid input else if (tokOffset->next()->str() == ";") break; else if (tokOffset->str() == "]") break; else tokOffset = tokOffset->next(); } arrayEnd = tokOffset; tokOffset = tokOffset->next(); } if (Token::Match(tokOffset, ";|,")) tok = tokOffset; else { // we encountered a typedef we don't support yet so just continue done = true; ok = false; } } else { // we encountered a typedef we don't support yet so just continue done = true; ok = false; } } else { // something is really wrong (internal error) done = true; ok = false; } } if (ok) { // remove typedef Token::eraseTokens(typeDef, tok); if (typeDef != list.front()) { tok = typeDef->previous(); tok->deleteNext(); //no need to remove last token in the list if (tok->tokAt(2)) tok->deleteNext(); } else { list.front()->deleteThis(); //no need to remove last token in the list if (list.front()->next()) list.front()->deleteThis(); tok = list.front(); //now the next token to process is 'tok', not 'tok->next()'; goback = true; } } } } namespace { struct ScopeInfo3 { ScopeInfo3(const std::string &name_, const Token *bodyEnd_) : name(name_), bodyEnd(bodyEnd_) {} const std::string name; const Token * const bodyEnd; std::set usingNamespaces; }; std::string getScopeName(const std::list &scopeInfo) { std::string ret; for (const ScopeInfo3 &i : scopeInfo) { if (!i.name.empty()) ret += (ret.empty() ? "" : " :: ") + i.name; } return ret; } void setScopeInfo(Token *tok, std::list *scopeInfo, bool all = false) { while (tok->str() == "}" && !scopeInfo->empty() && tok == scopeInfo->back().bodyEnd) scopeInfo->pop_back(); if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::")) { // check for using namespace if (Token::Match(tok, "using namespace %name% ;|::")) { const Token * tok1 = tok->tokAt(2); std::string nameSpace; while (tok1 && tok1->str() != ";") { if (!nameSpace.empty()) nameSpace += " "; nameSpace += tok1->str(); tok1 = tok1->next(); } scopeInfo->back().usingNamespaces.insert(nameSpace); } // check for member function else if (tok->str() == "{") { bool added = false; Token *tok1 = tok; while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1 && tok1->previous() && tok1->strAt(-1) == ")") { tok1 = tok1->linkAt(-1); if (Token::Match(tok1->previous(), "throw|noexcept")) { tok1 = tok1->previous(); while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->strAt(-1) != ")") return; } else if (Token::Match(tok->tokAt(-2), ":|, %name%")) { tok1 = tok1->tokAt(-2); if (tok1->strAt(-1) != ")") return; } if (tok1->strAt(-1) == ">") tok1 = tok1->previous()->findOpeningBracket(); if (tok1 && (Token::Match(tok1->tokAt(-3), "%name% :: %name%") || Token::Match(tok1->tokAt(-4), "%name% :: ~ %name%"))) { tok1 = tok1->tokAt(-2); if (tok1->str() == "~") tok1 = tok1->previous(); std::string scope = tok1->strAt(-1); while (Token::Match(tok1->tokAt(-2), ":: %name%")) { scope = tok1->strAt(-3) + " :: " + scope; tok1 = tok1->tokAt(-2); } scopeInfo->emplace_back(scope, tok->link()); added = true; } } if (all && !added) scopeInfo->emplace_back("", tok->link()); } return; } tok = tok->next(); std::string classname = tok->str(); while (Token::Match(tok, "%name% :: %name%")) { tok = tok->tokAt(2); classname += " :: " + tok->str(); } tok = tok->next(); if (tok && tok->str() == ":") { while (tok && !Token::Match(tok, ";|{")) tok = tok->next(); } if (tok && tok->str() == "{") { scopeInfo->emplace_back(classname,tok->link()); } } Token *findSemicolon(Token *tok) { int level = 0; for (; tok && (level > 0 || tok->str() != ";"); tok = tok->next()) { if (tok->str() == "{") ++level; else if (level > 0 && tok->str() == "}") --level; } return tok; } bool usingMatch( const Token *nameToken, const std::string &scope, Token **tok, const std::string &scope1, const std::list &scopeList1) { Token *tok1 = *tok; if (tok1 && tok1->str() != nameToken->str()) return false; // skip this using if (tok1 == nameToken) { *tok = findSemicolon(tok1); return false; } // skip other using with this name if (tok1->strAt(-1) == "using") { // fixme: this is wrong // skip to end of scope if (scopeList1.back().bodyEnd) *tok = scopeList1.back().bodyEnd->previous(); return false; } if (Token::Match(tok1->tokAt(-1), "class|struct|union|enum|namespace")) { // fixme return false; } // get qualification std::string qualification; const Token* tok2 = tok1; std::string::size_type index = scope.size(); std::string::size_type new_index = std::string::npos; bool match = true; while (tok2->strAt(-1) == "::") { std::string last; if (match && !scope1.empty()) { new_index = scope1.rfind(' ', index - 1); if (new_index != std::string::npos) last = scope1.substr(new_index, index - new_index); else if (!qualification.empty()) last.clear(); else last = scope1; } else match = false; if (match && tok2->strAt(-2) == last) index = new_index; else { if (!qualification.empty()) qualification = " :: " + qualification; qualification = tok2->strAt(-2) + qualification; } tok2 = tok2->tokAt(-2); } std::string fullScope1 = scope1; if (!scope1.empty() && !qualification.empty()) fullScope1 += " :: "; fullScope1 += qualification; if (scope == fullScope1) return true; // check using namespace if (!scopeList1.back().usingNamespaces.empty()) { if (qualification.empty()) { if (scopeList1.back().usingNamespaces.find(scope) != scopeList1.back().usingNamespaces.end()) return true; } else { for (auto ns : scopeList1.back().usingNamespaces) { if (scope == ns + " :: " + qualification) return true; } } } std::string newScope1 = scope1; // scopes didn't match so try higher scopes index = newScope1.size(); while (!newScope1.empty()) { std::string::size_type separator = newScope1.rfind(" :: ", index - 1); if (separator != std::string::npos) newScope1 = newScope1.substr(0, separator); else newScope1.clear(); std::string newFullScope1 = newScope1; if (!newScope1.empty() && !qualification.empty()) newFullScope1 += " :: "; newFullScope1 += qualification; if (scope == newFullScope1) return true; } return false; } std::string memberFunctionScope(const Token *tok) { std::string qualification; const Token *qualTok = tok->strAt(-2) == "~" ? tok->tokAt(-4) : tok->tokAt(-3); while (Token::Match(qualTok, "%type% ::")) { if (!qualification.empty()) qualification = " :: " + qualification; qualification = qualTok->str() + qualification; qualTok = qualTok->tokAt(-2); } return qualification; } } // namespace bool Tokenizer::isMemberFunction(const Token *openParen) const { return (Token::Match(openParen->tokAt(-2), ":: %name% (") || Token::Match(openParen->tokAt(-3), ":: ~ %name% (")) && isFunctionHead(openParen, "{|:"); } bool Tokenizer::simplifyUsing() { bool substitute = false; std::list scopeList; struct Using { Using(Token *start, Token *end) : startTok(start), endTok(end) { } Token *startTok; Token *endTok; }; std::list usingList; scopeList.emplace_back("", nullptr); for (Token *tok = list.front(); tok; tok = tok->next()) { if (mErrorLogger && !list.getFiles().empty()) mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (using)", tok->progressValue()); if (Settings::terminated()) return substitute; if (Token::Match(tok, "{|}|namespace|class|struct|union") || Token::Match(tok, "using namespace %name% ;|::")) { setScopeInfo(tok, &scopeList); } // skip template declarations if (Token::Match(tok, "template < !!>")) { Token *endToken = TemplateSimplifier::findTemplateDeclarationEnd(tok); if (endToken) tok = endToken; continue; } // look for non-template type aliases if (!(tok->strAt(-1) != ">" && (Token::Match(tok, "using %name% = ::| %name%") || (Token::Match(tok, "using %name% [ [") && Token::Match(tok->linkAt(2), "] ] = ::| %name%"))))) continue; std::list scopeList1; scopeList1.emplace_back("", nullptr); std::string name = tok->strAt(1); const Token *nameToken = tok->next(); std::string scope = getScopeName(scopeList); Token *usingStart = tok; Token *start; if (tok->strAt(2) == "=") start = tok->tokAt(3); else start = tok->linkAt(2)->tokAt(3); Token *usingEnd = findSemicolon(start); if (!usingEnd) continue; // Move struct defined in using out of using. // using T = struct t { }; => struct t { }; using T = struct t; // fixme: this doesn't handle attributes if (Token::Match(start, "struct|union|enum %name%| {")) { if (start->strAt(1) != "{") { Token *structEnd = start->linkAt(2); structEnd->insertToken(";", ""); TokenList::copyTokens(structEnd->next(), tok, start->next()); usingStart = structEnd->tokAt(2); nameToken = usingStart->next(); if (usingStart->strAt(2) == "=") start = usingStart->tokAt(3); else start = usingStart->linkAt(2)->tokAt(3); usingEnd = findSemicolon(start); tok->deleteThis(); tok->deleteThis(); tok->deleteThis(); tok = usingStart; } else { Token *structEnd = start->linkAt(1); structEnd->insertToken(";", ""); std::string newName; if (structEnd->strAt(2) == ";") newName = name; else newName = "Unnamed" + MathLib::toString(mUnnamedCount++); TokenList::copyTokens(structEnd->next(), tok, start); structEnd->tokAt(5)->insertToken(newName, ""); start->insertToken(newName, ""); usingStart = structEnd->tokAt(2); nameToken = usingStart->next(); if (usingStart->strAt(2) == "=") start = usingStart->tokAt(3); else start = usingStart->linkAt(2)->tokAt(3); usingEnd = findSemicolon(start); tok->deleteThis(); tok->deleteThis(); tok->deleteThis(); tok = usingStart; } } // remove 'typename' and 'template' else if (start->str() == "typename") { start->deleteThis(); Token *temp = start; while (Token::Match(temp, "%name% ::")) temp = temp->tokAt(2); if (Token::Match(temp, "template %name%")) temp->deleteThis(); } // Unfortunately we have to start searching from the beginning // of the token stream because templates are instantiated at // the end of the token stream and it may be used before then. std::string scope1; bool skip = false; // don't erase type aliases we can't parse for (Token* tok1 = list.front(); tok1; tok1 = tok1->next()) { if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") || Token::Match(tok1, "using namespace %name% ;|::")) { setScopeInfo(tok1, &scopeList1, true); scope1 = getScopeName(scopeList1); continue; } // skip template definitions if (Token::Match(tok1, "template < !!>")) { Token *endToken = TemplateSimplifier::findTemplateDeclarationEnd(tok1); if (endToken) tok1 = endToken; continue; } // check for member function and adjust scope if (isMemberFunction(tok1)) { if (!scope1.empty()) scope1 += " :: "; scope1 += memberFunctionScope(tok1); } if (!usingMatch(nameToken, scope, &tok1, scope1, scopeList1)) continue; // remove the qualification std::string fullScope = scope; while (tok1->strAt(-1) == "::") { if (fullScope == tok1->strAt(-2)) { tok1->deletePrevious(); tok1->deletePrevious(); break; } else { const std::string::size_type idx = fullScope.rfind(" "); if (idx == std::string::npos) break; if (tok1->strAt(-2) == fullScope.substr(idx + 1)) { tok1->deletePrevious(); tok1->deletePrevious(); fullScope.resize(idx - 3); } else break; } } Token * arrayStart = nullptr; // parse the type Token *type = start; if (type->str() == "::") { type = type->next(); while (Token::Match(type, "%type% ::")) type = type->tokAt(2); if (Token::Match(type, "%type%")) type = type->next(); } else if (Token::Match(type, "%type% ::")) { do { type = type->tokAt(2); } while (Token::Match(type, "%type% ::")); if (Token::Match(type, "%type%")) type = type->next(); } else if (Token::Match(type, "%type%")) { while (Token::Match(type, "const|struct|union|enum %type%") || (type->next() && type->next()->isStandardType())) type = type->next(); type = type->next(); while (Token::Match(type, "%type%") && (type->isStandardType() || Token::Match(type, "unsigned|signed"))) { type = type->next(); } bool atEnd = false; while (!atEnd) { if (type && type->str() == "::") { type = type->next(); } if (Token::Match(type, "%type%") && type->next() && !Token::Match(type->next(), "[|;|,|(")) { type = type->next(); } else if (Token::simpleMatch(type, "const (")) { type = type->next(); atEnd = true; } else atEnd = true; } } else syntaxError(type); // check for invalid input if (!type) syntaxError(tok1); // check for template if (type->str() == "<") { type = type->findClosingBracket(); while (type && Token::Match(type->next(), ":: %type%")) type = type->tokAt(2); if (!type) { syntaxError(tok1); } while (Token::Match(type->next(), "const|volatile")) type = type->next(); type = type->next(); } // check for pointers and references std::list pointers; while (Token::Match(type, "*|&|&&|const")) { pointers.push_back(type->str()); type = type->next(); } // check for array if (type && type->str() == "[") { do { if (!arrayStart) arrayStart = type; bool atEnd = false; while (!atEnd) { while (type->next() && !Token::Match(type->next(), ";|,")) { type = type->next(); } if (!type->next()) syntaxError(type); // invalid input else if (type->next()->str() == ";") atEnd = true; else if (type->str() == "]") atEnd = true; else type = type->next(); } type = type->next(); } while (type && type->str() == "["); } Token* after = tok1->next(); // check if type was parsed if (type && type == usingEnd) { // check for array syntax and add type around variable if (arrayStart) { if (Token::Match(tok1->next(), "%name%")) { TokenList::copyTokens(tok1->next(), arrayStart, usingEnd->previous()); TokenList::copyTokens(tok1, start, arrayStart->previous()); tok1->deleteThis(); substitute = true; } } else { // just replace simple type aliases TokenList::copyTokens(tok1, start, usingEnd->previous()); tok1->deleteThis(); substitute = true; } } else { skip = true; if (mSettings->debugwarnings && mErrorLogger) { std::string str; for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) { if (!str.empty()) str += ' '; str += tok3->str(); } str += " ;"; std::list callstack(1, usingStart); mErrorLogger->reportErr(ErrorLogger::ErrorMessage(callstack, &list, Severity::debug, "debug", "Failed to parse \'" + str + "\'. The checking continues anyway.", false)); } } tok1 = after; } if (!skip) usingList.emplace_back(usingStart, usingEnd); } // delete all used type alias definitions for (std::list::reverse_iterator it = usingList.rbegin(); it != usingList.rend(); ++it) { Token *usingStart = it->startTok; Token *usingEnd = it->endTok; if (usingStart->previous()) { if (usingEnd->next()) Token::eraseTokens(usingStart->previous(), usingEnd->next()); else { Token::eraseTokens(usingStart->previous(), usingEnd); usingEnd->deleteThis(); } } else { if (usingEnd->next()) { Token::eraseTokens(usingStart, usingEnd->next()); usingStart->deleteThis(); } else { // this is the only code being checked so leave ';' Token::eraseTokens(usingStart, usingEnd); usingStart->deleteThis(); } } } return substitute; } void Tokenizer::simplifyMulAndParens() { if (!list.front()) return; for (Token *tok = list.front()->tokAt(3); tok; tok = tok->next()) { if (!tok->isName()) continue; //fix ticket #2784 - improved by ticket #3184 int closedPars = 0; Token *tokend = tok->next(); Token *tokbegin = tok->previous(); while (tokend && tokend->str() == ")") { ++closedPars; tokend = tokend->next(); } if (!tokend || !(tokend->isAssignmentOp())) continue; while (Token::Match(tokbegin, "&|(")) { if (tokbegin->str() == "&") { if (Token::Match(tokbegin->tokAt(-2), "[;{}&(] *")) { //remove '* &' tokbegin = tokbegin->tokAt(-2); tokbegin->deleteNext(2); } else if (Token::Match(tokbegin->tokAt(-3), "[;{}&(] * (")) { if (closedPars == 0) break; --closedPars; //remove ')' tok->deleteNext(); //remove '* ( &' tokbegin = tokbegin->tokAt(-3); tokbegin->deleteNext(3); } else break; } else if (tokbegin->str() == "(") { if (closedPars == 0) break; //find consecutive opening parentheses int openPars = 0; while (tokbegin && tokbegin->str() == "(" && openPars <= closedPars) { ++openPars; tokbegin = tokbegin->previous(); } if (!tokbegin || openPars > closedPars) break; if ((openPars == closedPars && Token::Match(tokbegin, "[;{}]")) || Token::Match(tokbegin->tokAt(-2), "[;{}&(] * &") || Token::Match(tokbegin->tokAt(-3), "[;{}&(] * ( &")) { //remove the excessive parentheses around the variable while (openPars > 0) { tok->deleteNext(); tokbegin->deleteNext(); --closedPars; --openPars; } } else break; } } } } bool Tokenizer::createTokens(std::istream &code, const std::string& FileName) { // make sure settings specified assert(mSettings); return list.createTokens(code, FileName); } void Tokenizer::createTokens(const simplecpp::TokenList *tokenList) { // make sure settings specified assert(mSettings); list.createTokens(tokenList); } bool Tokenizer::simplifyTokens1(const std::string &configuration) { // Fill the map mTypeSize.. fillTypeSizes(); mConfiguration = configuration; if (!simplifyTokenList1(list.getFiles().front().c_str())) return false; if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::createAst", mSettings->showtime, mTimerResults); list.createAst(); list.validateAst(); } else { list.createAst(); list.validateAst(); } if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::createSymbolDatabase", mSettings->showtime, mTimerResults); createSymbolDatabase(); } else { createSymbolDatabase(); } if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::setValueType", mSettings->showtime, mTimerResults); mSymbolDatabase->setValueTypeInTokenList(true); } else { mSymbolDatabase->setValueTypeInTokenList(true); } if (mTimerResults) { Timer t("Tokenizer::simplifyTokens1::ValueFlow", mSettings->showtime, mTimerResults); ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); } else { ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); } mSymbolDatabase->setArrayDimensionsUsingValueFlow(); printDebugOutput(1); return true; } bool Tokenizer::tokenize(std::istream &code, const char FileName[], const std::string &configuration) { if (!createTokens(code, FileName)) return false; return simplifyTokens1(configuration); } //--------------------------------------------------------------------------- void Tokenizer::findComplicatedSyntaxErrorsInTemplates() { validate(); mTemplateSimplifier->checkComplicatedSyntaxErrorsInTemplates(); } void Tokenizer::checkForEnumsWithTypedef() { for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "enum %name% {")) { tok = tok->tokAt(2); const Token *tok2 = Token::findsimplematch(tok, "typedef", tok->link()); if (tok2) syntaxError(tok2); tok = tok->link(); } } } void Tokenizer::fillTypeSizes() { mTypeSize.clear(); mTypeSize["char"] = 1; mTypeSize["_Bool"] = mSettings->sizeof_bool; mTypeSize["bool"] = mSettings->sizeof_bool; mTypeSize["short"] = mSettings->sizeof_short; mTypeSize["int"] = mSettings->sizeof_int; mTypeSize["long"] = mSettings->sizeof_long; mTypeSize["float"] = mSettings->sizeof_float; mTypeSize["double"] = mSettings->sizeof_double; mTypeSize["wchar_t"] = mSettings->sizeof_wchar_t; mTypeSize["size_t"] = mSettings->sizeof_size_t; mTypeSize["*"] = mSettings->sizeof_pointer; } void Tokenizer::combineOperators() { const bool cpp = isCPP(); // Combine tokens.. for (Token *tok = list.front(); tok && tok->next(); tok = tok->next()) { const char c1 = tok->str()[0]; if (tok->str().length() == 1 && tok->next()->str().length() == 1) { const char c2 = tok->next()->str()[0]; // combine +-*/ and = if (c2 == '=' && (std::strchr("+-*/%|^=!<>", c1))) { // skip templates if (cpp && tok->str() == ">") { const Token *opening = tok->findOpeningBracket(); if (opening) { if (Token::Match(opening->previous(), "%name%")) continue; } } tok->str(tok->str() + c2); tok->deleteNext(); continue; } } else if (tok->next()->str() == "=") { if (tok->str() == ">>") { tok->str(">>="); tok->deleteNext(); } else if (tok->str() == "<<") { tok->str("<<="); tok->deleteNext(); } } else if (cpp && (c1 == 'p' || c1 == '_') && Token::Match(tok, "private|protected|public|__published : !!:")) { bool simplify = false; int par = 0; for (const Token *prev = tok->previous(); prev; prev = prev->previous()) { if (prev->str() == ")") { ++par; } else if (prev->str() == "(") { if (par == 0U) break; --par; } if (par != 0U || prev->str() == "(") continue; if (Token::Match(prev, "[;{}]")) { simplify = true; break; } if (prev->isName() && prev->isUpperCaseName()) continue; if (prev->isName() && endsWith(prev->str(), ':')) simplify = true; break; } if (simplify) { tok->str(tok->str() + ":"); tok->deleteNext(); } } else if (tok->str() == "->") { // If the preceding sequence is "( & %name% )", replace it by "%name%" Token *t = tok->tokAt(-4); if (Token::Match(t, "( & %name% )") && !Token::simpleMatch(t->previous(), ">")) { t->deleteThis(); t->deleteThis(); t->deleteNext(); } tok->str("."); tok->originalName("->"); } } } void Tokenizer::combineStringAndCharLiterals() { // Combine strings for (Token *tok = list.front(); tok; tok = tok->next()) { if (!isStringLiteral(tok->str())) continue; tok->str(simplifyString(tok->str())); while (tok->next() && tok->next()->tokType() == Token::eString) { // Two strings after each other, combine them tok->concatStr(simplifyString(tok->next()->str())); tok->deleteNext(); } } } void Tokenizer::concatenateNegativeNumberAndAnyPositive() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "?|:|,|(|[|{|return|case|sizeof|%op% +|-") || tok->tokType() == Token::eIncDecOp) continue; while (tok->str() != ">" && tok->next() && tok->next()->str() == "+") tok->deleteNext(); if (Token::Match(tok->next(), "- %num%")) { tok->deleteNext(); tok->next()->str("-" + tok->next()->str()); } } } void Tokenizer::simplifyExternC() { if (isC()) return; // Add attributes to all tokens within `extern "C"` inlines and blocks, and remove the `extern "C"` tokens. for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "extern \"C\"")) { Token *tok2 = tok->next(); if (tok->strAt(2) == "{") { tok2 = tok2->next(); // skip { while ((tok2 = tok2->next()) && tok2 != tok->linkAt(2)) tok2->isExternC(true); tok->linkAt(2)->deleteThis(); // } tok->deleteNext(2); // "C" { } else { while ((tok2 = tok2->next()) && !Token::simpleMatch(tok2, ";")) tok2->isExternC(true); tok->deleteNext(); // "C" } tok->deleteThis(); // extern } } } void Tokenizer::simplifyRoundCurlyParentheses() { for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::Match(tok, "[;{}:] ( {") && Token::simpleMatch(tok->linkAt(2), "} ) ;")) { if (tok->str() == ":" && !Token::Match(tok->tokAt(-2),"[;{}] %type% :")) break; Token *end = tok->linkAt(2)->tokAt(-3); if (Token::Match(end, "[;{}] %num%|%str% ;")) end->deleteNext(2); tok->linkAt(2)->previous()->deleteNext(3); tok->deleteNext(2); } if (Token::Match(tok, "( { %bool%|%char%|%num%|%str%|%name% ; } )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(3); } } } void Tokenizer::simplifySQL() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL")) continue; const Token *end = findSQLBlockEnd(tok); if (end == nullptr) syntaxError(nullptr); const std::string instruction = tok->stringifyList(end); // delete all tokens until the embedded SQL block end Token::eraseTokens(tok, end); // insert "asm ( "instruction" ) ;" tok->str("asm"); // it can happen that 'end' is NULL when wrong code is inserted if (!tok->next()) tok->insertToken(";"); tok->insertToken(")"); tok->insertToken("\"" + instruction + "\""); tok->insertToken("("); // jump to ';' and continue tok = tok->tokAt(3); } } void Tokenizer::simplifyArrayAccessSyntax() { // 0[a] -> a[0] for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isNumber() && Token::Match(tok, "%num% [ %name% ]")) { const std::string number(tok->str()); Token* indexTok = tok->tokAt(2); tok->str(indexTok->str()); tok->varId(indexTok->varId()); indexTok->str(number); } } } void Tokenizer::simplifyParameterVoid() { for (Token* tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%name% ( void )")) tok->next()->deleteNext(); } } void Tokenizer::simplifyRedundantConsecutiveBraces() { // Remove redundant consecutive braces, i.e. '.. { { .. } } ..' -> '.. { .. } ..'. for (Token *tok = list.front(); tok;) { if (Token::simpleMatch(tok, "= {")) { tok = tok->linkAt(1); } else if (Token::simpleMatch(tok, "{ {") && Token::simpleMatch(tok->next()->link(), "} }")) { //remove internal parentheses tok->next()->link()->deleteThis(); tok->deleteNext(); } else tok = tok->next(); } } void Tokenizer::simplifyDoublePlusAndDoubleMinus() { // Convert - - into + and + - into - for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->next()) { if (tok->str() == "+") { if (tok->next()->str()[0] == '-') { tok = tok->next(); if (tok->str().size() == 1) { tok = tok->previous(); tok->str("-"); tok->deleteNext(); } else if (tok->isNumber()) { tok->str(tok->str().substr(1)); tok = tok->previous(); tok->str("-"); } continue; } } else if (tok->str() == "-") { if (tok->next()->str()[0] == '-') { tok = tok->next(); if (tok->str().size() == 1) { tok = tok->previous(); tok->str("+"); tok->deleteNext(); } else if (tok->isNumber()) { tok->str(tok->str().substr(1)); tok = tok->previous(); tok->str("+"); } continue; } } break; } } } /** Specify array size if it hasn't been given */ void Tokenizer::arraySize() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName() || !Token::Match(tok, "%var% [ ] =")) continue; bool addlength = false; if (Token::Match(tok, "%var% [ ] = { %str% } ;")) { Token *t = tok->tokAt(3); t->deleteNext(); t->next()->deleteNext(); addlength = true; } if (addlength || Token::Match(tok, "%var% [ ] = %str% ;")) { tok = tok->next(); const int sz = Token::getStrArraySize(tok->tokAt(3)); tok->insertToken(MathLib::toString(sz)); tok = tok->tokAt(5); } else if (Token::Match(tok, "%var% [ ] = {")) { MathLib::biguint sz = 1; tok = tok->next(); Token *end = tok->linkAt(3); for (Token *tok2 = tok->tokAt(4); tok2 && tok2 != end; tok2 = tok2->next()) { if (tok2->link() && Token::Match(tok2, "{|(|[|<")) { if (tok2->str() == "[" && tok2->link()->strAt(1) == "=") { // designated initializer if (Token::Match(tok2, "[ %num% ]")) sz = std::max(sz, MathLib::toULongNumber(tok2->strAt(1)) + 1U); else { sz = 0; break; } } tok2 = tok2->link(); } else if (tok2->str() == ",") { if (!Token::Match(tok2->next(), "[},]")) ++sz; else { tok2 = tok2->previous(); tok2->deleteNext(); } } } if (sz != 0) tok->insertToken(MathLib::toString(sz)); tok = end->next() ? end->next() : end; } } } static Token *skipTernaryOp(Token *tok) { int colonLevel = 1; while (nullptr != (tok = tok->next())) { if (tok->str() == "?") { ++colonLevel; } else if (tok->str() == ":") { --colonLevel; if (colonLevel == 0) { tok = tok->next(); break; } } if (tok->link() && Token::Match(tok, "[(<]")) tok = tok->link(); else if (Token::Match(tok->next(), "[{};)]")) break; } if (colonLevel > 0) // Ticket #5214: Make sure the ':' matches the proper '?' return nullptr; return tok; } const Token * Tokenizer::startOfExecutableScope(const Token * tok) { if (tok->str() != ")") return nullptr; tok = isFunctionHead(tok, ":{", true); if (Token::Match(tok, ": %name% [({]")) { while (Token::Match(tok, "[:,] %name% [({]")) tok = tok->linkAt(2)->next(); } return (tok && tok->str() == "{") ? tok : nullptr; } /** simplify labels and case|default in the code: add a ";" if not already in.*/ void Tokenizer::simplifyLabelsCaseDefault() { const bool cpp = isCPP(); bool executablescope = false; int indentLevel = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { // Simplify labels in the executable scope.. Token *start = const_cast(startOfExecutableScope(tok)); if (start) { tok = start; executablescope = true; } if (!executablescope) continue; if (tok->str() == "{") { if (tok->previous()->str() == "=") tok = tok->link(); else ++indentLevel; } else if (tok->str() == "}") { --indentLevel; if (indentLevel == 0) { executablescope = false; continue; } } else if (Token::Match(tok, "(|[")) tok = tok->link(); if (Token::Match(tok, "[;{}:] case")) { while (nullptr != (tok = tok->next())) { if (Token::Match(tok, "(|[")) { tok = tok->link(); } else if (tok->str() == "?") { Token *tok1 = skipTernaryOp(tok); if (!tok1) { syntaxError(tok); } tok = tok1; } if (Token::Match(tok->next(),"[:{};]")) break; } if (!tok) break; if (tok->str() != "case" && tok->next() && tok->next()->str() == ":") { tok = tok->next(); if (!tok->next()) syntaxError(tok); if (tok->next()->str() != ";" && tok->next()->str() != "case") tok->insertToken(";"); else tok = tok->previous(); } else { syntaxError(tok); } } else if (Token::Match(tok, "[;{}] %name% : !!;")) { if (!cpp || !Token::Match(tok->next(), "class|struct|enum")) { tok = tok->tokAt(2); tok->insertToken(";"); } } } } void Tokenizer::simplifyCaseRange() { for (Token* tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "case %num% ... %num% :")) { const MathLib::bigint start = MathLib::toLongNumber(tok->strAt(1)); MathLib::bigint end = MathLib::toLongNumber(tok->strAt(3)); end = std::min(start + 50, end); // Simplify it 50 times at maximum if (start < end) { tok = tok->tokAt(2); tok->str(":"); tok->insertToken("case"); for (MathLib::bigint i = end-1; i > start; i--) { tok->insertToken(":"); tok->insertToken(MathLib::toString(i)); tok->insertToken("case"); } } } else if (Token::Match(tok, "case %char% ... %char% :")) { const char start = tok->strAt(1)[1]; const char end = tok->strAt(3)[1]; if (start < end) { tok = tok->tokAt(2); tok->str(":"); tok->insertToken("case"); for (char i = end - 1; i > start; i--) { tok->insertToken(":"); if (i == '\\') { tok->insertToken(std::string("\'\\") + i + '\''); } else { tok->insertToken(std::string(1, '\'') + i + '\''); } tok->insertToken("case"); } } } } } void Tokenizer::calculateScopes() { for (auto tok = list.front(); tok; tok = tok->next()) tok->scopeInfo(nullptr); std::string nextScopeNameAddition; std::shared_ptr primaryScope = std::make_shared("", nullptr); list.front()->scopeInfo(primaryScope); for (Token* tok = list.front(); tok; tok = tok->next()) { if (tok == list.front() || !tok->scopeInfo()) { if (tok != list.front()) tok->scopeInfo(tok->previous()->scopeInfo()); if (Token::Match(tok, "using namespace %name% ::|<|;")) { std::string usingNamespaceName; for (const Token* namespaceNameToken = tok->tokAt(2); namespaceNameToken && namespaceNameToken->str() != ";"; namespaceNameToken = namespaceNameToken->next()) { usingNamespaceName += namespaceNameToken->str(); usingNamespaceName += " "; } if (usingNamespaceName.length() > 0) usingNamespaceName = usingNamespaceName.substr(0, usingNamespaceName.length() - 1); tok->scopeInfo()->usingNamespaces.insert(usingNamespaceName); } else if (Token::Match(tok, "namespace|class|struct|union %name% {|::|:|<")) { for (Token* nameTok = tok->next(); nameTok && !Token::Match(nameTok, "{|:"); nameTok = nameTok->next()) { if (Token::Match(nameTok, ";|<")) { nextScopeNameAddition = ""; break; } nextScopeNameAddition.append(nameTok->str()); nextScopeNameAddition.append(" "); } if (nextScopeNameAddition.length() > 0) nextScopeNameAddition = nextScopeNameAddition.substr(0, nextScopeNameAddition.length() - 1); } if (Token::simpleMatch(tok, "{")) { // This might be the opening of a member function Token *tok1 = tok; while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1 && tok1->previous() && tok1->strAt(-1) == ")") { bool member = true; tok1 = tok1->linkAt(-1); if (Token::Match(tok1->previous(), "throw|noexcept")) { tok1 = tok1->previous(); while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) tok1 = tok1->previous(); if (tok1->strAt(-1) != ")") member = false; } else if (Token::Match(tok->tokAt(-2), ":|, %name%")) { tok1 = tok1->tokAt(-2); if (tok1->strAt(-1) != ")") member = false; } if (member) { if (tok1->strAt(-1) == ">") tok1 = tok1->previous()->findOpeningBracket(); if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) { tok1 = tok1->tokAt(-2); std::string scope = tok1->strAt(-1); while (Token::Match(tok1->tokAt(-2), ":: %name%")) { scope = tok1->strAt(-3) + " :: " + scope; tok1 = tok1->tokAt(-2); } if (!nextScopeNameAddition.empty() && !scope.empty()) nextScopeNameAddition += " :: "; nextScopeNameAddition += scope; } } } // New scope is opening, record it here std::shared_ptr newScopeInfo = std::make_shared(tok->scopeInfo()->name, tok->link(), tok->scopeInfo()->usingNamespaces); if (newScopeInfo->name != "" && nextScopeNameAddition != "") newScopeInfo->name.append(" :: "); newScopeInfo->name.append(nextScopeNameAddition); nextScopeNameAddition = ""; if (tok->link()) tok->link()->scopeInfo(tok->scopeInfo()); tok->scopeInfo(newScopeInfo); } } } } void Tokenizer::simplifyTemplates() { if (isC()) return; mTemplateSimplifier->simplifyTemplates( #ifdef MAXTIME mMaxTime, #else 0, // ignored #endif mCodeWithTemplates); } //--------------------------------------------------------------------------- static bool setVarIdParseDeclaration(const Token **tok, const std::map &variableId, bool executableScope, bool cpp, bool c) { const Token *tok2 = *tok; if (!tok2->isName()) return false; int typeCount = 0; int singleNameCount = 0; bool hasstruct = false; // Is there a "struct" or "class"? bool bracket = false; bool ref = false; while (tok2) { if (tok2->isName()) { if (cpp && Token::Match(tok2, "namespace|public|private|protected")) return false; if (Token::Match(tok2, "struct|union|enum") || (!c && Token::Match(tok2, "class|typename"))) { hasstruct = true; typeCount = 0; singleNameCount = 0; } else if (tok2->str() == "const") { ; // just skip "const" } else if (!hasstruct && variableId.find(tok2->str()) != variableId.end() && tok2->previous()->str() != "::") { ++typeCount; tok2 = tok2->next(); if (!tok2 || tok2->str() != "::") break; } else { if (tok2->str() != "void" || Token::Match(tok2, "void const| *|(")) // just "void" cannot be a variable type ++typeCount; ++singleNameCount; } } else if (!c && ((TemplateSimplifier::templateParameters(tok2) > 0) || Token::simpleMatch(tok2, "< >") /* Ticket #4764 */)) { const Token *start = *tok; if (Token::Match(start->previous(), "%or%|%oror%|&&|&|^|+|-|*|/")) return false; const Token * const closingBracket = tok2->findClosingBracket(); if (closingBracket == nullptr) { /* Ticket #8151 */ throw tok2; } tok2 = closingBracket; if (tok2->str() != ">") break; singleNameCount = 1; if (Token::Match(tok2, "> %name% %or%|%oror%|&&|&|^|+|-|*|/") && !Token::Match(tok2, "> const [*&]")) return false; if (Token::Match(tok2, "> %name% )")) { if (Token::Match(tok2->linkAt(2)->previous(), "if|for|while (")) return false; if (!Token::Match(tok2->linkAt(2)->previous(), "%name% (")) return false; } } else if (Token::Match(tok2, "&|&&")) { ref = !bracket; } else if (singleNameCount == 1 && Token::Match(tok2, "( [*&]") && Token::Match(tok2->link()->next(), "(|[")) { bracket = true; // Skip: Seems to be valid pointer to array or function pointer } else if (tok2->str() == "::") { singleNameCount = 0; } else if (tok2->str() != "*" && tok2->str() != "::" && tok2->str() != "...") { break; } tok2 = tok2->next(); } if (tok2) { bool isLambdaArg = false; { const Token *tok3 = (*tok)->previous(); if (tok3 && tok3->str() == ",") { while (tok3 && !Token::Match(tok3,";|(|[|{")) { if (Token::Match(tok3, ")|]")) tok3 = tok3->link(); tok3 = tok3->previous(); } if (tok3 && executableScope && Token::Match(tok3->previous(), "%name% (")) { const Token *fdecl = tok3->previous(); int count = 0; while (Token::Match(fdecl, "%name%|*")) { fdecl = fdecl->previous(); count++; } if (!Token::Match(fdecl, "[;{}] %name%") || count <= 1) return false; } } if (cpp && tok3 && Token::simpleMatch(tok3->previous(), "] (") && Token::simpleMatch(tok3->link(), ") {")) isLambdaArg = true; } *tok = tok2; // In executable scopes, references must be assigned // Catching by reference is an exception if (executableScope && ref && !isLambdaArg) { if (Token::Match(tok2, "(|=|{|:")) ; // reference is assigned => ok else if (tok2->str() != ")" || tok2->link()->strAt(-1) != "catch") return false; // not catching by reference => not declaration } } // Check if array declaration is valid (#2638) // invalid declaration: AAA a[4] = 0; if (typeCount >= 2 && executableScope && tok2 && tok2->str() == "[") { const Token *tok3 = tok2->link()->next(); while (tok3 && tok3->str() == "[") { tok3 = tok3->link()->next(); } if (Token::Match(tok3, "= %num%")) return false; } return (typeCount >= 2 && tok2 && Token::Match(tok2->tokAt(-2), "!!:: %type%")); } void Tokenizer::setVarIdStructMembers(Token **tok1, std::map >& structMembers, nonneg int *varId) { Token *tok = *tok1; if (Token::Match(tok, "%name% = { . %name% =")) { const int struct_varid = tok->varId(); if (struct_varid == 0) return; std::map& members = structMembers[struct_varid]; tok = tok->tokAt(3); while (tok->str() != "}") { if (Token::Match(tok, "{|[|(")) tok = tok->link(); if (Token::Match(tok->previous(), "[,{] . %name% =")) { tok = tok->next(); const std::map::iterator it = members.find(tok->str()); if (it == members.end()) { members[tok->str()] = ++(*varId); tok->varId(*varId); } else { tok->varId(it->second); } } tok = tok->next(); } return; } while (Token::Match(tok->next(), ")| . %name% !!(")) { // Don't set varid for trailing return type if (tok->strAt(1) == ")" && tok->linkAt(1)->previous()->isName() && isFunctionHead(tok->linkAt(1), "{|;")) { tok = tok->tokAt(3); continue; } const int struct_varid = tok->varId(); tok = tok->tokAt(2); if (struct_varid == 0) continue; if (tok->str() == ".") tok = tok->next(); // Don't set varid for template function if (TemplateSimplifier::templateParameters(tok->next()) > 0) break; std::map& members = structMembers[struct_varid]; const std::map::iterator it = members.find(tok->str()); if (it == members.end()) { members[tok->str()] = ++(*varId); tok->varId(*varId); } else { tok->varId(it->second); } } // tok can't be null *tok1 = tok; } void Tokenizer::setVarIdClassDeclaration(const Token * const startToken, const VariableMap &variableMap, const nonneg int scopeStartVarId, std::map >& structMembers) { // end of scope const Token * const endToken = startToken->link(); // determine class name std::string className; for (const Token *tok = startToken->previous(); tok; tok = tok->previous()) { if (!tok->isName() && tok->str() != ":") break; if (Token::Match(tok, "class|struct|enum %type% [:{]")) { className = tok->next()->str(); break; } } // replace varids.. int indentlevel = 0; bool initList = false; bool inEnum = false; const Token *initListArgLastToken = nullptr; for (Token *tok = startToken->next(); tok != endToken; tok = tok->next()) { if (!tok) syntaxError(nullptr); if (initList) { if (tok == initListArgLastToken) initListArgLastToken = nullptr; else if (!initListArgLastToken && Token::Match(tok->previous(), "%name%|>|>> {|(") && Token::Match(tok->link(), "}|) ,|{")) initListArgLastToken = tok->link(); } if (tok->str() == "{") { inEnum = isEnumStart(tok); if (initList && !initListArgLastToken) initList = false; ++indentlevel; } else if (tok->str() == "}") { --indentlevel; inEnum = false; } else if (initList && indentlevel == 0 && Token::Match(tok->previous(), "[,:] %name% [({]")) { const std::map::const_iterator it = variableMap.find(tok->str()); if (it != variableMap.end()) { tok->varId(it->second); } } else if (tok->isName() && tok->varId() <= scopeStartVarId) { if (indentlevel > 0 || initList) { if (Token::Match(tok->previous(), "::|.") && tok->strAt(-2) != "this" && !Token::simpleMatch(tok->tokAt(-5), "( * this ) .")) continue; if (!tok->next()) syntaxError(nullptr); if (tok->next()->str() == "::") { if (tok->str() == className) tok = tok->tokAt(2); else continue; } if (!inEnum) { const std::map::const_iterator it = variableMap.find(tok->str()); if (it != variableMap.end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); } } } } else if (indentlevel == 0 && tok->str() == ":" && !initListArgLastToken) initList = true; } } // Update the variable ids.. // Parse each function.. void Tokenizer::setVarIdClassFunction(const std::string &classname, Token * const startToken, const Token * const endToken, const std::map &varlist, std::map >& structMembers, nonneg int *varId_) { for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) { if (tok2->varId() != 0 || !tok2->isName()) continue; if (Token::Match(tok2->tokAt(-2), ("!!" + classname + " ::").c_str())) continue; if (Token::Match(tok2->tokAt(-4), "%name% :: %name% ::")) // Currently unsupported continue; if (Token::Match(tok2->tokAt(-2), "!!this .") && !Token::simpleMatch(tok2->tokAt(-5), "( * this ) .")) continue; const std::map::const_iterator it = varlist.find(tok2->str()); if (it != varlist.end()) { tok2->varId(it->second); setVarIdStructMembers(&tok2, structMembers, varId_); } } } void Tokenizer::setVarId() { // Clear all variable ids for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isName()) tok->varId(0); } setPodTypes(); setVarIdPass1(); setVarIdPass2(); } // Variable declarations can't start with "return" etc. #define NOTSTART_C "case", "default", "goto", "NOT", "return", "sizeof", "typedef" static const std::set notstart_c = { NOTSTART_C }; static const std::set notstart_cpp = { NOTSTART_C, "delete", "friend", "new", "throw", "using", "virtual", "explicit", "const_cast", "dynamic_cast", "reinterpret_cast", "static_cast", "template" }; void Tokenizer::setVarIdPass1() { // Variable declarations can't start with "return" etc. const std::set& notstart = (isC()) ? notstart_c : notstart_cpp; VariableMap variableMap; std::map > structMembers; std::stack scopeStack; scopeStack.push(VarIdScopeInfo()); std::stack functionDeclEndStack; const Token *functionDeclEndToken = nullptr; bool initlist = false; bool inlineFunction = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isOp()) continue; if (tok == functionDeclEndToken) { functionDeclEndStack.pop(); functionDeclEndToken = functionDeclEndStack.empty() ? nullptr : functionDeclEndStack.top(); if (tok->str() == ":") initlist = true; else if (tok->str() == ";") { if (!variableMap.leaveScope()) cppcheckError(tok); } else if (tok->str() == "{") scopeStack.push(VarIdScopeInfo(true, scopeStack.top().isStructInit || tok->strAt(-1) == "=", /*isEnum=*/false, *variableMap.getVarId())); } else if (!initlist && tok->str()=="(") { const Token * newFunctionDeclEnd = nullptr; if (!scopeStack.top().isExecutable) newFunctionDeclEnd = isFunctionHead(tok, "{:;"); else { Token const * const tokenLinkNext = tok->link()->next(); if (tokenLinkNext && tokenLinkNext->str() == "{") // might be for- or while-loop or if-statement newFunctionDeclEnd = tokenLinkNext; } if (newFunctionDeclEnd && newFunctionDeclEnd != functionDeclEndToken) { functionDeclEndStack.push(newFunctionDeclEnd); functionDeclEndToken = newFunctionDeclEnd; variableMap.enterScope(); } } else if (Token::Match(tok, "{|}")) { inlineFunction = false; const Token * const startToken = (tok->str() == "{") ? tok : tok->link(); // parse anonymous unions as part of the current scope if (!Token::Match(startToken->previous(), "union|struct|enum {") && !(initlist && Token::Match(startToken->previous(), "%name%|>|>>") && Token::Match(startToken->link(), "} ,|{"))) { if (tok->str() == "{") { bool isExecutable; const Token *prev = tok->previous(); while (Token::Match(prev, "%name%|.")) prev = prev->previous(); const bool isLambda = prev && prev->str() == ")" && Token::simpleMatch(prev->link()->previous(), "] ("); if ((!isLambda && (tok->strAt(-1) == ")" || Token::Match(tok->tokAt(-2), ") %type%"))) || (initlist && tok->strAt(-1) == "}")) { isExecutable = true; } else { isExecutable = ((scopeStack.top().isExecutable || initlist || tok->strAt(-1) == "else") && !isClassStructUnionEnumStart(tok)); if (!(scopeStack.top().isStructInit || tok->strAt(-1) == "=")) variableMap.enterScope(); } initlist = false; scopeStack.push(VarIdScopeInfo(isExecutable, scopeStack.top().isStructInit || tok->strAt(-1) == "=", isEnumStart(tok), *variableMap.getVarId())); } else { /* if (tok->str() == "}") */ bool isNamespace = false; for (const Token *tok1 = tok->link()->previous(); tok1 && tok1->isName(); tok1 = tok1->previous()) { if (tok1->str() == "namespace") { isNamespace = true; break; } } // Set variable ids in class declaration.. if (!initlist && !isC() && !scopeStack.top().isExecutable && tok->link() && !isNamespace) { setVarIdClassDeclaration(tok->link(), variableMap, scopeStack.top().startVarid, structMembers); } if (!scopeStack.top().isStructInit) { variableMap.leaveScope(); } scopeStack.pop(); if (scopeStack.empty()) { // should be impossible scopeStack.push(VarIdScopeInfo()); } } } } if (!scopeStack.top().isStructInit && (tok == list.front() || Token::Match(tok, "[;{}]") || (tok->str() == "(" && isFunctionHead(tok,"{")) || (tok->str() == "(" && !scopeStack.top().isExecutable && isFunctionHead(tok,";:")) || (tok->str() == "," && (!scopeStack.top().isExecutable || inlineFunction)) || (tok->isName() && endsWith(tok->str(), ':')))) { // No variable declarations in sizeof if (Token::simpleMatch(tok->previous(), "sizeof (")) { continue; } if (Settings::terminated()) return; // locate the variable name.. const Token *tok2 = (tok->isName()) ? tok : tok->next(); // private: protected: public: etc while (tok2 && endsWith(tok2->str(), ':')) { tok2 = tok2->next(); } if (!tok2) break; // Variable declaration can't start with "return", etc if (notstart.find(tok2->str()) != notstart.end()) continue; if (!isC() && Token::simpleMatch(tok2, "const new")) continue; bool decl; try { /* Ticket #8151 */ decl = setVarIdParseDeclaration(&tok2, variableMap.map(), scopeStack.top().isExecutable, isCPP(), isC()); } catch (const Token * errTok) { syntaxError(errTok); } if (decl) { if (tok->str() == "(" && isFunctionHead(tok,"{") && scopeStack.top().isExecutable) inlineFunction = true; const Token* prev2 = tok2->previous(); if (Token::Match(prev2, "%type% [;[=,)]") && tok2->previous()->str() != "const") ; else if (Token::Match(prev2, "%type% :") && tok->strAt(-1) == "for") ; else if (Token::Match(prev2, "%type% ( !!)") && Token::simpleMatch(tok2->link(), ") ;")) { // In C++ , a variable can't be called operator+ or something like that. if (isCPP() && prev2->isOperatorKeyword()) continue; const Token *tok3 = tok2->next(); if (!tok3->isStandardType() && tok3->str() != "void" && !Token::Match(tok3, "struct|union|class %type%") && tok3->str() != "." && !Token::Match(tok2->link()->previous(), "[&*]")) { if (!scopeStack.top().isExecutable) { // Detecting initializations with () in non-executable scope is hard and often impossible to be done safely. Thus, only treat code as a variable that definitely is one. decl = false; bool rhs = false; for (; tok3; tok3 = tok3->nextArgumentBeforeCreateLinks2()) { if (tok3->str() == "=") { rhs = true; continue; } if (tok3->str() == ",") { rhs = false; continue; } if (rhs) continue; if (tok3->isLiteral() || (tok3->isName() && variableMap.hasVariable(tok3->str())) || tok3->isOp() || tok3->str() == "(" || notstart.find(tok3->str()) != notstart.end()) { decl = true; break; } } } } else decl = false; } else if (isCPP() && Token::Match(prev2, "%type% {") && Token::simpleMatch(tok2->link(), "} ;")) { // C++11 initialization style if (Token::Match(prev2, "do|try|else") || Token::Match(prev2->tokAt(-2), "struct|class|:")) continue; } else decl = false; if (decl) { variableMap.addVariable(prev2->str()); // set varid for template parameters.. tok = tok->next(); while (Token::Match(tok, "%name%|::")) tok = tok->next(); if (tok && tok->str() == "<") { const Token *end = tok->findClosingBracket(); while (tok != end) { if (tok->isName()) { const std::map::const_iterator it = variableMap.find(tok->str()); if (it != variableMap.end()) tok->varId(it->second); } tok = tok->next(); } } tok = tok2->previous(); } } } if (tok->isName()) { // don't set variable id after a struct|enum|union if (Token::Match(tok->previous(), "struct|enum|union") || (isCPP() && tok->strAt(-1) == "class")) continue; if (!isC()) { if (tok->previous() && tok->previous()->str() == "::") continue; if (tok->next() && tok->next()->str() == "::") continue; if (Token::simpleMatch(tok->tokAt(-2), ":: template")) continue; } // function declaration inside executable scope? Function declaration is of form: type name "(" args ")" if (scopeStack.top().isExecutable && Token::Match(tok, "%name% [,)]")) { bool par = false; const Token *start, *end; // search begin of function declaration for (start = tok; Token::Match(start, "%name%|*|&|,|("); start = start->previous()) { if (start->str() == "(") { if (par) break; par = true; } if (Token::Match(start, "[(,]")) { if (!Token::Match(start, "[(,] %type% %name%|*|&")) break; } if (start->varId() > 0) break; } // search end of function declaration for (end = tok->next(); Token::Match(end, "%name%|*|&|,"); end = end->next()) {} // there are tokens which can't appear at the begin of a function declaration such as "return" const bool isNotstartKeyword = start->next() && notstart.find(start->next()->str()) != notstart.end(); // now check if it is a function declaration if (Token::Match(start, "[;{}] %type% %name%|*") && par && Token::simpleMatch(end, ") ;") && !isNotstartKeyword) // function declaration => don't set varid continue; } if (!scopeStack.top().isEnum) { const std::map::const_iterator it = variableMap.find(tok->str()); if (it != variableMap.end()) { tok->varId(it->second); setVarIdStructMembers(&tok, structMembers, variableMap.getVarId()); } } } else if (Token::Match(tok, "::|. %name%")) { // Don't set varid after a :: or . token tok = tok->next(); } else if (tok->str() == ":" && Token::Match(tok->tokAt(-2), "class %type%")) { do { tok = tok->next(); } while (tok && (tok->isName() || tok->str() == ",")); if (!tok) break; tok = tok->previous(); } } mVarId = *variableMap.getVarId(); } namespace { struct Member { Member(const std::list &s, const std::list &ns, Token *t) : usingnamespaces(ns), scope(s), tok(t) {} std::list usingnamespaces; std::list scope; Token *tok; }; } static std::string getScopeName(const std::list &scopeInfo) { std::string ret; for (const ScopeInfo2 &si : scopeInfo) ret += (ret.empty() ? "" : " :: ") + (si.name); return ret; } static Token * matchMemberName(const std::list &scope, const Token *nsToken, Token *memberToken, const std::list &scopeInfo) { std::list::const_iterator scopeIt = scopeInfo.begin(); // Current scope.. for (std::list::const_iterator it = scope.begin(); it != scope.end(); ++it) { if (scopeIt == scopeInfo.end() || scopeIt->name != *it) return nullptr; ++scopeIt; } // using namespace.. if (nsToken) { while (Token::Match(nsToken, "%name% ::")) { if (scopeIt != scopeInfo.end() && nsToken->str() == scopeIt->name) { nsToken = nsToken->tokAt(2); ++scopeIt; } else { return nullptr; } } if (!Token::Match(nsToken, "%name% ;")) return nullptr; if (scopeIt == scopeInfo.end() || nsToken->str() != scopeIt->name) return nullptr; ++scopeIt; } // Parse member tokens.. while (scopeIt != scopeInfo.end()) { if (!Token::Match(memberToken, "%name% ::|<")) return nullptr; if (memberToken->str() != scopeIt->name) return nullptr; if (memberToken->next()->str() == "<") { memberToken = memberToken->next()->findClosingBracket(); if (!Token::simpleMatch(memberToken, "> ::")) return nullptr; } memberToken = memberToken->tokAt(2); ++scopeIt; } return Token::Match(memberToken, "~| %name%") ? memberToken : nullptr; } static Token * matchMemberName(const Member &member, const std::list &scopeInfo) { if (scopeInfo.empty()) return nullptr; // Does this member match without "using namespace".. Token *ret = matchMemberName(member.scope, nullptr, member.tok, scopeInfo); if (ret) return ret; // Try to match member using the "using namespace ..." namespaces.. for (const Token *ns : member.usingnamespaces) { ret = matchMemberName(member.scope, ns, member.tok, scopeInfo); if (ret) return ret; } return nullptr; } static Token * matchMemberVarName(const Member &var, const std::list &scopeInfo) { Token *tok = matchMemberName(var, scopeInfo); return Token::Match(tok, "%name% !!(") ? tok : nullptr; } static Token * matchMemberFunctionName(const Member &func, const std::list &scopeInfo) { Token *tok = matchMemberName(func, scopeInfo); return Token::Match(tok, "~| %name% (") ? tok : nullptr; } void Tokenizer::setVarIdPass2() { std::map > structMembers; // Member functions and variables in this source std::list allMemberFunctions; std::list allMemberVars; if (!isC()) { std::map endOfScope; std::list scope; std::list usingnamespaces; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) { if (Token::Match(tok, "using namespace %name% ::|;")) { Token *endtok = tok->tokAt(2); while (Token::Match(endtok, "%name% ::")) endtok = endtok->tokAt(2); if (Token::Match(endtok, "%name% ;")) usingnamespaces.push_back(tok->tokAt(2)); tok = endtok; continue; } else if (Token::Match(tok, "namespace %name% {")) { scope.push_back(tok->strAt(1)); endOfScope[tok->linkAt(2)] = tok->strAt(1); } } if (tok->str() == "}") { const std::map::iterator it = endOfScope.find(tok); if (it != endOfScope.end()) scope.remove(it->second); } Token* const tok1 = tok; if (Token::Match(tok->previous(), "!!:: %name% :: ~| %name%")) tok = tok->next(); else if (Token::Match(tok->previous(), "!!:: %name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) tok = tok->next()->findClosingBracket()->next(); else continue; while (Token::Match(tok, ":: ~| %name%")) { tok = tok->next(); if (tok->str() == "~") tok = tok->next(); else if (Token::Match(tok, "%name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) tok = tok->next()->findClosingBracket()->next(); else if (Token::Match(tok, "%name% ::")) tok = tok->next(); else break; } if (!tok->next()) syntaxError(tok); if (Token::Match(tok, "%name% (")) allMemberFunctions.emplace_back(scope, usingnamespaces, tok1); else allMemberVars.emplace_back(scope, usingnamespaces, tok1); } } std::list scopeInfo; // class members.. std::map > varsByClass; for (Token *tok = list.front(); tok; tok = tok->next()) { while (tok->str() == "}" && !scopeInfo.empty() && tok == scopeInfo.back().bodyEnd) scopeInfo.pop_back(); if (!Token::Match(tok, "namespace|class|struct %name% {|:|::")) continue; const std::string &scopeName(getScopeName(scopeInfo)); const std::string scopeName2(scopeName.empty() ? std::string() : (scopeName + " :: ")); std::list classnameTokens; classnameTokens.push_back(tok->next()); const Token* tokStart = tok->tokAt(2); while (Token::Match(tokStart, ":: %name%")) { classnameTokens.push_back(tokStart->next()); tokStart = tokStart->tokAt(2); } std::string classname; for (const Token *it : classnameTokens) classname += (classname.empty() ? "" : " :: ") + it->str(); std::map &thisClassVars = varsByClass[scopeName2 + classname]; while (Token::Match(tokStart, ":|::|,|%name%")) { if (Token::Match(tokStart, "%name% <")) { tokStart = tokStart->next()->findClosingBracket(); if (tokStart) tokStart = tokStart->next(); continue; } if (Token::Match(tokStart, "%name% ,|{")) { const std::map& baseClassVars = varsByClass[tokStart->str()]; thisClassVars.insert(baseClassVars.begin(), baseClassVars.end()); } tokStart = tokStart->next(); } if (!Token::simpleMatch(tokStart, "{")) continue; // What member variables are there in this class? for (const Token *it : classnameTokens) scopeInfo.emplace_back(it->str(), tokStart->link()); for (Token *tok2 = tokStart->next(); tok2 && tok2 != tokStart->link(); tok2 = tok2->next()) { // skip parentheses.. if (tok2->link()) { if (tok2->str() == "{") { if (tok2->strAt(-1) == ")" || tok2->strAt(-2) == ")") setVarIdClassFunction(scopeName2 + classname, tok2, tok2->link(), thisClassVars, structMembers, &mVarId); tok2 = tok2->link(); } else if (tok2->str() == "(" && tok2->link()->strAt(1) != "(") { tok2 = tok2->link(); // Skip initialization list while (Token::Match(tok2, ") [:,] %name% (")) tok2 = tok2->linkAt(3); } } // Found a member variable.. else if (tok2->varId() > 0) thisClassVars[tok2->str()] = tok2->varId(); } // Are there any member variables in this class? if (thisClassVars.empty()) continue; // Member variables for (const Member &var : allMemberVars) { Token *tok2 = matchMemberVarName(var, scopeInfo); if (!tok2) continue; tok2->varId(thisClassVars[tok2->str()]); } if (isC() || tok->str() == "namespace") continue; // Set variable ids in member functions for this class.. for (const Member &func : allMemberFunctions) { Token *tok2 = matchMemberFunctionName(func, scopeInfo); if (!tok2) continue; if (tok2->str() == "~") tok2 = tok2->linkAt(2); else tok2 = tok2->linkAt(1); // If this is a function implementation.. add it to funclist Token * start = const_cast(isFunctionHead(tok2, "{")); if (start) { setVarIdClassFunction(classname, start, start->link(), thisClassVars, structMembers, &mVarId); } if (Token::Match(tok2, ") %name% (")) tok2 = tok2->linkAt(2); // constructor with initializer list if (!Token::Match(tok2, ") : ::| %name%")) continue; Token *tok3 = tok2; while (Token::Match(tok3, "[)}] [,:]")) { tok3 = tok3->tokAt(2); if (Token::Match(tok3, ":: %name%")) tok3 = tok3->next(); while (Token::Match(tok3, "%name% :: %name%")) tok3 = tok3->tokAt(2); if (!Token::Match(tok3, "%name% (|{|<")) break; // set varid const std::map::const_iterator varpos = thisClassVars.find(tok3->str()); if (varpos != thisClassVars.end()) tok3->varId(varpos->second); // goto end of var if (tok3->strAt(1) == "<") { tok3 = tok3->next()->findClosingBracket(); if (tok3 && tok3->next() && tok3->next()->link()) tok3 = tok3->next()->link(); } else tok3 = tok3->linkAt(1); } if (Token::Match(tok3, ")|} {")) { setVarIdClassFunction(classname, tok2, tok3->next()->link(), thisClassVars, structMembers, &mVarId); } } } } static void linkBrackets(const Tokenizer * const tokenizer, std::stack& type, std::stack& links, Token * const token, const char open, const char close) { if (token->str()[0] == open) { links.push(token); type.push(token); } else if (token->str()[0] == close) { if (links.empty()) { // Error, { and } don't match. tokenizer->unmatchedToken(token); } if (type.top()->str()[0] != open) { tokenizer->unmatchedToken(type.top()); } type.pop(); Token::createMutualLinks(links.top(), token); links.pop(); } } void Tokenizer::createLinks() { std::stack type; std::stack links1; std::stack links2; std::stack links3; for (Token *token = list.front(); token; token = token->next()) { if (token->link()) { token->link(nullptr); } linkBrackets(this, type, links1, token, '{', '}'); linkBrackets(this, type, links2, token, '(', ')'); linkBrackets(this, type, links3, token, '[', ']'); } if (!links1.empty()) { // Error, { and } don't match. unmatchedToken(links1.top()); } if (!links2.empty()) { // Error, ( and ) don't match. unmatchedToken(links2.top()); } if (!links3.empty()) { // Error, [ and ] don't match. unmatchedToken(links3.top()); } } void Tokenizer::createLinks2() { if (isC()) return; const Token * templateToken = nullptr; bool isStruct = false; std::stack type; for (Token *token = list.front(); token; token = token->next()) { if (Token::Match(token, "%name%|> %name% [:<]")) isStruct = true; else if (Token::Match(token, "[;{}]")) isStruct = false; if (token->link()) { if (Token::Match(token, "{|[|(")) type.push(token); else if (!type.empty() && Token::Match(token, "}|]|)")) { while (type.top()->str() == "<") { if (templateToken && templateToken->next() == type.top()) templateToken = nullptr; type.pop(); } type.pop(); } else token->link(nullptr); } else if (!templateToken && !isStruct && Token::Match(token, "%oror%|&&|;")) { if (Token::Match(token, "&& [,>]")) continue; // If there is some such code: A.. // Then this is probably a template instantiation if either "B" or "C" has comparisons if (token->tokType() == Token::eLogicalOp && !type.empty() && type.top()->str() == "<") { const Token *prev = token->previous(); bool foundComparison = false; while (Token::Match(prev, "%name%|%num%|%str%|%cop%|)|]") && prev != type.top()) { if (prev->str() == ")" || prev->str() == "]") prev = prev->link(); else if (prev->tokType() == Token::eLogicalOp) break; else if (prev->isComparisonOp()) foundComparison = true; prev = prev->previous(); } if (prev == type.top() && foundComparison) continue; const Token *next = token->next(); foundComparison = false; while (Token::Match(next, "%name%|%num%|%str%|%cop%|(|[") && next->str() != ">") { if (next->str() == "(" || next->str() == "[") next = next->link(); else if (next->tokType() == Token::eLogicalOp) break; else if (next->isComparisonOp()) foundComparison = true; next = next->next(); } if (next && next->str() == ">" && foundComparison) continue; } while (!type.empty() && type.top()->str() == "<") { const Token* end = type.top()->findClosingBracket(); if (Token::Match(end, "> %comp%|;|.|=|{")) break; // Variable declaration if (Token::Match(end, "> %var% ;") && (type.top()->tokAt(-2) == nullptr || Token::Match(type.top()->tokAt(-2), ";|}|{"))) break; type.pop(); } } else if (token->str() == "<" && ((token->previous() && token->previous()->isName() && !token->previous()->varId()) || Token::Match(token->next(), ">|>>"))) { type.push(token); if (!templateToken && (token->previous()->str() == "template")) templateToken = token; } else if (token->str() == ">" || token->str() == ">>") { if (type.empty() || type.top()->str() != "<") // < and > don't match. continue; Token * const top1 = type.top(); type.pop(); Token * const top2 = type.empty() ? nullptr : type.top(); type.push(top1); if (!top2 || top2->str() != "<") { if (token->str() == ">>") continue; if (token->next() && !Token::Match(token->next(), "%name%|%comp%|&|&&|*|::|,|(|)|{|}|;|[|:|.|=") && !Token::simpleMatch(token->next(), "...") && !Token::Match(token->next(), "&& %name% =")) continue; } // if > is followed by [ .. "new a[" is expected // unless this is from varidiac expansion if (token->strAt(1) == "[" && !Token::simpleMatch(token->tokAt(-1), "... >")) { Token *prev = type.top()->previous(); while (prev && Token::Match(prev->previous(), ":: %name%")) prev = prev->tokAt(-2); if (prev && prev->str() != "new") prev = prev->previous(); if (!prev || prev->str() != "new") continue; } if (token->str() == ">>") { type.pop(); type.pop(); Token::createMutualLinks(top2, token); if (top1 == templateToken || top2 == templateToken) templateToken = nullptr; } else { type.pop(); if (Token::Match(token, "> %name%") && Token::Match(top1->tokAt(-2), "%op% %name% <")) continue; Token::createMutualLinks(top1, token); if (top1 == templateToken) templateToken = nullptr; } } } } void Tokenizer::sizeofAddParentheses() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "sizeof !!(")) continue; if (tok->next()->isLiteral() || Token::Match(tok->next(), "%name%|*|~|!|&")) { Token *endToken = tok->next(); while (Token::simpleMatch(endToken, "* *")) endToken = endToken->next(); while (Token::Match(endToken->next(), "%name%|%num%|%str%|[|(|.|::|++|--|!|~") || (Token::Match(endToken, "%type% * %op%|?|:|const|;|,"))) { if (Token::Match(endToken->next(), "(|[")) endToken = endToken->linkAt(1); else endToken = endToken->next(); } // Add ( after sizeof and ) behind endToken tok->insertToken("("); endToken->insertToken(")"); Token::createMutualLinks(tok->next(), endToken->next()); } } } bool Tokenizer::simplifySizeof() { // Locate variable declarations and calculate the size std::map sizeOfVar; std::map declTokOfVar; for (const Token *tok = list.front(); tok; tok = tok->next()) { if (tok->varId() != 0 && sizeOfVar.find(tok->varId()) == sizeOfVar.end()) { const int varId = tok->varId(); if (Token::Match(tok->tokAt(-3), "[;{}(,] %type% * %name% [;,)]") || Token::Match(tok->tokAt(-4), "[;{}(,] const %type% * %name% [;),]") || Token::Match(tok->tokAt(-2), "[;{}(,] %type% %name% [;),]") || Token::Match(tok->tokAt(-3), "[;{}(,] const %type% %name% [;),]")) { const int size = sizeOfType(tok->previous()); if (size == 0) { continue; } sizeOfVar[varId] = size; declTokOfVar[varId] = tok; } else if (Token::Match(tok->previous(), "%type% %name% [ %num% ] [[;=]") || Token::Match(tok->tokAt(-2), "%type% * %name% [ %num% ] [[;=]")) { int size = sizeOfType(tok->previous()); if (size == 0) continue; const Token* tok2 = tok->next(); do { const MathLib::bigint num = MathLib::toLongNumber(tok2->strAt(1)); if (num<0) break; size *= num; tok2 = tok2->tokAt(3); } while (Token::Match(tok2, "[ %num% ]")); if (Token::Match(tok2, "[;=]")) { sizeOfVar[varId] = size; declTokOfVar[varId] = tok; } if (!tok2) { syntaxError(tok); } tok = tok2; } else if (Token::Match(tok->previous(), "%type% %name% [ %num% ] [,)]") || Token::Match(tok->tokAt(-2), "%type% * %name% [ %num% ] [,)]")) { Token tempTok; tempTok.str("*"); sizeOfVar[varId] = sizeOfType(&tempTok); declTokOfVar[varId] = tok; } } } bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "sizeof") continue; if (Token::simpleMatch(tok->next(), "...")) { //tok->deleteNext(3); tok->deleteNext(); } // sizeof('x') if (Token::Match(tok->next(), "( %char% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream sz; sz << ((isC()) ? mSettings->sizeof_int : 1); tok->str(sz.str()); ret = true; continue; } // sizeof ("text") if (Token::Match(tok->next(), "( %str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); std::ostringstream ostr; ostr << (Token::getStrLength(tok) + 1); tok->str(ostr.str()); ret = true; continue; } // sizeof(type *) => sizeof(*) if (Token::Match(tok->next(), "( %type% * )")) { tok->next()->deleteNext(); } if (Token::simpleMatch(tok->next(), "( * )")) { tok->str(MathLib::toString(sizeOfType(tok->tokAt(2)))); tok->deleteNext(3); ret = true; } // sizeof( a ) else if (Token::Match(tok->next(), "( %var% )")) { const std::map::const_iterator sizeOfVarPos = sizeOfVar.find(tok->tokAt(2)->varId()); if (sizeOfVarPos != sizeOfVar.end()) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); tok->str(MathLib::toString(sizeOfVarPos->second)); ret = true; } else { // don't try to replace size of variable if variable has // similar name with type (#329) } } else if (Token::Match(tok->next(), "( %type% )")) { const int size = sizeOfType(tok->tokAt(2)); if (size > 0) { tok->str(MathLib::toString(size)); tok->deleteNext(3); ret = true; } } else if (Token::simpleMatch(tok->next(), "( *") || Token::Match(tok->next(), "( %name% [")) { int derefs = 0; const Token* nametok = tok->tokAt(2); if (nametok->str() == "*") { do { nametok = nametok->next(); derefs++; } while (nametok && nametok->str() == "*"); if (!Token::Match(nametok, "%name% )")) continue; } else { const Token* tok2 = nametok->next(); do { tok2 = tok2->link()->next(); derefs++; } while (tok2 && tok2->str() == "["); if (!tok2 || tok2->str() != ")") continue; } // Some default value MathLib::biguint size = 0; const int varid = nametok->varId(); if (derefs != 0 && varid != 0 && declTokOfVar.find(varid) != declTokOfVar.end()) { // Try to locate variable declaration.. const Token *decltok = declTokOfVar[varid]; if (Token::Match(decltok->previous(), "%type%|* %name% [")) { size = sizeOfType(decltok->previous()); } else if (Token::Match(decltok->tokAt(-2), "%type% * %name%")) { size = sizeOfType(decltok->tokAt(-2)); } // Multi-dimensional array.. if (Token::Match(decltok, "%name% [") && Token::simpleMatch(decltok->linkAt(1), "] [")) { const Token *tok2 = decltok; for (int i = 0; i < derefs; i++) tok2 = tok2->linkAt(1); // Skip all dimensions that are dereferenced before the sizeof call while (Token::Match(tok2, "] [ %num% ]")) { size *= MathLib::toULongNumber(tok2->strAt(2)); tok2 = tok2->linkAt(1); } if (Token::simpleMatch(tok2, "] [")) continue; } } else if (nametok->strAt(1) == "[" && nametok->isStandardType()) { size = sizeOfType(nametok); if (size == 0) continue; const Token *tok2 = nametok->next(); while (Token::Match(tok2, "[ %num% ]")) { size *= MathLib::toULongNumber(tok2->strAt(1)); tok2 = tok2->link()->next(); } if (!tok2 || tok2->str() != ")") continue; } if (size > 0) { tok->str(MathLib::toString(size)); Token::eraseTokens(tok, tok->next()->link()->next()); ret = true; } } } return ret; } bool Tokenizer::simplifyTokenList1(const char FileName[]) { if (Settings::terminated()) return false; // if MACRO for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "if|for|while|BOOST_FOREACH %name% (")) { if (Token::simpleMatch(tok, "for each")) { // 'for each ( )' -> 'asm ( )' tok->str("asm"); tok->deleteNext(); } else if (tok->strAt(1) == "constexpr") { tok->deleteNext(); tok->isConstexpr(true); } else { syntaxError(tok); } } } // Is there C++ code in C file? validateC(); // remove MACRO in variable declaration: MACRO int x; removeMacroInVarDecl(); // Combine strings and character literals, e.g. L"string", L'c', "string1" "string2" combineStringAndCharLiterals(); // replace inline SQL with "asm()" (Oracle PRO*C). Ticket: #1959 simplifySQL(); createLinks(); simplifyHeaders(); // Remove __asm.. simplifyAsm(); // foo < bar < >> => foo < bar < > > if (isCPP()) mTemplateSimplifier->fixAngleBrackets(); // Bail out if code is garbage if (mTimerResults) { Timer t("Tokenizer::tokenize::findGarbageCode", mSettings->showtime, mTimerResults); findGarbageCode(); } else { findGarbageCode(); } checkConfiguration(); // if (x) MACRO() .. for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "if (")) { tok = tok->next()->link(); if (Token::Match(tok, ") %name% (") && tok->next()->isUpperCaseName() && Token::Match(tok->linkAt(2), ") {|else")) { syntaxError(tok->next()); } } } if (Settings::terminated()) return false; // convert C++17 style nested namespaces to old style namespaces simplifyNestedNamespace(); // simplify namespace aliases simplifyNamespaceAliases(); // Remove [[attribute]] simplifyCPPAttribute(); // remove __attribute__((?)) simplifyAttribute(); // simplify cppcheck attributes __cppcheck_?__(?) simplifyCppcheckAttribute(); // Combine tokens.. combineOperators(); // Simplify the C alternative tokens (and, or, etc.) simplifyCAlternativeTokens(); // replace 'sin(0)' to '0' and other similar math expressions simplifyMathExpressions(); // combine "- %num%" concatenateNegativeNumberAndAnyPositive(); // remove extern "C" and extern "C" {} if (isCPP()) simplifyExternC(); // simplify weird but legal code: "[;{}] ( { code; } ) ;"->"[;{}] code;" simplifyRoundCurlyParentheses(); // check for simple syntax errors.. for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "> struct {") && Token::simpleMatch(tok->linkAt(2), "} ;")) { syntaxError(tok); } } if (!simplifyAddBraces()) return false; sizeofAddParentheses(); // Simplify: 0[foo] -> *(foo) for (Token* tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "0 [") && tok->linkAt(1)) { tok->str("*"); tok->next()->str("("); tok->linkAt(1)->str(")"); } } if (Settings::terminated()) return false; // Remove __declspec() simplifyDeclspec(); validate(); // Remove "inline", "register", and "restrict" simplifyKeyword(); // simplify simple calculations inside <..> if (isCPP()) { Token *lt = nullptr; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}]")) lt = nullptr; else if (Token::Match(tok, "%type% <")) lt = tok->next(); else if (lt && Token::Match(tok, ">|>> %name%|::|(")) { const Token * const end = tok; for (tok = lt; tok != end; tok = tok->next()) { if (tok->isNumber()) TemplateSimplifier::simplifyNumericCalculations(tok); } lt = tok->next(); } } } // Convert K&R function declarations to modern C simplifyVarDecl(true); simplifyFunctionParameters(); // simplify case ranges (gcc extension) simplifyCaseRange(); // simplify labels and 'case|default'-like syntaxes simplifyLabelsCaseDefault(); // simplify '[;{}] * & ( %any% ) =' to '%any% =' simplifyMulAndParens(); if (!isC() && !mSettings->library.markupFile(FileName)) { findComplicatedSyntaxErrorsInTemplates(); } if (Settings::terminated()) return false; // remove calling conventions __cdecl, __stdcall.. simplifyCallingConvention(); addSemicolonAfterUnknownMacro(); // remove some unhandled macros in global scope removeMacrosInGlobalScope(); // remove undefined macro in class definition: // class DLLEXPORT Fred { }; // class Fred FINAL : Base { }; removeMacroInClassDef(); // That call here fixes #7190 validate(); // remove unnecessary member qualification.. removeUnnecessaryQualification(); // convert Microsoft memory functions simplifyMicrosoftMemoryFunctions(); // convert Microsoft string functions simplifyMicrosoftStringFunctions(); if (Settings::terminated()) return false; // Remove Qt signals and slots simplifyQtSignalsSlots(); // remove Borland stuff.. simplifyBorland(); // syntax error: enum with typedef in it checkForEnumsWithTypedef(); // Add parentheses to ternary operator where necessary prepareTernaryOpForAST(); // Change initialisation of variable to assignment simplifyInitVar(); // Split up variable declarations. simplifyVarDecl(false); // typedef.. if (mTimerResults) { Timer t("Tokenizer::tokenize::simplifyTypedef", mSettings->showtime, mTimerResults); simplifyTypedef(); } else { simplifyTypedef(); } // using A = B; while (simplifyUsing()) ; // Add parentheses to ternary operator where necessary // TODO: this is only necessary if one typedef simplification had a comma and was used within ?: // If typedef handling is refactored and moved to symboldatabase someday we can remove this prepareTernaryOpForAST(); for (Token* tok = list.front(); tok;) { if (Token::Match(tok, "union|struct|class union|struct|class")) tok->deleteNext(); else tok = tok->next(); } // class x y { if (isCPP() && mSettings->isEnabled(Settings::INFORMATION)) { for (const Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "class %type% %type% [:{]")) { unhandled_macro_class_x_y(tok); } } } // catch bad typedef canonicalization // // to reproduce bad typedef, download upx-ucl from: // http://packages.debian.org/sid/upx-ucl // analyse the file src/stub/src/i386-linux.elf.interp-main.c validate(); // The simplify enum have inner loops if (Settings::terminated()) return false; // Put ^{} statements in asm() simplifyAsm2(); // @.. simplifyAt(); // When the assembly code has been cleaned up, no @ is allowed for (const Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "(") { const Token *tok1 = tok; tok = tok->link(); if (!tok) syntaxError(tok1); } else if (tok->str() == "@") { syntaxError(tok); } } // Order keywords "static" and "const" simplifyStaticConst(); // convert platform dependent types to standard types // 32 bits: size_t -> unsigned long // 64 bits: size_t -> unsigned long long list.simplifyPlatformTypes(); // collapse compound standard types into a single token // unsigned long long int => long (with _isUnsigned=true,_isLong=true) list.simplifyStdType(); if (Settings::terminated()) return false; // simplify bit fields.. simplifyBitfields(); if (Settings::terminated()) return false; // struct simplification "struct S {} s; => struct S { } ; S s ; simplifyStructDecl(); if (Settings::terminated()) return false; // x = ({ 123; }); => { x = 123; } simplifyAssignmentBlock(); if (Settings::terminated()) return false; simplifyVariableMultipleAssign(); // Collapse operator name tokens into single token // operator = => operator= simplifyOperatorName(); // Remove redundant parentheses simplifyRedundantParentheses(); if (isCPP()) simplifyTypeIntrinsics(); if (!isC()) { // Handle templates.. if (mTimerResults) { Timer t("Tokenizer::tokenize::simplifyTemplates", mSettings->showtime, mTimerResults); simplifyTemplates(); } else { simplifyTemplates(); } // The simplifyTemplates have inner loops if (Settings::terminated()) return false; // sometimes the "simplifyTemplates" fail and then unsimplified // function calls etc remain. These have the "wrong" syntax. So // this function will just fix so that the syntax is corrected. validate(); // #6847 - invalid code mTemplateSimplifier->cleanupAfterSimplify(); } // Simplify pointer to standard types (C only) simplifyPointerToStandardType(); // simplify function pointers simplifyFunctionPointers(); // Change initialisation of variable to assignment simplifyInitVar(); // Split up variable declarations. simplifyVarDecl(false); validate(); // #6772 "segmentation fault (invalid code) in Tokenizer::setVarId" if (mTimerResults) { Timer t("Tokenizer::tokenize::setVarId", mSettings->showtime, mTimerResults); setVarId(); } else { setVarId(); } // Link < with > createLinks2(); // specify array size arraySize(); // The simplify enum might have inner loops if (Settings::terminated()) return false; // Add std:: in front of std classes, when using namespace std; was given simplifyNamespaceStd(); // Change initialisation of variable to assignment simplifyInitVar(); simplifyDoublePlusAndDoubleMinus(); simplifyArrayAccessSyntax(); Token::assignProgressValues(list.front()); removeRedundantSemicolons(); simplifyParameterVoid(); simplifyRedundantConsecutiveBraces(); simplifyEmptyNamespaces(); elseif(); validate(); list.front()->assignIndexes(); return true; } bool Tokenizer::simplifyTokenList2() { // clear the _functionList so it can't contain dead pointers deleteSymbolDatabase(); // Clear AST,ValueFlow. These will be created again at the end of this function. for (Token *tok = list.front(); tok; tok = tok->next()) { tok->clearAst(); tok->clearValueFlow(); } // Convert e.g. atol("0") into 0 simplifyMathFunctions(); // f(x=g()) => x=g(); f(x) simplifyAssignmentInFunctionCall(); // ";a+=b;" => ";a=a+b;" simplifyCompoundAssignment(); simplifyCharAt(); // simplify references simplifyReference(); simplifyStd(); if (Settings::terminated()) return false; simplifySizeof(); simplifyUndefinedSizeArray(); simplifyCasts(); // Simplify simple calculations before replace constants, this allows the replacement of constants that are calculated // e.g. const static int value = sizeof(X)/sizeof(Y); simplifyCalculations(); if (Settings::terminated()) return false; // Replace "*(ptr + num)" => "ptr[num]" simplifyOffsetPointerDereference(); // Replace "&str[num]" => "(str + num)" simplifyOffsetPointerReference(); removeRedundantAssignment(); simplifyRealloc(); // Change initialisation of variable to assignment simplifyInitVar(); // Simplify variable declarations simplifyVarDecl(false); simplifyErrNoInWhile(); simplifyIfAndWhileAssign(); simplifyRedundantParentheses(); simplifyNestedStrcat(); simplifyFuncInWhile(); simplifyIfAndWhileAssign(); // replace strlen(str) for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "strlen ( %str% )")) { tok->str(MathLib::toString(Token::getStrLength(tok->tokAt(2)))); tok->deleteNext(3); } } bool modified = true; while (modified) { if (Settings::terminated()) return false; modified = false; modified |= simplifyConditions(); modified |= simplifyFunctionReturn(); modified |= simplifyKnownVariables(); modified |= simplifyStrlen(); modified |= removeRedundantConditions(); modified |= simplifyRedundantParentheses(); modified |= simplifyConstTernaryOp(); modified |= simplifyCalculations(); validate(); } // simplify redundant loops simplifyWhile0(); removeRedundantFor(); // Remove redundant parentheses in return.. for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::simpleMatch(tok, "return (")) { Token *tok2 = tok->next()->link(); if (Token::simpleMatch(tok2, ") ;")) { tok->deleteNext(); tok2->deleteThis(); } else { break; } } } simplifyReturnStrncat(); removeRedundantAssignment(); simplifyComma(); removeRedundantSemicolons(); simplifyFlowControl(); simplifyRedundantConsecutiveBraces(); simplifyEmptyNamespaces(); simplifyMathFunctions(); validate(); Token::assignProgressValues(list.front()); list.createAst(); // needed for #7208 (garbage code) and #7724 (ast max depth limit) list.validateAst(); // Create symbol database and then remove const keywords createSymbolDatabase(); mSymbolDatabase->setValueTypeInTokenList(true); ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); if (Settings::terminated()) return false; printDebugOutput(2); return true; } //--------------------------------------------------------------------------- void Tokenizer::printDebugOutput(int simplification) const { const bool debug = (simplification != 1U && mSettings->debugSimplified) || (simplification != 2U && mSettings->debugnormal); if (debug && list.front()) { list.front()->printOut(nullptr, list.getFiles()); if (mSettings->xml) std::cout << "" << std::endl; if (mSymbolDatabase) { if (mSettings->xml) mSymbolDatabase->printXml(std::cout); else if (mSettings->verbose) { mSymbolDatabase->printOut("Symbol database"); } } if (mSettings->verbose) list.front()->printAst(mSettings->verbose, mSettings->xml, std::cout); list.front()->printValueFlow(mSettings->xml, std::cout); if (mSettings->xml) std::cout << "" << std::endl; } if (mSymbolDatabase && simplification == 2U && mSettings->debugwarnings) { printUnknownTypes(); // the typeStartToken() should come before typeEndToken() for (const Variable *var : mSymbolDatabase->variableList()) { if (!var) continue; const Token * typetok = var->typeStartToken(); while (typetok && typetok != var->typeEndToken()) typetok = typetok->next(); if (typetok != var->typeEndToken()) { reportError(var->typeStartToken(), Severity::debug, "debug", "Variable::typeStartToken() of variable '" + var->name() + "' is not located before Variable::typeEndToken(). The location of the typeStartToken() is '" + var->typeStartToken()->str() + "' at line " + MathLib::toString(var->typeStartToken()->linenr())); } } } } void Tokenizer::dump(std::ostream &out) const { // Create a xml data dump. // The idea is not that this will be readable for humans. It's a // data dump that 3rd party tools could load and get useful info from. // tokens.. out << " " << std::endl; for (const Token *tok = list.front(); tok; tok = tok->next()) { out << " linenr() << "\" column=\"" << tok->column() << "\""; out << " str=\"" << ErrorLogger::toxml(tok->str()) << '\"'; out << " scope=\"" << tok->scope() << '\"'; if (tok->isName()) { out << " type=\"name\""; if (tok->isUnsigned()) out << " isUnsigned=\"true\""; else if (tok->isSigned()) out << " isSigned=\"true\""; } else if (tok->isNumber()) { out << " type=\"number\""; if (MathLib::isInt(tok->str())) out << " isInt=\"True\""; if (MathLib::isFloat(tok->str())) out << " isFloat=\"True\""; } else if (tok->tokType() == Token::eString) out << " type=\"string\" strlen=\"" << Token::getStrLength(tok) << '\"'; else if (tok->tokType() == Token::eChar) out << " type=\"char\""; else if (tok->isBoolean()) out << " type=\"boolean\""; else if (tok->isOp()) { out << " type=\"op\""; if (tok->isArithmeticalOp()) out << " isArithmeticalOp=\"True\""; else if (tok->isAssignmentOp()) out << " isAssignmentOp=\"True\""; else if (tok->isComparisonOp()) out << " isComparisonOp=\"True\""; else if (tok->tokType() == Token::eLogicalOp) out << " isLogicalOp=\"True\""; } if (tok->isExpandedMacro()) out << " isExpandedMacro=\"True\""; if (tok->link()) out << " link=\"" << tok->link() << '\"'; if (tok->varId() > 0) out << " varId=\"" << MathLib::toString(tok->varId()) << '\"'; if (tok->variable()) out << " variable=\"" << tok->variable() << '\"'; if (tok->function()) out << " function=\"" << tok->function() << '\"'; if (!tok->values().empty()) out << " values=\"" << &tok->values() << '\"'; if (tok->type()) out << " type-scope=\"" << tok->type()->classScope << '\"'; if (tok->astParent()) out << " astParent=\"" << tok->astParent() << '\"'; if (tok->astOperand1()) out << " astOperand1=\"" << tok->astOperand1() << '\"'; if (tok->astOperand2()) out << " astOperand2=\"" << tok->astOperand2() << '\"'; if (!tok->originalName().empty()) out << " originalName=\"" << tok->originalName() << '\"'; if (tok->valueType()) { const std::string vt = tok->valueType()->dump(); if (!vt.empty()) out << ' ' << vt; } out << "/>" << std::endl; } out << " " << std::endl; mSymbolDatabase->printXml(out); if (list.front()) list.front()->printValueFlow(true, out); } void Tokenizer::simplifyHeaders() { // TODO : can we remove anything in headers here? Like unused declarations. // Maybe if --dump is used we want to have _everything_. if (mSettings->checkHeaders && mSettings->checkUnusedTemplates) // Full analysis. All information in the headers are kept. return; const bool checkHeaders = mSettings->checkHeaders; const bool removeUnusedIncludedFunctions = !mSettings->checkHeaders; const bool removeUnusedIncludedClasses = !mSettings->checkHeaders; const bool removeUnusedIncludedTemplates = !mSettings->checkUnusedTemplates || !mSettings->checkHeaders; const bool removeUnusedTemplates = !mSettings->checkUnusedTemplates; // We want to remove selected stuff from the headers but not *everything*. // The intention here is to not damage the analysis of the source file. // You should get all warnings in the source file. // TODO: Remove unused types/variables/etc in headers.. // functions and types to keep std::set keep; for (const Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName()) continue; if (checkHeaders && tok->fileIndex() != 0) continue; if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { keep.insert(tok->str()); continue; } if (Token::Match(tok, "%name% %name%|::|*|&|<")) { keep.insert(tok->str()); } } const std::set functionStart{"static", "const", "unsigned", "signed", "void", "bool", "char", "short", "int", "long", "float", "*"}; for (Token *tok = list.front(); tok; tok = tok->next()) { const bool isIncluded = (tok->fileIndex() != 0); // Remove executable code if (isIncluded && !mSettings->checkHeaders && tok->str() == "{") { // TODO: We probably need to keep the executable code if this function is called from the source file. const Token *prev = tok->previous(); while (prev && prev->isName()) prev = prev->previous(); if (Token::simpleMatch(prev, ")")) { // Replace all tokens from { to } with a ";". Token::eraseTokens(tok,tok->link()->next()); tok->str(";"); tok->link(nullptr); } } if (Token::Match(tok, "[;{}]")) { // Remove unused function declarations if (isIncluded && removeUnusedIncludedFunctions) { while (1) { Token *start = tok->next(); while (start && functionStart.find(start->str()) != functionStart.end()) start = start->next(); if (Token::Match(start, "%name% (") && Token::Match(start->linkAt(1), ") const| ;") && keep.find(start->str()) == keep.end()) Token::eraseTokens(tok, start->linkAt(1)->tokAt(2)); else break; } } if (isIncluded && removeUnusedIncludedClasses) { if (Token::Match(tok, "[;{}] class|struct %name% [:{]") && keep.find(tok->strAt(2)) == keep.end()) { // Remove this class/struct const Token *endToken = tok->tokAt(3); if (endToken->str() == ":") { endToken = endToken->next(); while (Token::Match(endToken, "%name%|,")) endToken = endToken->next(); } if (endToken && endToken->str() == "{" && Token::simpleMatch(endToken->link(), "} ;")) Token::eraseTokens(tok, endToken->link()->next()); } } if (removeUnusedTemplates || (isIncluded && removeUnusedIncludedTemplates)) { if (Token::Match(tok->next(), "template < %name%")) { const Token *tok2 = tok->tokAt(3); while (Token::Match(tok2, "%name% %name% [,=>]") || Token::Match(tok2, "typename ... %name% [,>]")) { if (Token::simpleMatch(tok2, "typename ...")) tok2 = tok2->tokAt(3); else tok2 = tok2->tokAt(2); if (Token::Match(tok2, "= %name% [,>]")) tok2 = tok2->tokAt(2); if (tok2->str() == ",") tok2 = tok2->next(); } if (Token::Match(tok2, "> class|struct %name% [;:{]") && keep.find(tok2->strAt(2)) == keep.end()) { const Token *endToken = tok2->tokAt(3); if (endToken->str() == ":") { endToken = endToken->next(); while (Token::Match(endToken, "%name%|,")) endToken = endToken->next(); } if (endToken && endToken->str() == "{") endToken = endToken->link()->next(); if (endToken && endToken->str() == ";") Token::eraseTokens(tok, endToken); } else if (Token::Match(tok2, "> %type% %name% (") && Token::simpleMatch(tok2->linkAt(3), ") {") && keep.find(tok2->strAt(2)) == keep.end()) { const Token *endToken = tok2->linkAt(3)->linkAt(1)->next(); Token::eraseTokens(tok, endToken); } } } } } } void Tokenizer::removeMacrosInGlobalScope() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "(") { tok = tok->link(); if (Token::Match(tok, ") %type% {") && !Token::Match(tok->next(), "const|namespace|class|struct|union|noexcept|override|final|volatile")) tok->deleteNext(); } if (Token::Match(tok, "%type%") && tok->isUpperCaseName() && (!tok->previous() || Token::Match(tok->previous(), "[;{}]") || (tok->previous()->isName() && endsWith(tok->previous()->str(), ':')))) { const Token *tok2 = tok->next(); if (tok2 && tok2->str() == "(") tok2 = tok2->link()->next(); // Several unknown macros... while (Token::Match(tok2, "%type% (") && tok2->isUpperCaseName()) tok2 = tok2->linkAt(1)->next(); if (Token::Match(tok, "%name% (") && Token::Match(tok2, "%name% *|&|::|<| %name%") && !Token::Match(tok2, "namespace|class|struct|union")) unknownMacroError(tok); if (Token::Match(tok, "%type% (") && Token::Match(tok2, "%type% (") && !Token::Match(tok2, "noexcept|throw") && isFunctionHead(tok2->next(), ":;{")) unknownMacroError(tok); // remove unknown macros before namespace|class|struct|union if (Token::Match(tok2, "namespace|class|struct|union")) { // is there a "{" for? const Token *tok3 = tok2; while (tok3 && !Token::Match(tok3,"[;{}()]")) tok3 = tok3->next(); if (tok3 && tok3->str() == "{") { Token::eraseTokens(tok, tok2); tok->deleteThis(); } continue; } // replace unknown macros before foo( /* if (Token::Match(tok2, "%type% (") && isFunctionHead(tok2->next(), "{")) { std::string typeName; for (const Token* tok3 = tok; tok3 != tok2; tok3 = tok3->next()) typeName += tok3->str(); Token::eraseTokens(tok, tok2); tok->str(typeName); } */ // remove unknown macros before foo::foo( if (Token::Match(tok2, "%type% :: %type%")) { const Token *tok3 = tok2; while (Token::Match(tok3, "%type% :: %type% ::")) tok3 = tok3->tokAt(2); if (Token::Match(tok3, "%type% :: %type% (") && tok3->str() == tok3->strAt(2)) { Token::eraseTokens(tok, tok2); tok->deleteThis(); } continue; } } // Skip executable scopes if (tok->str() == "{") { const Token *prev = tok->previous(); while (prev && prev->isName()) prev = prev->previous(); if (prev && prev->str() == ")") tok = tok->link(); } } } //--------------------------------------------------------------------------- void Tokenizer::removeMacroInClassDef() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "class|struct %name% %name% {|:")) continue; const bool nextIsUppercase = tok->next()->isUpperCaseName(); const bool afterNextIsUppercase = tok->tokAt(2)->isUpperCaseName(); if (nextIsUppercase && !afterNextIsUppercase) tok->deleteNext(); else if (!nextIsUppercase && afterNextIsUppercase) tok->next()->deleteNext(); } } //--------------------------------------------------------------------------- void Tokenizer::removeMacroInVarDecl() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %name% (") && tok->next()->isUpperCaseName()) { // goto ')' parentheses const Token *tok2 = tok; int parlevel = 0; while (tok2) { if (tok2->str() == "(") ++parlevel; else if (tok2->str() == ")") { if (--parlevel <= 0) break; } tok2 = tok2->next(); } tok2 = tok2 ? tok2->next() : nullptr; // check if this is a variable declaration.. const Token *tok3 = tok2; while (tok3 && tok3->isUpperCaseName()) tok3 = tok3->next(); if (tok3 && (tok3->isStandardType() || Token::Match(tok3,"const|static|struct|union|class"))) Token::eraseTokens(tok,tok2); } } } //--------------------------------------------------------------------------- void Tokenizer::addSemicolonAfterUnknownMacro() { if (!isCPP()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != ")") continue; const Token *macro = tok->link() ? tok->link()->previous() : nullptr; if (!macro || !macro->isName()) continue; if (Token::simpleMatch(tok, ") try") && !Token::Match(macro, "if|for|while")) tok->insertToken(";"); else if (Token::simpleMatch(tok, ") using")) tok->insertToken(";"); } } //--------------------------------------------------------------------------- void Tokenizer::removeRedundantAssignment() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); const Token * const start = const_cast(startOfExecutableScope(tok)); if (start) { tok = start->previous(); // parse in this function.. std::set localvars; const Token * const end = tok->next()->link(); for (Token * tok2 = tok->next(); tok2 && tok2 != end; tok2 = tok2->next()) { // skip local class or struct if (Token::Match(tok2, "class|struct %type% {|:")) { // skip to '{' tok2 = tok2->tokAt(2); while (tok2 && tok2->str() != "{") tok2 = tok2->next(); if (tok2) tok2 = tok2->link(); // skip local class or struct else return; } else if (Token::Match(tok2, "[;{}] %type% * %name% ;") && tok2->next()->str() != "return") { tok2 = tok2->tokAt(3); localvars.insert(tok2->varId()); } else if (Token::Match(tok2, "[;{}] %type% %name% ;") && tok2->next()->isStandardType()) { tok2 = tok2->tokAt(2); localvars.insert(tok2->varId()); } else if (tok2->varId() && !Token::Match(tok2->previous(), "[;{}] %name% = %char%|%num%|%name% ;")) { localvars.erase(tok2->varId()); } } localvars.erase(0); if (!localvars.empty()) { for (Token *tok2 = tok->next(); tok2 && tok2 != end;) { if (Token::Match(tok2, "[;{}] %type% %name% ;") && localvars.find(tok2->tokAt(2)->varId()) != localvars.end()) { tok2->deleteNext(3); } else if ((Token::Match(tok2, "[;{}] %type% * %name% ;") && localvars.find(tok2->tokAt(3)->varId()) != localvars.end()) || (Token::Match(tok2, "[;{}] %name% = %any% ;") && localvars.find(tok2->next()->varId()) != localvars.end())) { tok2->deleteNext(4); } else tok2 = tok2->next(); } } } } } void Tokenizer::simplifyRealloc() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "(|[") || (tok->str() == "{" && tok->previous() && tok->previous()->str() == "=")) tok = tok->link(); else if (Token::Match(tok, "[;{}] %name% = realloc (")) { tok = tok->tokAt(3); if (Token::simpleMatch(tok->next(), "( 0 ,")) { //no "x = realloc(0,);" if (!Token::simpleMatch(tok->next()->link(), ") ;") || tok->next()->link()->previous() == tok->tokAt(3)) continue; // delete "0 ," tok->next()->deleteNext(2); // Change function name "realloc" to "malloc" tok->str("malloc"); tok = tok->next()->link(); } else { Token *tok2 = tok->next()->link()->tokAt(-2); //no "x = realloc(,0);" if (!Token::simpleMatch(tok2, ", 0 ) ;") || tok2 == tok->tokAt(2)) continue; //remove ", 0" tok2 = tok2->previous(); tok2->deleteNext(2); //change "realloc" to "free" tok->str("free"); //insert "0" after "var =" tok = tok->previous(); tok->insertToken("0"); //move "var = 0" between "free(...)" and ";" tok2 = tok2->next(); Token::move(tok->previous(), tok->next(), tok2); //add missing ";" after "free(...)" tok2->insertToken(";"); //goto before last ";" and continue tok = tok->next(); } } } } void Tokenizer::simplifyEmptyNamespaces() { if (isC()) return; bool goback = false; for (Token *tok = list.front(); tok; tok = tok ? tok->next() : nullptr) { if (goback) { tok = tok->previous(); goback = false; } if (Token::Match(tok, "(|[|{")) { tok = tok->link(); continue; } if (!Token::Match(tok, "namespace %name% {")) continue; if (tok->strAt(3) == "}") { tok->deleteNext(3); // remove '%name% { }' if (!tok->previous()) { // remove 'namespace' or replace it with ';' if isolated tok->deleteThis(); goback = true; } else { // '%any% namespace %any%' tok = tok->previous(); // goto previous token tok->deleteNext(); // remove next token: 'namespace' } } else { tok = tok->tokAt(2); } } } void Tokenizer::simplifyFlowControl() { for (Token *begin = list.front(); begin; begin = begin->next()) { if (Token::Match(begin, "(|[") || (begin->str() == "{" && begin->previous() && begin->strAt(-1) == "=")) begin = begin->link(); //function scope if (!Token::simpleMatch(begin, ") {") && !Token::Match(begin, ") %name% {")) continue; Token* end = begin->linkAt(1+(begin->next()->str() == "{" ? 0 : 1)); int indentLevel = 0; bool stilldead = false; for (Token *tok = begin; tok && tok != end; tok = tok->next()) { if (Token::Match(tok, "(|[")) { tok = tok->link(); continue; } if (tok->str() == "{") { if (tok->previous() && tok->previous()->str() == "=") { tok = tok->link(); continue; } ++indentLevel; } else if (tok->str() == "}") { if (indentLevel == 0) break; --indentLevel; if (stilldead) { eraseDeadCode(tok, nullptr); if (indentLevel == 1 || tok->next()->str() != "}" || !Token::Match(tok->next()->link()->previous(), ";|{|}|do {")) stilldead = false; continue; } } if (indentLevel == 0) continue; if (Token::Match(tok,"continue|break ;")) { tok = tok->next(); eraseDeadCode(tok, nullptr); } else if (Token::Match(tok,"return|goto") || (Token::Match(tok->previous(), "[;{}] %name% (") && mSettings->library.isnoreturn(tok)) || (isCPP() && tok->str() == "throw")) { if (tok->next()->str() == "}") syntaxError(tok->next()); // invalid code like in #6731 //TODO: ensure that we exclude user-defined 'exit|abort|throw', except for 'noreturn' //catch the first ';' for (Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "(|[")) { tok2 = tok2->link(); } else if (tok2->str() == ";") { tok = tok2; eraseDeadCode(tok, nullptr); break; } else if (Token::Match(tok2, "[{}]")) break; } //if everything is removed, then remove also the code after an inferior scope //only if the actual scope is not special if (indentLevel > 1 && tok->next()->str() == "}" && Token::Match(tok->next()->link()->previous(), ";|{|}|do {")) stilldead = true; } } begin = end; } } bool Tokenizer::removeRedundantConditions() { // Return value for function. Set to true if there are any simplifications bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "if ( %bool% ) {")) continue; // Find matching else Token *elseTag = tok->linkAt(4)->next(); const bool boolValue = (tok->strAt(2) == "true"); // Handle if with else if (Token::simpleMatch(elseTag, "else {")) { // Handle else if (!boolValue) { // Convert "if( false ) {aaa;} else {bbb;}" => "{bbb;}" //remove '(false)' tok->deleteNext(3); //delete dead code inside scope eraseDeadCode(tok, elseTag); //remove 'else' elseTag->deleteThis(); //remove 'if' tok->deleteThis(); } else { // Convert "if( true ) {aaa;} else {bbb;}" => "{aaa;}" const Token *end = elseTag->next()->link()->next(); // Remove "else { bbb; }" elseTag = elseTag->previous(); eraseDeadCode(elseTag, end); // Remove "if( true )" tok->deleteNext(3); tok->deleteThis(); } ret = true; } // Handle if without else else { if (!boolValue) { //remove '(false)' tok->deleteNext(3); //delete dead code inside scope eraseDeadCode(tok, elseTag); //remove 'if' tok->deleteThis(); } else { // convert "if( true ) {aaa;}" => "{aaa;}" tok->deleteNext(3); tok->deleteThis(); } ret = true; } } return ret; } void Tokenizer::removeRedundantFor() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] for ( %name% = %num% ; %name% < %num% ; ++| %name% ++| ) {") || Token::Match(tok, "[;{}] for ( %type% %name% = %num% ; %name% < %num% ; ++| %name% ++| ) {")) { // Same variable name.. const Token* varTok = tok->tokAt(3); const bool type = varTok->next()->isName(); if (type) varTok = varTok->next(); const std::string varname(varTok->str()); const int varid(varTok->varId()); if (varname != varTok->strAt(4)) continue; const Token *vartok2 = tok->linkAt(2)->previous(); if (vartok2->str() == "++") vartok2 = vartok2->previous(); else if (vartok2->strAt(-1) != "++") continue; if (varname != vartok2->str()) continue; // Check that the difference of the numeric values is 1 const MathLib::bigint num1(MathLib::toLongNumber(varTok->strAt(2))); const MathLib::bigint num2(MathLib::toLongNumber(varTok->strAt(6))); if (num1 + 1 != num2) continue; // check how loop variable is used in loop.. bool read = false; bool write = false; const Token* end = tok->linkAt(2)->next()->link(); for (const Token *tok2 = tok->linkAt(2); tok2 != end; tok2 = tok2->next()) { if (tok2->str() == varname) { if (tok2->previous()->isArithmeticalOp() && tok2->next() && (tok2->next()->isArithmeticalOp() || tok2->next()->str() == ";")) { read = true; } else { read = write = true; break; } } } // Simplify loop if loop variable isn't written if (!write) { Token* bodyBegin = tok->linkAt(2)->next(); // remove "for (" tok->deleteNext(2); // If loop variable is read then keep assignment before // loop body.. if (type) { tok->insertToken("{"); Token::createMutualLinks(tok->next(), bodyBegin->link()); bodyBegin->deleteThis(); tok = tok->tokAt(6); } else if (read) { // goto ";" tok = tok->tokAt(4); } else { // remove "x = 0 ;" tok->deleteNext(4); } // remove "x < 1 ; x ++ )" tok->deleteNext(7); if (!type) { // Add assignment after the loop body so the loop variable // get the correct end value Token *tok2 = tok->next()->link(); tok2->insertToken(";"); tok2->insertToken(MathLib::toString(num2)); tok2->insertToken("="); tok2->insertToken(varname); tok2->next()->varId(varid); } } } } } void Tokenizer::removeRedundantSemicolons() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->link() && tok->str() == "(") { tok = tok->link(); continue; } for (;;) { if (Token::simpleMatch(tok, "; ;")) { tok->deleteNext(); } else if (Token::simpleMatch(tok, "; { ; }")) { tok->deleteNext(3); } else { break; } } } } bool Tokenizer::simplifyAddBraces() { for (Token *tok = list.front(); tok; tok = tok->next()) { Token const * tokRet=simplifyAddBracesToCommand(tok); if (!tokRet) return false; } return true; } Token *Tokenizer::simplifyAddBracesToCommand(Token *tok) { Token * tokEnd=tok; if (Token::Match(tok,"for|switch|BOOST_FOREACH")) { tokEnd=simplifyAddBracesPair(tok,true); } else if (tok->str()=="while") { Token *tokPossibleDo=tok->previous(); if (Token::simpleMatch(tok->previous(), "{")) tokPossibleDo = nullptr; else if (Token::simpleMatch(tokPossibleDo,"}")) tokPossibleDo = tokPossibleDo->link(); if (!tokPossibleDo || tokPossibleDo->strAt(-1) != "do") tokEnd=simplifyAddBracesPair(tok,true); } else if (tok->str()=="do") { tokEnd=simplifyAddBracesPair(tok,false); if (tokEnd!=tok) { // walk on to next token, i.e. "while" // such that simplifyAddBracesPair does not close other braces // before the "while" if (tokEnd) { tokEnd=tokEnd->next(); if (!tokEnd || tokEnd->str()!="while") // no while syntaxError(tok); } } } else if (tok->str()=="if" && !Token::simpleMatch(tok->tokAt(-2), "operator \"\"")) { tokEnd=simplifyAddBracesPair(tok,true); if (!tokEnd) return nullptr; if (tokEnd->strAt(1) == "else") { Token * tokEndNextNext= tokEnd->tokAt(2); if (!tokEndNextNext || tokEndNextNext->str() == "}") syntaxError(tokEndNextNext); if (tokEndNextNext->str() == "if") // do not change "else if ..." to "else { if ... }" tokEnd=simplifyAddBracesToCommand(tokEndNextNext); else tokEnd=simplifyAddBracesPair(tokEnd->next(),false); } } return tokEnd; } Token *Tokenizer::simplifyAddBracesPair(Token *tok, bool commandWithCondition) { Token * tokCondition=tok->next(); if (!tokCondition) // Missing condition return tok; Token *tokAfterCondition=tokCondition; if (commandWithCondition) { if (tokCondition->str()=="(") tokAfterCondition=tokCondition->link(); else syntaxError(tok); // Bad condition if (!tokAfterCondition || tokAfterCondition->strAt(1) == "]") syntaxError(tok); // Bad condition tokAfterCondition=tokAfterCondition->next(); if (!tokAfterCondition || Token::Match(tokAfterCondition, ")|}|,")) { // No tokens left where to add braces around return tok; } } Token * tokBracesEnd=nullptr; if (tokAfterCondition->str()=="{") { // already surrounded by braces tokBracesEnd=tokAfterCondition->link(); } else if (Token::simpleMatch(tokAfterCondition, "try {") && Token::simpleMatch(tokAfterCondition->linkAt(1), "} catch (")) { tokAfterCondition->previous()->insertToken("{"); Token * tokOpenBrace = tokAfterCondition->previous(); Token * tokEnd = tokAfterCondition->linkAt(1)->linkAt(2)->linkAt(1); if (!tokEnd) { syntaxError(tokAfterCondition); } tokEnd->insertToken("}"); Token * tokCloseBrace = tokEnd->next(); Token::createMutualLinks(tokOpenBrace, tokCloseBrace); tokBracesEnd = tokCloseBrace; } else if (Token::Match(tokAfterCondition, "%name% : {")) { tokAfterCondition->previous()->insertToken("{"); tokAfterCondition->linkAt(2)->insertToken("}"); tokBracesEnd = tokAfterCondition->linkAt(2)->next(); Token::createMutualLinks(tokAfterCondition->previous(), tokBracesEnd); } else { Token * tokEnd = simplifyAddBracesToCommand(tokAfterCondition); if (!tokEnd) // Ticket #4887 return tok; if (tokEnd->str()!="}") { // Token does not end with brace // Look for ; to add own closing brace after it while (tokEnd && !Token::Match(tokEnd, ";|)|}")) { if (tokEnd->tokType()==Token::eBracket || tokEnd->str() == "(") { tokEnd = tokEnd->link(); if (!tokEnd) { // Inner bracket does not close return tok; } } tokEnd=tokEnd->next(); } if (!tokEnd || tokEnd->str() != ";") { // No trailing ; return tok; } } tokAfterCondition->previous()->insertToken("{"); Token * tokOpenBrace=tokAfterCondition->previous(); tokEnd->insertToken("}"); Token * tokCloseBrace=tokEnd->next(); Token::createMutualLinks(tokOpenBrace,tokCloseBrace); tokBracesEnd=tokCloseBrace; } return tokBracesEnd; } void Tokenizer::simplifyCompoundAssignment() { // Simplify compound assignments: // "a+=b" => "a = a + b" for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "[;{}] (| *| (| %name%")) continue; if (tok->next()->str() == "return") continue; // backup current token.. Token * const tok1 = tok; if (tok->next()->str() == "*") tok = tok->next(); if (tok->next() && tok->next()->str() == "(") { tok = tok->next()->link()->next(); } else { // variable.. tok = tok->tokAt(2); while (Token::Match(tok, ". %name%") || Token::Match(tok, "[|(")) { if (tok->str() == ".") tok = tok->tokAt(2); else { // goto "]" or ")" tok = tok->link(); // goto next token.. tok = tok ? tok->next() : nullptr; } } } if (!tok) break; // Is current token at a compound assignment: +=|-=|.. ? const std::string &str = tok->str(); std::string op; // operator used in assignment if (tok->isAssignmentOp() && str.size() == 2) op = str.substr(0, 1); else if (tok->isAssignmentOp() && str.size() == 3) op = str.substr(0, 2); else { tok = tok1; continue; } // Remove the whole statement if it says: "+=0;", "-=0;", "*=1;" or "/=1;" if (Token::Match(tok, "+=|-= 0 ;") || Token::simpleMatch(tok, "|= 0 ;") || Token::Match(tok, "*=|/= 1 ;")) { tok = tok1; while (tok->next()->str() != ";") tok->deleteNext(); } else { // Enclose the rhs in parentheses.. if (!Token::Match(tok->tokAt(2), "[;)]")) { // Only enclose rhs in parentheses if there is some operator bool someOperator = false; for (Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->link() && Token::Match(tok2, "{|[|(")) tok2 = tok2->link(); if (Token::Match(tok2->next(), "[;)]")) { if (someOperator) { tok->insertToken("("); tok2->insertToken(")"); Token::createMutualLinks(tok->next(), tok2->next()); } break; } someOperator |= (tok2->isOp() || tok2->str() == "?"); } } // simplify the compound assignment.. tok->str("="); tok->insertToken(op); std::stack tokend; for (Token *tok2 = tok->previous(); tok2 && tok2 != tok1; tok2 = tok2->previous()) { // Don't duplicate ++ and --. Put preincrement in lhs. Put // postincrement in rhs. if (tok2->tokType() == Token::eIncDecOp) { // pre increment/decrement => don't copy if (tok2->next()->isName()) { continue; } // post increment/decrement => move from lhs to rhs tok->insertToken(tok2->str()); tok2->deleteThis(); continue; } // Copy token from lhs to rhs tok->insertToken(tok2->str()); tok->next()->varId(tok2->varId()); if (Token::Match(tok->next(), "]|)|}")) tokend.push(tok->next()); else if (Token::Match(tok->next(), "(|[|{")) { Token::createMutualLinks(tok->next(), tokend.top()); tokend.pop(); } } } } } bool Tokenizer::simplifyConditions() { bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "! %bool%|%num%")) { tok->deleteThis(); if (Token::Match(tok, "0|false")) tok->str("true"); else tok->str("false"); ret = true; } if (Token::simpleMatch(tok, "&& true &&")) { tok->deleteNext(2); ret = true; } else if (Token::simpleMatch(tok, "|| false ||")) { tok->deleteNext(2); ret = true; } else if (Token::Match(tok, "(|&& true && true &&|)")) { tok->deleteNext(2); ret = true; } else if (Token::Match(tok, "%oror%|( false %oror% false %oror%|)")) { tok->deleteNext(2); ret = true; } else if (Token::simpleMatch(tok, "( true ||") || Token::simpleMatch(tok, "( false &&")) { Token::eraseTokens(tok->next(), tok->link()); ret = true; } else if (Token::simpleMatch(tok, "|| true )") || Token::simpleMatch(tok, "&& false )")) { tok = tok->next(); Token::eraseTokens(tok->next()->link(), tok); ret = true; } else if (Token::simpleMatch(tok, "&& false &&") || Token::simpleMatch(tok, "|| true ||")) { //goto '(' Token *tok2 = tok; while (tok2->previous()) { if (tok2->previous()->str() == ")") tok2 = tok2->previous()->link(); else { tok2 = tok2->previous(); if (tok2->str() == "(") break; } } if (!tok2) continue; //move tok to 'true|false' position tok = tok->next(); //remove everything before 'true|false' Token::eraseTokens(tok2, tok); //remove everything after 'true|false' Token::eraseTokens(tok, tok2->link()); ret = true; } // Change numeric constant in condition to "true" or "false" if (Token::Match(tok, "if|while ( %num% )|%oror%|&&")) { tok->tokAt(2)->str((tok->strAt(2) != "0") ? "true" : "false"); ret = true; } if (Token::Match(tok, "&&|%oror% %num% )|%oror%|&&")) { tok->next()->str((tok->next()->str() != "0") ? "true" : "false"); ret = true; } // Reduce "(%num% == %num%)" => "(true)"/"(false)" if (Token::Match(tok, "&&|%oror%|(") && (Token::Match(tok->next(), "%num% %any% %num%") || Token::Match(tok->next(), "%bool% %any% %bool%")) && Token::Match(tok->tokAt(4), "&&|%oror%|)|?")) { std::string cmp = tok->strAt(2); bool result = false; if (tok->next()->isNumber()) { // Compare numbers if (cmp == "==" || cmp == "!=") { const std::string& op1(tok->next()->str()); const std::string& op2(tok->strAt(3)); bool eq = false; if (MathLib::isInt(op1) && MathLib::isInt(op2)) eq = (MathLib::toLongNumber(op1) == MathLib::toLongNumber(op2)); else { eq = (op1 == op2); // It is inconclusive whether two unequal float representations are numerically equal if (!eq && MathLib::isFloat(op1)) cmp.clear(); } if (cmp == "==") result = eq; else result = !eq; } else { const double op1 = MathLib::toDoubleNumber(tok->next()->str()); const double op2 = MathLib::toDoubleNumber(tok->strAt(3)); if (cmp == ">=") result = (op1 >= op2); else if (cmp == ">") result = (op1 > op2); else if (cmp == "<=") result = (op1 <= op2); else if (cmp == "<") result = (op1 < op2); else cmp.clear(); } } else { // Compare boolean const bool op1 = (tok->next()->str() == std::string("true")); const bool op2 = (tok->strAt(3) == std::string("true")); if (cmp == "==") result = (op1 == op2); else if (cmp == "!=") result = (op1 != op2); else if (cmp == ">=") result = (op1 >= op2); else if (cmp == ">") result = (op1 > op2); else if (cmp == "<=") result = (op1 <= op2); else if (cmp == "<") result = (op1 < op2); else cmp.clear(); } if (! cmp.empty()) { tok = tok->next(); tok->deleteNext(2); tok->str(result ? "true" : "false"); ret = true; } } } return ret; } bool Tokenizer::simplifyConstTernaryOp() { bool ret = false; const Token *templateParameterEnd = nullptr; // The end of the current template parameter list, if any for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "<" && TemplateSimplifier::templateParameters(tok)) templateParameterEnd = tok->findClosingBracket(); if (tok == templateParameterEnd) templateParameterEnd = nullptr; // End of the current template parameter list if (tok->str() != "?") continue; if (!Token::Match(tok->tokAt(-2), "<|=|,|(|[|{|}|;|case|return %bool%|%num%") && !Token::Match(tok->tokAt(-4), "<|=|,|(|[|{|}|;|case|return ( %bool%|%num% )")) continue; const int offset = (tok->previous()->str() == ")") ? 2 : 1; if (tok->strAt(-2*offset) == "<") { if (isC() || !TemplateSimplifier::templateParameters(tok->tokAt(-2*offset))) continue; // '<' is less than; the condition is not a constant } // Find the token ":" then go to the next token Token *colon = skipTernaryOp(tok); if (!colon || colon->previous()->str() != ":" || !colon->next()) continue; //handle the GNU extension: "x ? : y" <-> "x ? x : y" if (colon->previous() == tok->next()) tok->insertToken(tok->strAt(-offset)); // go back before the condition, if possible tok = tok->tokAt(-2); if (offset == 2) { // go further back before the "(" tok = tok->tokAt(-2); //simplify the parentheses tok->deleteNext(); tok->next()->deleteNext(); } if (Token::Match(tok->next(), "false|0")) { // Use code after colon, remove code before it. Token::eraseTokens(tok, colon); tok = tok->next(); ret = true; } // The condition is true. Delete the operator after the ":".. else { // delete the condition token and the "?" tok->deleteNext(2); int ternaryOplevel = 0; for (const Token *endTok = colon; endTok; endTok = endTok->next()) { if (Token::Match(endTok, "(|[|{")) endTok = endTok->link(); else if (endTok->str() == "<" && (endTok->strAt(1) == ">" || TemplateSimplifier::templateParameters(endTok))) endTok = endTok->findClosingBracket(); else if (endTok->str() == "?") ++ternaryOplevel; else if (Token::Match(endTok, ")|}|]|;|,|:|>")) { if (endTok->str() == ":" && ternaryOplevel) --ternaryOplevel; else if (endTok->str() == ">" && !templateParameterEnd) ; else { Token::eraseTokens(colon->tokAt(-2), endTok); ret = true; break; } } } } } return ret; } void Tokenizer::simplifyUndefinedSizeArray() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%type%")) { Token *tok2 = tok->next(); while (tok2 && tok2->str() == "*") tok2 = tok2->next(); if (!Token::Match(tok2, "%name% [ ] ;|[")) continue; tok = tok2->previous(); Token *end = tok2->next(); int count = 0; do { end = end->tokAt(2); ++count; } while (Token::Match(end, "[ ] [;=[]")); if (Token::Match(end, "[;=]")) { do { tok2->deleteNext(2); tok->insertToken("*"); } while (--count); tok = end; } else tok = tok->tokAt(3); } } } void Tokenizer::simplifyCasts() { for (Token *tok = list.front(); tok; tok = tok->next()) { // Don't remove cast in such cases: // *((char *)a + 1) = 0; // Remove cast when casting a function pointer: // (*(void (*)(char *))fp)(x); if (!tok->isName() && Token::simpleMatch(tok->next(), "* (") && !Token::Match(tok->linkAt(2), ") %name%|&")) { tok = tok->linkAt(2); continue; } // #3935 : don't remove cast in such cases: // ((char *)a)[1] = 0; if (tok->str() == "(" && Token::simpleMatch(tok->link(), ") [")) { tok = tok->link(); continue; } // #4164 : ((unsigned char)1) => (1) if (Token::Match(tok->next(), "( %type% ) %num%") && tok->next()->link()->previous()->isStandardType()) { const MathLib::bigint value = MathLib::toLongNumber(tok->next()->link()->next()->str()); int bits = mSettings->char_bit * mTypeSize[tok->next()->link()->previous()->str()]; if (!tok->tokAt(2)->isUnsigned() && bits > 0) bits--; if (bits < 31 && value >= 0 && value < (1LL << bits)) { tok->linkAt(1)->next()->isCast(true); Token::eraseTokens(tok, tok->next()->link()->next()); } continue; } while ((Token::Match(tok->next(), "( %type% *| *| *|&| ) *|&| %name%") && (tok->str() != ")" || tok->tokAt(2)->isStandardType())) || Token::Match(tok->next(), "( const| %type% * *| *|&| ) *|&| %name%") || Token::Match(tok->next(), "( const| %type% %type% *| *| *|&| ) *|&| %name%") || (!tok->isName() && (Token::Match(tok->next(), "( %type% * *| *|&| ) (") || Token::Match(tok->next(), "( const| %type% %type% * *| *|&| ) (")))) { if (tok->isName() && tok->str() != "return") break; if (isCPP() && tok->strAt(-1) == "operator") break; // Remove cast.. Token::eraseTokens(tok, tok->next()->link()->next()); // Set isCasted flag. Token *tok2 = tok->next(); if (!Token::Match(tok2, "%name% [|.")) tok2->isCast(true); else { // TODO: handle more complex expressions tok2->next()->isCast(true); } // Remove '* &' if (Token::simpleMatch(tok, "* &")) { tok->deleteNext(); tok->deleteThis(); } if (tok->str() == ")" && tok->link()->previous()) { // If there was another cast before this, go back // there to check it also. e.g. "(int)(char)x" tok = tok->link()->previous(); } } // Replace pointer casts of 0.. "(char *)0" => "0" while (Token::Match(tok->next(), "( %type% %type%| * *| ) 0")) { tok->linkAt(1)->next()->isCast(true); Token::eraseTokens(tok, tok->next()->link()->next()); if (tok->str() == ")" && tok->link()->previous()) { // If there was another cast before this, go back // there to check it also. e.g. "(char*)(char*)0" tok = tok->link()->previous(); } } if (Token::Match(tok->next(), "dynamic_cast|reinterpret_cast|const_cast|static_cast <")) { Token *tok2 = tok->linkAt(2); if (!Token::simpleMatch(tok2, "> (")) break; tok2->tokAt(2)->isCast(true); Token::eraseTokens(tok, tok2->next()); } } } void Tokenizer::simplifyFunctionParameters() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->link() && Token::Match(tok, "{|[|(")) { tok = tok->link(); } // Find the function e.g. foo( x ) or foo( x, y ) else if (Token::Match(tok, "%name% ( %name% [,)]") && !(tok->strAt(-1) == ":" || tok->strAt(-1) == "," || tok->strAt(-1) == "::")) { // We have found old style function, now we need to change it // First step: Get list of argument names in parentheses std::map argumentNames; bool bailOut = false; Token * tokparam = nullptr; //take count of the function name.. const std::string& funcName(tok->str()); //floating token used to check for parameters Token *tok1 = tok; while (nullptr != (tok1 = tok1->tokAt(2))) { if (!Token::Match(tok1, "%name% [,)]")) { bailOut = true; break; } //same parameters: take note of the parameter if (argumentNames.find(tok1->str()) != argumentNames.end()) tokparam = tok1; else if (tok1->str() != funcName) argumentNames[tok1->str()] = tok1; else { if (tok1->next()->str() == ")") { if (tok1->previous()->str() == ",") { tok1 = tok1->tokAt(-2); tok1->deleteNext(2); } else { tok1 = tok1->previous(); tok1->deleteNext(); bailOut = true; break; } } else { tok1 = tok1->tokAt(-2); tok1->next()->deleteNext(2); } } if (tok1->next()->str() == ")") { tok1 = tok1->tokAt(2); //expect at least a type name after round brace.. if (!tok1 || !tok1->isName()) bailOut = true; break; } } //goto '(' tok = tok->next(); if (bailOut) { tok = tok->link(); continue; } tok1 = tok->link()->next(); // there should be the sequence '; {' after the round parentheses for (const Token* tok2 = tok1; tok2; tok2 = tok2->next()) { if (Token::simpleMatch(tok2, "; {")) break; else if (tok2->str() == "{") { bailOut = true; break; } } if (bailOut) { tok = tok->link(); continue; } // Last step: check out if the declarations between ')' and '{' match the parameters list std::map argumentNames2; while (tok1 && tok1->str() != "{") { if (Token::Match(tok1, "(|)")) { bailOut = true; break; } if (tok1->str() == ";") { if (tokparam) { syntaxError(tokparam); } Token *tok2 = tok1->previous(); while (tok2->str() == "]") tok2 = tok2->link()->previous(); //it should be a name.. if (!tok2->isName()) { bailOut = true; break; } if (argumentNames2.find(tok2->str()) != argumentNames2.end()) { //same parameter names... syntaxError(tok1); } else argumentNames2[tok2->str()] = tok2; if (argumentNames.find(tok2->str()) == argumentNames.end()) { //non-matching parameter... bailout bailOut = true; break; } } tok1 = tok1->next(); } if (bailOut || !tok1) { tok = tok->link(); continue; } //the two containers may not hold the same size... //in that case, the missing parameters are defined as 'int' if (argumentNames.size() != argumentNames2.size()) { //move back 'tok1' to the last ';' tok1 = tok1->previous(); for (std::map::iterator it = argumentNames.begin(); it != argumentNames.end(); ++it) { if (argumentNames2.find(it->first) == argumentNames2.end()) { //add the missing parameter argument declaration tok1->insertToken(";"); tok1->insertToken(it->first); //register the change inside argumentNames2 argumentNames2[it->first] = tok1->next(); tok1->insertToken("int"); } } } while (tok->str() != ")") { //initialize start and end tokens to be moved Token *declStart = argumentNames2[tok->next()->str()]; Token *declEnd = declStart; while (declStart->previous()->str() != ";" && declStart->previous()->str() != ")") declStart = declStart->previous(); while (declEnd->next()->str() != ";" && declEnd->next()->str() != "{") declEnd = declEnd->next(); //remove ';' after declaration declEnd->deleteNext(); //replace the parameter name in the parentheses with all the declaration Token::replace(tok->next(), declStart, declEnd); //since there are changes to tokens, put tok where tok1 is tok = declEnd->next(); } //goto forward and continue tok = tok->next()->link(); } } } void Tokenizer::simplifyPointerToStandardType() { if (!isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "& %name% [ 0 ] !![")) continue; if (!Token::Match(tok->previous(), "[,(=]")) continue; // Remove '[ 0 ]' suffix Token::eraseTokens(tok->next(), tok->tokAt(5)); // Remove '&' prefix tok = tok->previous(); if (!tok) break; tok->deleteNext(); } } void Tokenizer::simplifyFunctionPointers() { for (Token *tok = list.front(); tok; tok = tok->next()) { // #2873 - do not simplify function pointer usage here: // (void)(xy(*p)(0)); if (Token::simpleMatch(tok, ") (")) { tok = tok->next()->link(); continue; } // check for function pointer cast if (Token::Match(tok, "( %type% %type%| *| *| ( * ) (") || Token::Match(tok, "static_cast < %type% %type%| *| *| ( * ) (")) { Token *tok1 = tok; if (isCPP() && tok1->str() == "static_cast") tok1 = tok1->next(); tok1 = tok1->next(); if (Token::Match(tok1->next(), "%type%")) tok1 = tok1->next(); while (tok1->next()->str() == "*") tok1 = tok1->next(); // check that the cast ends if (!Token::Match(tok1->linkAt(4), ") )|>")) continue; // ok simplify this function pointer cast to an ordinary pointer cast tok1->deleteNext(); tok1->next()->deleteNext(); Token::eraseTokens(tok1->next(), tok1->linkAt(2)->next()); continue; } // check for start of statement else if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|,|(|public:|protected:|private:")) continue; if (Token::Match(tok, "delete|else|return|throw|typedef")) continue; while (Token::Match(tok, "%type%|:: %type%|::")) tok = tok->next(); Token *tok2 = (tok && tok->isName()) ? tok->next() : nullptr; while (Token::Match(tok2, "*|&")) tok2 = tok2->next(); if (!tok2 || tok2->str() != "(") continue; while (Token::Match(tok2, "(|:: %type%")) tok2 = tok2->tokAt(2); if (!Token::Match(tok2, "(|:: * *| %name%")) continue; tok2 = tok2->tokAt(2); if (tok2->str() == "*") tok2 = tok2->next(); while (Token::Match(tok2, "%type%|:: %type%|::")) tok2 = tok2->next(); if (!Token::Match(tok2, "%name% ) (") && !Token::Match(tok2, "%name% [ ] ) (") && !(Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") ) ("))) continue; while (tok->str() != "(") tok = tok->next(); // check that the declaration ends if (!tok || !tok->link() || !tok->link()->next()) { syntaxError(nullptr); } Token *endTok = tok->link()->next()->link(); if (Token::simpleMatch(endTok, ") throw (")) endTok = endTok->linkAt(2); if (!Token::Match(endTok, ") const|volatile| const|volatile| ;|,|)|=|[|{")) continue; while (Token::Match(endTok->next(), "const|volatile")) endTok->deleteNext(); // ok simplify this function pointer to an ordinary pointer Token::eraseTokens(tok->link(), endTok->next()); tok->link()->deleteThis(); while (Token::Match(tok, "( %type% ::")) tok->deleteNext(2); tok->deleteThis(); } } bool Tokenizer::simplifyFunctionReturn() { std::map functions; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); else if (Token::Match(tok, "%name% ( ) { return %bool%|%char%|%num%|%str% ; }") && tok->strAt(-1) != "::") { const Token* const any = tok->tokAt(5); functions[tok->str()] = any; tok = any; } } if (functions.empty()) return false; bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "(|[|=|return|%op% %name% ( ) ;|]|)|%cop%")) { tok = tok->next(); auto it = functions.find(tok->str()); if (it != functions.cend()) { tok->str(it->second->str()); tok->deleteNext(2); ret = true; } } } return ret; } void Tokenizer::simplifyVarDecl(const bool only_k_r_fpar) { simplifyVarDecl(list.front(), nullptr, only_k_r_fpar); } void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, const bool only_k_r_fpar) { const bool isCPP11 = mSettings->standards.cpp >= Standards::CPP11; // Split up variable declarations.. // "int a=4;" => "int a; a=4;" bool finishedwithkr = true; for (Token *tok = tokBegin; tok != tokEnd; tok = tok->next()) { if (Token::simpleMatch(tok, "= {")) { tok = tok->next()->link(); } if (!tok) { syntaxError(tokBegin); } if (only_k_r_fpar && finishedwithkr) { if (Token::Match(tok, "(|[|{")) { tok = tok->link(); if (tok->next() && Token::Match(tok, ") !!{")) tok = tok->next(); else continue; } else continue; } else if (tok->str() == "(") { if (isCPP()) { for (Token * tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { if (Token::Match(tok2, "[(,] [")) { // lambda function at tok2->next() // find start of lambda body Token * lambdaBody = tok2; while (lambdaBody && lambdaBody != tok2->link() && lambdaBody->str() != "{") lambdaBody = lambdaBody->next(); if (lambdaBody && lambdaBody != tok2->link() && lambdaBody->link()) simplifyVarDecl(lambdaBody, lambdaBody->link()->next(), only_k_r_fpar); } } } tok = tok->link(); } if (!tok) syntaxError(nullptr); // #7043 invalid code if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|)|public:|protected:|private:")) continue; if (Token::simpleMatch(tok, "template <")) continue; Token *type0 = tok; if (!Token::Match(type0, "::|extern| %type%")) continue; if (Token::Match(type0, "else|return|public:|protected:|private:")) continue; if (isCPP11 && type0->str() == "using") continue; if (isCPP() && type0->str() == "namespace") continue; bool isconst = false; bool isstatic = false; Token *tok2 = type0; int typelen = 1; if (Token::Match(tok2, "::|extern")) { tok2 = tok2->next(); typelen++; } //check if variable is declared 'const' or 'static' or both while (tok2) { if (!Token::Match(tok2, "const|static") && Token::Match(tok2, "%type% const|static")) { tok2 = tok2->next(); ++typelen; } if (tok2->str() == "const") isconst = true; else if (tok2->str() == "static") isstatic = true; else if (Token::Match(tok2, "%type% :: %type%")) { tok2 = tok2->next(); ++typelen; } else break; if (tok2->strAt(1) == "*") break; if (Token::Match(tok2->next(), "& %name% ,")) break; tok2 = tok2->next(); ++typelen; } // strange looking variable declaration => don't split up. if (Token::Match(tok2, "%type% *|&| %name% , %type% *|&| %name%")) continue; if (Token::Match(tok2, "struct|union|class %type%")) { tok2 = tok2->next(); ++typelen; } // check for qualification.. if (Token::Match(tok2, ":: %type%")) { ++typelen; tok2 = tok2->next(); } //skip combinations of templates and namespaces while (!isC() && (Token::Match(tok2, "%type% <") || Token::Match(tok2, "%type% ::"))) { if (tok2->next()->str() == "<" && !TemplateSimplifier::templateParameters(tok2->next())) { tok2 = nullptr; break; } typelen += 2; tok2 = tok2->tokAt(2); if (tok2 && tok2->previous()->str() == "::") continue; int indentlevel = 0; int parens = 0; for (Token *tok3 = tok2; tok3; tok3 = tok3->next()) { ++typelen; if (!parens && tok3->str() == "<") { ++indentlevel; } else if (!parens && tok3->str() == ">") { if (indentlevel == 0) { tok2 = tok3->next(); break; } --indentlevel; } else if (!parens && tok3->str() == ">>") { if (indentlevel <= 1) { tok2 = tok3->next(); break; } indentlevel -= 2; } else if (tok3->str() == "(") { ++parens; } else if (tok3->str() == ")") { if (!parens) { tok2 = nullptr; break; } --parens; } else if (tok3->str() == ";") { break; } } if (Token::Match(tok2, ":: %type%")) { ++typelen; tok2 = tok2->next(); } } //pattern: "%type% *| ... *| const| %name% ,|=" if (Token::Match(tok2, "%type%") || (tok2 && tok2->previous() && tok2->previous()->str() == ">")) { Token *varName = tok2; if (!tok2->previous() || tok2->previous()->str() != ">") varName = varName->next(); else --typelen; //skip all the pointer part bool isPointerOrRef = false; while (Token::simpleMatch(varName, "*") || Token::Match(varName, "& %name% ,")) { isPointerOrRef = true; varName = varName->next(); } while (Token::Match(varName, "%type% %type%")) { if (varName->str() != "const") { ++typelen; } varName = varName->next(); } //non-VLA case if (Token::Match(varName, "%name% ,|=")) { if (varName->str() != "operator") { tok2 = varName->next(); // The ',' or '=' token if (tok2->str() == "=" && (isstatic || (isconst && !isPointerOrRef))) { //do not split const non-pointer variables.. while (tok2 && tok2->str() != "," && tok2->str() != ";") { if (Token::Match(tok2, "{|(|[")) tok2 = tok2->link(); const Token *tok3 = tok2; if (!isC() && tok2->str() == "<" && TemplateSimplifier::templateParameters(tok2) > 0) { tok2 = tok2->findClosingBracket(); } if (!tok2) syntaxError(tok3); // #6881 invalid code tok2 = tok2->next(); } if (tok2 && tok2->str() == ";") tok2 = nullptr; } } else tok2 = nullptr; } //VLA case else if (Token::Match(varName, "%name% [")) { tok2 = varName->next(); while (Token::Match(tok2->link(), "] ,|=|[")) tok2 = tok2->link()->next(); if (!Token::Match(tok2, "=|,")) tok2 = nullptr; if (tok2 && tok2->str() == "=") { while (tok2 && tok2->str() != "," && tok2->str() != ";") { if (Token::Match(tok2, "{|(|[")) tok2 = tok2->link(); tok2 = tok2->next(); } if (tok2 && tok2->str() == ";") tok2 = nullptr; } } // brace initialization else if (Token::Match(varName, "%name% {")) { tok2 = varName->next(); tok2 = tok2->link(); if (tok2) tok2 = tok2->next(); if (tok2 && tok2->str() != ",") tok2 = nullptr; } // parenthesis, functions can't be declared like: // int f1(a,b), f2(c,d); // so if there is a comma assume this is a variable declaration else if (Token::Match(varName, "%name% (") && Token::simpleMatch(varName->linkAt(1), ") ,")) { tok2 = varName->linkAt(1)->next(); } else tok2 = nullptr; } else { tok2 = nullptr; } if (!tok2) { if (only_k_r_fpar) finishedwithkr = false; continue; } if (tok2->str() == ",") { tok2->str(";"); //TODO: should we have to add also template '<>' links? TokenList::insertTokens(tok2, type0, typelen); } else { Token *eq = tok2; while (tok2) { if (Token::Match(tok2, "{|(|[")) tok2 = tok2->link(); else if (!isC() && tok2->str() == "<" && tok2->previous()->isName() && !tok2->previous()->varId()) tok2 = tok2->findClosingBracket(); else if (std::strchr(";,", tok2->str()[0])) { // "type var =" => "type var; var =" const Token *varTok = type0->tokAt(typelen); while (Token::Match(varTok, "*|&|const|volatile")) varTok = varTok->next(); if (!varTok) syntaxError(tok2); // invalid code TokenList::insertTokens(eq, varTok, 2); eq->str(";"); // "= x, " => "= x; type " if (tok2->str() == ",") { tok2->str(";"); TokenList::insertTokens(tok2, type0, typelen); } break; } if (tok2) tok2 = tok2->next(); } } finishedwithkr = (only_k_r_fpar && tok2 && tok2->strAt(1) == "{"); } } void Tokenizer::simplifyStaticConst() { // This function will simplify the token list so that the qualifiers "extern", "static" // and "const" appear in the same order as in the array below. const std::string qualifiers[] = {"extern", "static", "const"}; // Move 'const' before all other qualifiers and types and then // move 'static' before all other qualifiers and types, ... for (Token *tok = list.front(); tok; tok = tok->next()) { bool continue2 = false; for (int i = 0; i < sizeof(qualifiers)/sizeof(qualifiers[0]); i++) { // Keep searching for a qualifier if (!tok->next() || tok->next()->str() != qualifiers[i]) continue; // Look backwards to find the beginning of the declaration Token* leftTok = tok; bool behindOther = false; for (; leftTok; leftTok = leftTok->previous()) { for (int j = 0; j <= i; j++) { if (leftTok->str() == qualifiers[j]) { behindOther = true; break; } } if (behindOther) break; if (!Token::Match(leftTok, "%type%|struct|::") || (isCPP() && Token::Match(leftTok, "private:|protected:|public:|operator|template"))) { break; } } // The token preceding the declaration should indicate the start of a declaration if (leftTok == tok) continue; if (leftTok && !behindOther && !Token::Match(leftTok, ";|{|}|(|,|private:|protected:|public:")) { continue2 = true; break; } // Move the qualifier to the left-most position in the declaration tok->deleteNext(); if (!leftTok) { list.front()->insertToken(qualifiers[i], emptyString, false); list.front()->swapWithNext(); tok = list.front(); } else if (leftTok->next()) { leftTok->next()->insertToken(qualifiers[i], emptyString, true); tok = leftTok->next(); } else { leftTok->insertToken(qualifiers[i]); tok = leftTok; } } if (continue2) continue; } } void Tokenizer::simplifyIfAndWhileAssign() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok->next(), "if|while (")) continue; const Token* tokAt3 = tok->tokAt(3); if (!Token::Match(tokAt3, "!| (| %name% =") && !Token::Match(tokAt3, "!| (| %name% . %name% =") && !Token::Match(tokAt3, "0 == (| %name% =") && !Token::Match(tokAt3, "0 == (| %name% . %name% =")) continue; // simplifying a "while(cond) { }" condition ? const bool iswhile(tok->next()->str() == "while"); // simplifying a "do { } while(cond);" condition ? const bool isDoWhile = iswhile && Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), "do"); Token* openBraceTok = tok->link(); // delete the "if|while" tok->deleteNext(); // Remember if there is a "!" or not. And delete it if there are. const bool isNot(Token::Match(tok->tokAt(2), "!|0")); if (isNot) tok->next()->deleteNext((tok->strAt(2) == "0") ? 2 : 1); // Delete parentheses.. and remember how many there are with // their links. std::stack braces; while (tok->next()->str() == "(") { braces.push(tok->next()->link()); tok->deleteNext(); } // Skip the "%name% = ..." Token *tok2; for (tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == "(") tok2 = tok2->link(); else if (tok2->str() == ")") break; } // Insert "; if|while ( .." tok2 = tok2->previous(); if (tok->strAt(2) == ".") { tok2->insertToken(tok->strAt(3)); tok2->next()->varId(tok->tokAt(3)->varId()); tok2->insertToken("."); } tok2->insertToken(tok->next()->str()); tok2->next()->varId(tok->next()->varId()); while (! braces.empty()) { tok2->insertToken("("); Token::createMutualLinks(tok2->next(), braces.top()); braces.pop(); } if (isNot) tok2->next()->insertToken("!"); tok2->insertToken(iswhile ? "while" : "if"); if (isDoWhile) { tok2->insertToken("}"); Token::createMutualLinks(openBraceTok, tok2->next()); } tok2->insertToken(";"); // delete the extra "}" if (isDoWhile) tok->deleteThis(); // If it's a while loop, insert the assignment in the loop if (iswhile && !isDoWhile) { int indentlevel = 0; Token *tok3 = tok2; for (; tok3; tok3 = tok3->next()) { if (tok3->str() == "{") ++indentlevel; else if (tok3->str() == "}") { if (indentlevel <= 1) break; --indentlevel; } } if (tok3 && indentlevel == 1) { tok3 = tok3->previous(); std::stack braces2; for (tok2 = tok2->next(); tok2 && tok2 != tok; tok2 = tok2->previous()) { tok3->insertToken(tok2->str()); Token *newTok = tok3->next(); newTok->varId(tok2->varId()); newTok->fileIndex(tok2->fileIndex()); newTok->linenr(tok2->linenr()); // link() new tokens manually if (tok2->link()) { if (Token::Match(newTok, "}|)|]|>")) { braces2.push(newTok); } else { Token::createMutualLinks(newTok, braces2.top()); braces2.pop(); } } } } } } } void Tokenizer::simplifyVariableMultipleAssign() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%name% = %name% = %num%|%name% ;")) { // skip intermediate assignments Token *tok2 = tok->previous(); while (tok2 && tok2->str() == "=" && Token::Match(tok2->previous(), "%name%")) { tok2 = tok2->tokAt(-2); } if (!tok2 || tok2->str() != ";") { continue; } Token *stopAt = tok->tokAt(2); const Token *valueTok = stopAt->tokAt(2); const std::string& value(valueTok->str()); tok2 = tok2->next(); while (tok2 != stopAt) { tok2->next()->insertToken(";"); tok2->next()->insertToken(value); tok2 = tok2->tokAt(4); } } } } // Binary operators simplification map static const std::map cAlternativeTokens = { std::make_pair("and", "&&") , std::make_pair("and_eq", "&=") , std::make_pair("bitand", "&") , std::make_pair("bitor", "|") , std::make_pair("not_eq", "!=") , std::make_pair("or", "||") , std::make_pair("or_eq", "|=") , std::make_pair("xor", "^") , std::make_pair("xor_eq", "^=") }; // Simplify the C alternative tokens: // and => && // and_eq => &= // bitand => & // bitor => | // compl => ~ // not => ! // not_eq => != // or => || // or_eq => |= // xor => ^ // xor_eq => ^= bool Tokenizer::simplifyCAlternativeTokens() { /* executable scope level */ int executableScopeLevel = 0; bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{") { if (executableScopeLevel > 0 || Token::simpleMatch(tok->previous(), ") {")) ++executableScopeLevel; continue; } if (tok->str() == "}") { if (executableScopeLevel > 0) --executableScopeLevel; continue; } if (!tok->isName()) continue; const std::map::const_iterator cOpIt = cAlternativeTokens.find(tok->str()); if (cOpIt != cAlternativeTokens.end()) { if (!Token::Match(tok->previous(), "%name%|%num%|%char%|)|]|> %name% %name%|%num%|%char%|%op%|(")) continue; if (Token::Match(tok->next(), "%assign%|%or%|%oror%|&&|*|/|%|^") && !Token::Match(tok->previous(), "%num%|%char% %name% *")) continue; if (executableScopeLevel == 0 && Token::Match(tok, "%name% (")) { const Token *start = tok; while (Token::Match(start, "%name%|*")) start = start->previous(); if (!start || Token::Match(start, "[;}]")) continue; } tok->str(cOpIt->second); ret = true; } else if (Token::Match(tok, "not|compl")) { // Don't simplify 'not p;' (in case 'not' is a type) if (!Token::Match(tok->next(), "%name%|(") || Token::Match(tok->previous(), "[;{}]") || (executableScopeLevel == 0U && tok->strAt(-1) == "(")) continue; tok->str((tok->str() == "not") ? "!" : "~"); ret = true; } } return ret; } // int i(0); => int i; i = 0; // int i(0), j; => int i; i = 0; int j; void Tokenizer::simplifyInitVar() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName() || (tok->previous() && !Token::Match(tok->previous(), "[;{}]"))) continue; if (tok->str() == "return") continue; if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ;")) { tok = initVar(tok); } else if (Token::Match(tok, "%type% *| %name% ( %type% (")) { const Token* tok2 = tok->tokAt(2); if (!tok2->link()) tok2 = tok2->next(); if (!tok2->link() || (tok2->link()->strAt(1) == ";" && !Token::simpleMatch(tok2->linkAt(2), ") ("))) tok = initVar(tok); } else if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ,")) { Token *tok1 = tok->tokAt(5); while (tok1->str() != ",") tok1 = tok1->next(); tok1->str(";"); const int numTokens = (Token::Match(tok, "class|struct|union")) ? 2U : 1U; TokenList::insertTokens(tok1, tok, numTokens); tok = initVar(tok); } } } Token * Tokenizer::initVar(Token * tok) { // call constructor of class => no simplification if (Token::Match(tok, "class|struct|union")) { if (tok->strAt(2) != "*") return tok; tok = tok->next(); } else if (!tok->isStandardType() && tok->next()->str() != "*") return tok; // goto variable name.. tok = tok->next(); if (tok->str() == "*") tok = tok->next(); // sizeof is not a variable name.. if (tok->str() == "sizeof") return tok; // check initializer.. if (tok->tokAt(2)->isStandardType() || tok->strAt(2) == "void") return tok; else if (!tok->tokAt(2)->isNumber() && !Token::Match(tok->tokAt(2), "%type% (") && tok->strAt(2) != "&" && tok->tokAt(2)->varId() == 0) return tok; // insert '; var =' tok->insertToken(";"); tok->next()->insertToken(tok->str()); tok->tokAt(2)->varId(tok->varId()); tok = tok->tokAt(2); tok->insertToken("="); // goto '('.. tok = tok->tokAt(2); // delete ')' tok->link()->deleteThis(); // delete this tok->deleteThis(); return tok; } bool Tokenizer::simplifyKnownVariables() { // return value for function. Set to true if any simplifications are made bool ret = false; // constants.. { std::unordered_map constantValues; std::map constantVars; std::unordered_map> constantValueUsages; bool goback = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (goback) { tok = tok->previous(); goback = false; } // Reference to variable if (Token::Match(tok, "%type%|* & %name% = %name% ;")) { Token *start = tok->previous(); while (Token::Match(start,"%type%|*|&")) start = start->previous(); if (!Token::Match(start,"[;{}]")) continue; const Token *reftok = tok->tokAt(2); const Token *vartok = reftok->tokAt(2); int level = 0; for (Token *tok2 = tok->tokAt(6); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { ++level; } else if (tok2->str() == "}") { if (level <= 0) break; --level; } else if (tok2->varId() == reftok->varId()) { tok2->str(vartok->str()); tok2->varId(vartok->varId()); } } Token::eraseTokens(start, tok->tokAt(6)); tok = start; } if (tok->isName() && (Token::Match(tok, "static| const| static| %type% const| %name% = %any% ;") || Token::Match(tok, "static| const| static| %type% const| %name% ( %any% ) ;"))) { bool isconst = false; for (const Token *tok2 = tok; (tok2->str() != "=") && (tok2->str() != "("); tok2 = tok2->next()) { if (tok2->str() == "const") { isconst = true; break; } } if (!isconst) continue; Token *tok1 = tok; // start of statement if (tok != list.front() && !Token::Match(tok->previous(),";|{|}|private:|protected:|public:")) continue; // skip "const" and "static" while (Token::Match(tok, "const|static")) tok = tok->next(); // pod type if (!tok->isStandardType()) continue; Token * const vartok = (tok->next() && tok->next()->str() == "const") ? tok->tokAt(2) : tok->next(); const Token * const valuetok = vartok->tokAt(2); if (Token::Match(valuetok, "%bool%|%char%|%num%|%str% )| ;")) { // record a constant value for this variable constantValues[vartok->varId()] = valuetok->str(); constantVars[vartok->varId()] = tok1; } } else if (tok->varId()) { // find the entry for the known variable, if any. Exclude the location where the variable is assigned with next == "=" if (constantValues.find(tok->varId()) != constantValues.end() && tok->next()->str() != "=") { constantValueUsages[tok->varId()].push_back(tok); } } } for (auto constantVar = constantVars.rbegin(); constantVar != constantVars.rend(); constantVar++) { bool referenceFound = false; std::list usageList = constantValueUsages[constantVar->first]; for (Token* usage : usageList) { // check if any usages of each known variable are a reference if (Token::Match(usage->tokAt(-2), "(|[|,|{|return|%op% & %varid%", constantVar->first)) { referenceFound = true; break; } } if (!referenceFound) { // replace all usages of non-referenced known variables with their value for (Token* usage : usageList) { usage->str(constantValues[constantVar->first]); } Token* startTok = constantVar->second; // remove variable assignment statement while (startTok->next()->str() != ";") startTok->deleteNext(); startTok->deleteNext(); // #8579 if we can we want another token to delete startTok. if we can't it doesn't matter if (startTok->previous()) { startTok->previous()->deleteNext(); } else if (startTok->next()) { startTok->next()->deletePrevious(); } else { startTok->deleteThis(); } startTok = nullptr; constantVar->second = nullptr; ret = true; } } } // variable id for local, float/double, array variables std::set localvars; std::set floatvars; std::set arrays; // auto variables.. for (Token *tok = list.front(); tok; tok = tok->next()) { // Search for a block of code Token * const start = const_cast(startOfExecutableScope(tok)); if (!start) continue; for (const Token *tok2 = start->previous(); tok2 && !Token::Match(tok2, "[;{}]"); tok2 = tok2->previous()) { if (tok2->varId() != 0) localvars.insert(tok2->varId()); } tok = start; // parse the block of code.. int indentlevel = 0; Token *tok2 = tok; for (; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[;{}] %type% %name%|*")) { bool isfloat = false; bool ispointer = false; const Token *vartok = tok2->next(); while (Token::Match(vartok, "%name%|* %name%|*")) { if (Token::Match(vartok, "float|double")) isfloat = true; if (vartok->str() == "*") ispointer = true; vartok = vartok->next(); } if (Token::Match(vartok, "%var% ;|[")) localvars.insert(vartok->varId()); if (isfloat && !ispointer && Token::Match(vartok, "%var% ;")) floatvars.insert(vartok->varId()); if (Token::Match(vartok, "%var% [")) arrays.insert(vartok->varId()); } if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { --indentlevel; if (indentlevel <= 0) break; } else if (Token::simpleMatch(tok2, "for (")) tok2 = tok2->next()->link(); else if (tok2->previous()->str() != "*" && !Token::Match(tok2->tokAt(-2), "* --|++") && (Token::Match(tok2, "%name% = %bool%|%char%|%num%|%str%|%name% ;") || Token::Match(tok2, "%name% [ %num%| ] = %str% ;") || Token::Match(tok2, "%name% = & %name% ;") || (Token::Match(tok2, "%name% = & %name% [ 0 ] ;") && arrays.find(tok2->tokAt(3)->varId()) != arrays.end()))) { const int varid = tok2->varId(); if (varid == 0) continue; if (Token::Match(tok2->previous(), "[;{}]") && localvars.find(varid) == localvars.end()) continue; // initialization of static variable => the value is not *known* { bool isstatic = false; const Token *decl = tok2->previous(); while (decl && (decl->isName() || decl->str() == "*")) { if (decl->str() == "static") { isstatic = true; break; } decl = decl->previous(); } if (isstatic) continue; } // skip loop variable if (Token::Match(tok2->tokAt(-2), "(|:: %type%")) { const Token *tok3 = tok2->previous(); do { tok3 = tok3->tokAt(-2); } while (Token::Match(tok3->previous(), ":: %type%")); if (Token::Match(tok3->tokAt(-2), "for ( %type%")) continue; } // struct name.. if (Token::Match(tok2, "%varid% = &| %varid%", tok2->varId())) continue; const std::string structname = Token::Match(tok2->tokAt(-3), "[;{}] %name% .") ? std::string(tok2->strAt(-2) + " .") : std::string(); const Token * const valueToken = tok2->tokAt(2); std::string value; nonneg int valueVarId = 0; Token *tok3 = nullptr; bool valueIsPointer = false; // there could be a hang here if tok2 is moved back by the function calls below for some reason if (Settings::terminated()) return false; if (!simplifyKnownVariablesGetData(varid, &tok2, &tok3, value, valueVarId, valueIsPointer, floatvars.find(tok2->varId()) != floatvars.end())) continue; if (valueVarId > 0 && arrays.find(valueVarId) != arrays.end()) continue; ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, structname, value, valueVarId, valueIsPointer, valueToken, indentlevel); } else if (Token::Match(tok2, "strcpy|sprintf ( %name% , %str% ) ;")) { const int varid(tok2->tokAt(2)->varId()); if (varid == 0) continue; const Token * const valueToken = tok2->tokAt(4); std::string value(valueToken->str()); if (tok2->str() == "sprintf") { std::string::size_type n = 0; while ((n = value.find("%%", n)) != std::string::npos) { // Replace "%%" with "%" - erase the first '%' and continue past the second '%' value.erase(n, 1); ++n; } } const int valueVarId(0); const bool valueIsPointer(false); Token *tok3 = tok2->tokAt(6); ret |= simplifyKnownVariablesSimplify(&tok2, tok3, varid, emptyString, value, valueVarId, valueIsPointer, valueToken, indentlevel); // there could be a hang here if tok2 was moved back by the function call above for some reason if (Settings::terminated()) return false; } } if (tok2) tok = tok2->previous(); } return ret; } bool Tokenizer::simplifyKnownVariablesGetData(nonneg int varid, Token **_tok2, Token **_tok3, std::string &value, nonneg int &valueVarId, bool &valueIsPointer, bool floatvar) { Token *tok2 = *_tok2; Token *tok3 = nullptr; if (Token::simpleMatch(tok2->tokAt(-2), "for (")) { // only specific for loops is handled if (!Token::Match(tok2, "%varid% = %num% ; %varid% <|<= %num% ; ++| %varid% ++| ) {", varid)) return false; // is there a "break" in the for loop? bool hasbreak = false; const Token* end4 = tok2->linkAt(-1)->linkAt(1); for (const Token *tok4 = tok2->previous()->link(); tok4 != end4; tok4 = tok4->next()) { if (tok4->str() == "break") { hasbreak = true; break; } } if (hasbreak) return false; // no break => the value of the counter value is known after the for loop.. const Token* compareTok = tok2->tokAt(5); if (compareTok->str() == "<") { value = compareTok->next()->str(); valueVarId = compareTok->next()->varId(); } else value = MathLib::toString(MathLib::toLongNumber(compareTok->next()->str()) + 1); // Skip for-body.. tok3 = tok2->previous()->link()->next()->link()->next(); } else { value = tok2->strAt(2); valueVarId = tok2->tokAt(2)->varId(); if (tok2->strAt(1) == "[") { value = tok2->next()->link()->strAt(2); valueVarId = 0; } else if (value == "&") { value = tok2->strAt(3); valueVarId = tok2->tokAt(3)->varId(); // *ptr = &var; *ptr = 5; // equals // var = 5; not *var = 5; if (tok2->strAt(4) == ";") valueIsPointer = true; } // Add a '.0' to a decimal value and therefore convert it to an floating point number. else if (MathLib::isDec(tok2->strAt(2)) && floatvar) { value += ".0"; } // float variable: convert true/false to 1.0 / 0.0 else if (tok2->tokAt(2)->isBoolean() && floatvar) { value = (value == "true") ? "1.0" : "0.0"; } if (Token::simpleMatch(tok2->next(), "= &")) tok2 = tok2->tokAt(3); tok3 = tok2->next(); } *_tok2 = tok2; *_tok3 = tok3; return true; } bool Tokenizer::simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, nonneg int varid, const std::string &structname, std::string &value, nonneg int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel) const { const bool pointeralias(valueToken->isName() || Token::Match(valueToken, "& %name% [")); const bool varIsGlobal = (indentlevel == 0); const bool printDebug = mSettings->debugwarnings; if (mErrorLogger && !list.getFiles().empty()) mErrorLogger->reportProgress(list.getFiles()[0], "Tokenize (simplifyKnownVariables)", tok3->progressValue()); if (isMaxTime()) return false; bool ret = false; Token* bailOutFromLoop = nullptr; int indentlevel3 = indentlevel; bool ret3 = false; for (; tok3; tok3 = tok3->next()) { if (tok3->str() == "{") { ++indentlevel3; } else if (tok3->str() == "}") { --indentlevel3; if (indentlevel3 < indentlevel) { if (Token::Match((*tok2)->tokAt(-7), "%type% * %name% ; %name% = & %name% ;") && (*tok2)->strAt(-5) == (*tok2)->strAt(-3)) { (*tok2) = (*tok2)->tokAt(-4); Token::eraseTokens((*tok2), (*tok2)->tokAt(6)); } break; } } // Stop if there is a pointer alias and a shadow variable is // declared in an inner scope (#3058) if (valueIsPointer && tok3->varId() > 0 && tok3->previous() && (tok3->previous()->isName() || tok3->previous()->str() == "*") && valueToken->str() == "&" && valueToken->next() && valueToken->next()->isName() && tok3->str() == valueToken->next()->str() && tok3->varId() > valueToken->next()->varId()) { // more checking if this is a variable declaration bool decl = true; for (const Token *tok4 = tok3->previous(); tok4; tok4 = tok4->previous()) { if (Token::Match(tok4, "[;{}]")) break; else if (tok4->isName()) { if (tok4->varId() > 0) { decl = false; break; } } else if (!Token::Match(tok4, "[&*]")) { decl = false; break; } } if (decl) break; } // Stop if label is found if (Token::Match(tok3, "; %type% : ;")) break; // Stop if break/continue is found .. if (Token::Match(tok3, "break|continue")) break; if ((indentlevel3 > 1 || !Token::simpleMatch(Token::findsimplematch(tok3,";"), "; }")) && tok3->str() == "return") ret3 = true; if (ret3 && tok3->str() == ";") break; if (pointeralias && Token::Match(tok3, ("!!= " + value).c_str())) break; // Stop if a loop is found if (pointeralias && Token::Match(tok3, "do|for|while")) break; // Stop if unknown function call is seen and the variable is global: it might be // changed by the function call if (varIsGlobal && tok3->str() == ")" && tok3->link() && Token::Match(tok3->link()->tokAt(-2), "[;{}] %name% (") && !Token::Match(tok3->link()->previous(), "if|for|while|switch|BOOST_FOREACH")) break; // Stop if something like 'while (--var)' is found if (Token::Match(tok3, "for|while|do")) { const Token *endpar = tok3->next()->link(); if (Token::simpleMatch(endpar, ") {")) endpar = endpar->next()->link(); bool bailout = false; for (const Token *tok4 = tok3; tok4 && tok4 != endpar; tok4 = tok4->next()) { if (Token::Match(tok4, "++|-- %varid%", varid) || Token::Match(tok4, "%varid% ++|--|=", varid)) { bailout = true; break; } } if (bailout) break; } if (bailOutFromLoop) { // This could be a loop, skip it, but only if it doesn't contain // the variable we are checking for. If it contains the variable // we will bail out. if (tok3->varId() == varid) { // Continue //tok2 = bailOutFromLoop; break; } else if (tok3 == bailOutFromLoop) { // We have skipped the loop bailOutFromLoop = nullptr; continue; } continue; } else if (tok3->str() == "{" && tok3->previous()->str() == ")") { // There is a possible loop after the assignment. Try to skip it. if (tok3->previous()->link() && tok3->previous()->link()->strAt(-1) != "if") bailOutFromLoop = tok3->link(); continue; } // Variable used in realloc (see Ticket #1649) if (Token::Match(tok3, "%name% = realloc ( %name% ,") && tok3->varId() == varid && tok3->tokAt(4)->varId() == varid) { tok3->tokAt(4)->str(value); ret = true; } // condition "(|&&|%OROR% %varid% )|&&|%OROR%|; if (!Token::Match(tok3->previous(), "( %name% )") && Token::Match(tok3->previous(), "&&|(|%oror% %varid% &&|%oror%|)|;", varid)) { tok3->str(value); tok3->varId(valueVarId); ret = true; } // parameter in function call.. if (tok3->varId() == varid && Token::Match(tok3->previous(), "[(,] %name% [,)]")) { // If the parameter is passed by value then simplify it if (isFunctionParameterPassedByValue(tok3)) { tok3->str(value); tok3->varId(valueVarId); ret = true; } } // Variable is used somehow in a non-defined pattern => bail out if (tok3->varId() == varid) { // This is a really generic bailout so let's try to avoid this. // There might be lots of false negatives. if (printDebug) { // FIXME: Fix all the debug warnings for values and then // remove this bailout if (pointeralias) break; // suppress debug-warning when calling member function if (Token::Match(tok3->next(), ". %name% (")) break; // suppress debug-warning when assignment if (tok3->strAt(1) == "=") break; // taking address of variable.. if (Token::Match(tok3->tokAt(-2), "return|= & %name% ;")) break; // parameter in function call.. if (Token::Match(tok3->tokAt(-2), "%name% ( %name% ,|)") || Token::Match(tok3->previous(), ", %name% ,|)")) break; // conditional increment if (Token::Match(tok3->tokAt(-3), ") { ++|--") || Token::Match(tok3->tokAt(-2), ") { %name% ++|--")) break; reportError(tok3, Severity::debug, "debug", "simplifyKnownVariables: bailing out (variable="+tok3->str()+", value="+value+")"); } break; } // Using the variable in condition.. if (Token::Match(tok3->previous(), ("if ( " + structname + " %varid% %cop%|)").c_str(), varid) || Token::Match(tok3, ("( " + structname + " %varid% %comp%").c_str(), varid) || Token::Match(tok3, ("%comp%|!|= " + structname + " %varid% %cop%|)|;").c_str(), varid) || Token::Match(tok3->previous(), "strlen|free ( %varid% )", varid)) { if (value[0] == '\"' && tok3->previous()->str() != "strlen") { // bail out if value is a string unless if it's just given // as parameter to strlen break; } if (!structname.empty()) { tok3->deleteNext(2); } if (Token::Match(valueToken, "& %name% ;")) { tok3->insertToken("&"); tok3 = tok3->next(); } tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); ret = true; } // pointer alias used in condition.. if (Token::Match(valueToken,"& %name% ;") && Token::Match(tok3, ("( * " + structname + " %varid% %cop%").c_str(), varid)) { tok3->deleteNext(); if (!structname.empty()) tok3->deleteNext(2); tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); ret = true; } // Delete pointer alias if (isCPP() && pointeralias && (tok3->str() == "delete") && tok3->next() && (Token::Match(tok3->next(), "%varid% ;", varid) || Token::Match(tok3->next(), "[ ] %varid%", varid))) { tok3 = (tok3->next()->str() == "[") ? tok3->tokAt(3) : tok3->next(); tok3->str(value); tok3->varId(valueVarId); ret = true; } // Variable is used in function call.. if (Token::Match(tok3, ("%name% ( " + structname + " %varid% ,").c_str(), varid)) { static const char * const functionName[] = { // always simplify "strcmp", "strdup", // don't simplify buffer value "memcmp","memcpy","memmove","memset","strcpy","strncmp","strncpy" }; for (int i = 0; i < (sizeof(functionName) / sizeof(*functionName)); ++i) { if (valueVarId == 0U && i >= 2) break; if (tok3->str() == functionName[i]) { Token *par1 = tok3->tokAt(2); if (!structname.empty()) { par1->deleteNext(); par1->deleteThis(); } par1->str(value); par1->varId(valueVarId); break; } } } // Variable is used as 2nd parameter in function call.. if (Token::Match(tok3, ("%name% ( %any% , " + structname + " %varid% ,|)").c_str(), varid)) { static const char * const functionName[] = { // always simplify "strcmp","strcpy","strncmp","strncpy", // don't simplify buffer value "memcmp","memcpy","memmove" }; for (int i = 0; i < (sizeof(functionName) / sizeof(*functionName)); ++i) { if (valueVarId == 0U && i >= 4) break; if (tok3->str() == functionName[i]) { Token *par = tok3->tokAt(4); if (!structname.empty()) { par->deleteNext(); par->deleteThis(); } par->str(value); par->varId(valueVarId); break; } } } // array usage if (value[0] != '\"' && Token::Match(tok3, ("[(,] " + structname + " %varid% [|%cop%").c_str(), varid)) { if (!structname.empty()) { tok3->deleteNext(2); } tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); ret = true; } // The >> operator is sometimes used to assign a variable in C++ if (isCPP() && Token::Match(tok3, (">> " + structname + " %varid%").c_str(), varid)) { // bailout for such code: ; std :: cin >> i ; const Token *prev = tok3->previous(); while (prev && prev->str() != "return" && Token::Match(prev, "%name%|::|*")) prev = prev->previous(); if (Token::Match(prev, ";|{|}|>>")) break; } // Variable is used in calculation.. if (((tok3->previous()->varId() > 0) && Token::Match(tok3, ("& " + structname + " %varid%").c_str(), varid)) || (Token::Match(tok3, ("[=+-*/%^|[] " + structname + " %varid% [=?+-*/%^|;])]").c_str(), varid) && !Token::Match(tok3, ("= " + structname + " %name% =").c_str())) || Token::Match(tok3, ("[(=+-*/%^|[] " + structname + " %varid% <<|>>").c_str(), varid) || Token::Match(tok3, ("<<|>> " + structname + " %varid% %cop%|;|]|)").c_str(), varid) || Token::Match(tok3->previous(), ("[=+-*/%^|[] ( " + structname + " %varid% !!=").c_str(), varid)) { if (value[0] == '\"') break; if (!structname.empty()) { tok3->deleteNext(2); ret = true; } tok3 = tok3->next(); if (tok3->str() != value) ret = true; tok3->str(value); tok3->varId(valueVarId); if (tok3->previous()->str() == "*" && (valueIsPointer || Token::Match(valueToken, "& %name% ;"))) { tok3 = tok3->previous(); tok3->deleteThis(); ret = true; } else if (Token::Match(valueToken, "& %name% ;")) tok3->insertToken("&", emptyString, true); } if (Token::simpleMatch(tok3, "= {")) { const Token* const end4 = tok3->linkAt(1); for (const Token *tok4 = tok3; tok4 != end4; tok4 = tok4->next()) { if (Token::Match(tok4, "{|, %varid% ,|}", varid)) { tok4->next()->str(value); tok4->next()->varId(valueVarId); ret = true; } } } // Using the variable in for-condition.. if (Token::simpleMatch(tok3, "for (")) { for (Token *tok4 = tok3->tokAt(2); tok4; tok4 = tok4->next()) { if (Token::Match(tok4, "(|)")) break; // Replace variable used in condition.. if (Token::Match(tok4, "; %name% <|<=|!= %name% ; ++| %name% ++| )")) { const Token *inctok = tok4->tokAt(5); if (inctok->str() == "++") inctok = inctok->next(); if (inctok->varId() == varid) break; if (tok4->next()->varId() == varid) { tok4->next()->str(value); tok4->next()->varId(valueVarId); ret = true; } if (tok4->tokAt(3)->varId() == varid) { tok4->tokAt(3)->str(value); tok4->tokAt(3)->varId(valueVarId); ret = true; } } } } if (indentlevel == indentlevel3 && Token::Match(tok3->next(), "%varid% ++|--", varid) && MathLib::isInt(value)) { const std::string op(tok3->strAt(2)); if (Token::Match(tok3, "[{};] %any% %any% ;")) { tok3->deleteNext(3); } else { tok3 = tok3->next(); tok3->str(value); tok3->varId(valueVarId); tok3->deleteNext(); } value = MathLib::incdec(value, op); if (!Token::simpleMatch((*tok2)->tokAt(-2), "for (")) { (*tok2)->tokAt(2)->str(value); (*tok2)->tokAt(2)->varId(valueVarId); } ret = true; } if (indentlevel == indentlevel3 && Token::Match(tok3->next(), "++|-- %varid%", varid) && MathLib::isInt(value) && !Token::Match(tok3->tokAt(3), "[.[]")) { value = MathLib::incdec(value, tok3->next()->str()); (*tok2)->tokAt(2)->str(value); (*tok2)->tokAt(2)->varId(valueVarId); if (Token::Match(tok3, "[;{}] %any% %any% ;")) { tok3->deleteNext(3); } else { tok3->deleteNext(); tok3->next()->str(value); tok3->next()->varId(valueVarId); } tok3 = tok3->next(); ret = true; } // return variable.. if (Token::Match(tok3, "return %varid% %any%", varid) && valueToken->str() != "&" && (tok3->tokAt(2)->isExtendedOp() || tok3->strAt(2) == ";") && value[0] != '\"') { tok3->next()->str(value); tok3->next()->varId(valueVarId); } else if (pointeralias && Token::Match(tok3, "return * %varid% ;", varid) && value[0] != '\"') { tok3->deleteNext(); tok3->next()->str(value); tok3->next()->varId(valueVarId); } } return ret; } void Tokenizer::elseif() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "else if")) continue; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "(|{|[")) tok2 = tok2->link(); if (Token::Match(tok2, "}|;")) { if (tok2->next() && tok2->next()->str() != "else") { tok->insertToken("{"); tok2->insertToken("}"); Token::createMutualLinks(tok->next(), tok2->next()); break; } } } } } bool Tokenizer::simplifyRedundantParentheses() { bool ret = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "(") continue; if (isCPP() && Token::simpleMatch(tok->previous(), "} (") && Token::Match(tok->previous()->link()->previous(), "%name%|> {")) continue; if (Token::simpleMatch(tok, "( {")) continue; if (Token::Match(tok->link(), ") %num%")) { tok = tok->link(); continue; } // !!operator = ( x ) ; if (tok->strAt(-2) != "operator" && tok->previous() && tok->previous()->str() == "=" && tok->next() && tok->next()->str() != "{" && Token::simpleMatch(tok->link(), ") ;")) { tok->link()->deleteThis(); tok->deleteThis(); continue; } while (Token::simpleMatch(tok, "( (") && tok->link() && tok->link()->previous() == tok->next()->link()) { // We have "(( *something* ))", remove the inner // parentheses tok->deleteNext(); tok->link()->tokAt(-2)->deleteNext(); ret = true; } if (isCPP() && Token::Match(tok->tokAt(-2), "[;{}=(] new (") && Token::Match(tok->link(), ") [;,{}[]")) { // Remove the parentheses in "new (type)" constructs tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "! ( %name% )")) { // Remove the parentheses tok->deleteThis(); tok->deleteNext(); ret = true; } if (Token::Match(tok->previous(), "[(,;{}] ( %name% ) .")) { // Remove the parentheses tok->deleteThis(); tok->deleteNext(); ret = true; } if (Token::Match(tok->previous(), "[(,;{}] ( %name% (") && tok->link()->previous() == tok->linkAt(2)) { // We have "( func ( *something* ))", remove the outer // parentheses tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "[,;{}] ( delete [| ]| %name% ) ;")) { // We have "( delete [| ]| var )", remove the outer // parentheses tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (!Token::simpleMatch(tok->tokAt(-2), "operator delete") && Token::Match(tok->previous(), "delete|; (") && (tok->previous()->str() != "delete" || tok->next()->varId() > 0) && Token::Match(tok->link(), ") ;|,")) { tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "[(!*;{}] ( %name% )") && (tok->next()->varId() != 0 || Token::Match(tok->tokAt(3), "[+-/=]")) && !tok->next()->isStandardType()) { // We have "( var )", remove the parentheses tok->deleteThis(); tok->deleteNext(); ret = true; } while (Token::Match(tok->previous(), "[;{}[(,!*] ( %name% .")) { Token *tok2 = tok->tokAt(2); while (Token::Match(tok2, ". %name%")) { tok2 = tok2->tokAt(2); } if (tok2 != tok->link()) break; // We have "( var . var . ... . var )", remove the parentheses tok = tok->previous(); tok->deleteNext(); tok2->deleteThis(); ret = true; } if (Token::simpleMatch(tok->previous(), "? (") && Token::simpleMatch(tok->link(), ") :")) { const Token *tok2 = tok->next(); while (tok2 && (Token::Match(tok2,"%bool%|%num%|%name%") || tok2->isArithmeticalOp())) tok2 = tok2->next(); if (tok2 && tok2->str() == ")") { tok->link()->deleteThis(); tok->deleteThis(); ret = true; continue; } } while (Token::Match(tok->previous(), "[{([,] ( !!{") && Token::Match(tok->link(), ") [;,])]") && !Token::simpleMatch(tok->tokAt(-2), "operator ,") && // Ticket #5709 !Token::findsimplematch(tok, ",", tok->link())) { // We have "( ... )", remove the parentheses tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::simpleMatch(tok->previous(), ", (") && Token::simpleMatch(tok->link(), ") =")) { tok->link()->deleteThis(); tok->deleteThis(); ret = true; } // Simplify "!!operator !!%name%|)|>|>> ( %num%|%bool% ) %op%|;|,|)" if (Token::Match(tok, "( %bool%|%num% ) %cop%|;|,|)") && tok->strAt(-2) != "operator" && tok->previous() && !Token::Match(tok->previous(), "%name%|)") && (!(isCPP() && Token::Match(tok->previous(),">|>>")))) { tok->link()->deleteThis(); tok->deleteThis(); ret = true; } if (Token::Match(tok->previous(), "*|& ( %name% )")) { // We may have a variable declaration looking like "type_name *(var_name)" Token *tok2 = tok->tokAt(-2); while (Token::Match(tok2, "%type%|static|const|extern") && tok2->str() != "operator") { tok2 = tok2->previous(); } if (tok2 && !Token::Match(tok2, "[;,{]")) { // Not a variable declaration } else { tok->deleteThis(); tok->deleteNext(); } } } return ret; } void Tokenizer::simplifyTypeIntrinsics() { static const std::unordered_map intrinsics = { { "__has_nothrow_assign", "has_nothrow_assign" }, { "__has_nothrow_constructor", "has_nothrow_constructor" }, { "__has_nothrow_copy", "has_nothrow_copy" }, { "__has_trivial_assign", "has_trivial_assign" }, { "__has_trivial_constructor", "has_trivial_constructor" }, { "__has_trivial_copy", "has_trivial_copy" }, { "__has_trivial_destructor", "has_trivial_destructor" }, { "__has_virtual_destructor", "has_virtual_destructor" }, { "__is_abstract", "is_abstract" }, { "__is_aggregate", "is_aggregate" }, { "__is_assignable", "is_assignable" }, { "__is_base_of", "is_base_of" }, { "__is_class", "is_class" }, { "__is_constructible", "is_constructible" }, { "__is_convertible_to", "is_convertible_to" }, { "__is_destructible", "is_destructible" }, { "__is_empty", "is_empty" }, { "__is_enum", "is_enum" }, { "__is_final", "is_final" }, { "__is_nothrow_assignable", "is_nothrow_assignable" }, { "__is_nothrow_constructible", "is_nothrow_constructible" }, { "__is_nothrow_destructible", "is_nothrow_destructible" }, { "__is_pod", "is_pod" }, { "__is_polymorphic", "is_polymorphic" }, { "__is_trivially_assignable", "is_trivially_assignable" }, { "__is_trivially_constructible", "is_trivially_constructible" }, { "__is_union", "is_union" }, }; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; auto p = intrinsics.find(tok->str()); if (p == intrinsics.end()) continue; Token * end = tok->next()->link(); Token * prev = tok->previous(); tok->str(p->second); prev->insertToken("::"); prev->insertToken("std"); tok->next()->str("<"); end->str(">"); end->insertToken("}"); end->insertToken("{"); Token::createMutualLinks(end->tokAt(1), end->tokAt(2)); } } void Tokenizer::simplifyCharAt() { // Replace "string"[0] with 's' for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%str% [ %num% ]")) { const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(2)); // Check within range if (index >= 0 && index <= Token::getStrLength(tok)) { tok->str("'" + Token::getCharAt(tok, index) + "'"); tok->deleteNext(3); } } } } void Tokenizer::simplifyReference() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { // starting executable scope.. Token *start = const_cast(startOfExecutableScope(tok)); if (start) { tok = start; // replace references in this scope.. Token * const end = tok->link(); for (Token *tok2 = tok; tok2 && tok2 != end; tok2 = tok2->next()) { // found a reference.. if (Token::Match(tok2, "[;{}] %type% & %name% (|= %name% )| ;")) { const int refId = tok2->tokAt(3)->varId(); if (!refId) continue; // replace reference in the code.. for (Token *tok3 = tok2->tokAt(7); tok3 && tok3 != end; tok3 = tok3->next()) { if (tok3->varId() == refId) { tok3->str(tok2->strAt(5)); tok3->varId(tok2->tokAt(5)->varId()); } } tok2->deleteNext(6+(tok2->strAt(6)==")" ? 1 : 0)); } } tok = end; } } } bool Tokenizer::simplifyCalculations() { return mTemplateSimplifier->simplifyCalculations(nullptr, nullptr, false); } void Tokenizer::simplifyOffsetPointerDereference() { // Replace "*(str + num)" => "str[num]" and // Replace "*(str - num)" => "str[-num]" for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName() && !tok->isLiteral() && !Token::Match(tok, "]|)|++|--") && Token::Match(tok->next(), "* ( %name% +|- %num%|%name% )")) { // remove '* (' tok->deleteNext(2); // '+'->'[' tok = tok->tokAt(2); Token* const openBraceTok = tok; const bool isNegativeIndex = (tok->str() == "-"); tok->str("["); // Insert a "-" in front of the number or variable if (isNegativeIndex) { if (tok->next()->isName()) { tok->insertToken("-"); tok = tok->next(); } else tok->next()->str(std::string("-") + tok->next()->str()); } tok = tok->tokAt(2); tok->str("]"); Token::createMutualLinks(openBraceTok, tok); } } } void Tokenizer::simplifyOffsetPointerReference() { std::set pod; for (const Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isStandardType()) { tok = tok->next(); while (tok && (tok->str() == "*" || tok->isName())) { if (tok->varId() > 0) { pod.insert(tok->varId()); break; } tok = tok->next(); } if (!tok) break; } } for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "%num%|%name%|]|)") && (Token::Match(tok->next(), "& %name% [ %num%|%name% ] !!["))) { tok = tok->next(); if (tok->next()->varId()) { if (pod.find(tok->next()->varId()) == pod.end()) { tok = tok->tokAt(5); if (!tok) syntaxError(tok); continue; } } // '&' => '(' tok->str("("); tok = tok->next(); // '[' => '+' tok->deleteNext(); tok->insertToken("+"); tok = tok->tokAt(3); //remove ']' tok->str(")"); Token::createMutualLinks(tok->tokAt(-4), tok); } } } void Tokenizer::simplifyNestedStrcat() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (! Token::Match(tok, "[;{}] strcat ( strcat (")) { continue; } // find inner strcat call Token *tok2 = tok->tokAt(3); while (Token::simpleMatch(tok2, "strcat ( strcat")) tok2 = tok2->tokAt(2); if (tok2->strAt(3) != ",") continue; // If we have this code: // strcat(strcat(dst, foo), bar); // We move this part of code before all strcat() calls: strcat(dst, foo) // And place "dst" token where the code was. Token *prevTok = tok2->previous(); // Move tokens to new place Token::move(tok2, tok2->next()->link(), tok); tok = tok2->next()->link(); // Insert the "dst" token prevTok->insertToken(tok2->strAt(2)); prevTok->next()->varId(tok2->tokAt(2)->varId()); // Insert semicolon after the moved strcat() tok->insertToken(";"); } } // Check if this statement is a duplicate definition. A duplicate // definition will hide the enumerator within it's scope so just // skip the entire scope of the duplicate. bool Tokenizer::duplicateDefinition(Token ** tokPtr) { // check for an end of definition const Token * tok = *tokPtr; if (tok && Token::Match(tok->next(), ";|,|[|=|)|>")) { const Token * end = tok->next(); if (end->str() == "[") { end = end->link()->next(); } else if (end->str() == ",") { // check for function argument if (Token::Match(tok->previous(), "(|,")) return false; // find end of definition int level = 0; while (end->next() && (!Token::Match(end->next(), ";|)|>") || (end->next()->str() == ")" && level == 0))) { if (end->next()->str() == "(") ++level; else if (end->next()->str() == ")") --level; end = end->next(); } } else if (end->str() == ")") { // check for function argument if (tok->previous()->str() == ",") return false; } if (end) { if (Token::simpleMatch(end, ") {")) { // function parameter ? // make sure it's not a conditional if (Token::Match(end->link()->previous(), "if|for|while|switch|BOOST_FOREACH") || Token::Match(end->link()->tokAt(-2), ":|,")) return false; // look backwards if (tok->previous()->str() == "enum" || (Token::Match(tok->previous(), "%type%") && tok->previous()->str() != "return") || Token::Match(tok->tokAt(-2), "%type% &|*")) { // duplicate definition so skip entire function *tokPtr = end->next()->link(); return true; } } else if (end->str() == ">") { // template parameter ? // look backwards if (tok->previous()->str() == "enum" || (Token::Match(tok->previous(), "%type%") && tok->previous()->str() != "return")) { // duplicate definition so skip entire template while (end && end->str() != "{") end = end->next(); if (end) { *tokPtr = end->link(); return true; } } } else { if (Token::Match(tok->previous(), "enum|,")) return true; else if (Token::Match(tok->previous(), "%type%")) { // look backwards const Token *back = tok; while (back && back->isName()) back = back->previous(); if (!back || (Token::Match(back, "[(,;{}]") && !Token::Match(back->next(),"return|throw"))) return true; } } } } return false; } static const std::set stdFunctionsPresentInC = { "strcat", "strcpy", "strncat", "strncpy", "free", "malloc", "strdup" }; void Tokenizer::simplifyStd() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "std") continue; if (Token::Match(tok->previous(), "[(,{};] std :: %name% (") && stdFunctionsPresentInC.find(tok->strAt(2)) != stdFunctionsPresentInC.end()) { tok->deleteNext(); tok->deleteThis(); } } } //--------------------------------------------------------------------------- // Helper functions for handling the tokens list //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- bool Tokenizer::isScopeNoReturn(const Token *endScopeToken, bool *unknown) const { std::string unknownFunc; const bool ret = mSettings->library.isScopeNoReturn(endScopeToken,&unknownFunc); if (unknown) *unknown = !unknownFunc.empty(); if (!unknownFunc.empty() && mSettings->checkLibrary && mSettings->isEnabled(Settings::INFORMATION)) { // Is function global? bool globalFunction = true; if (Token::simpleMatch(endScopeToken->tokAt(-2), ") ; }")) { const Token * const ftok = endScopeToken->linkAt(-2)->previous(); if (ftok && ftok->isName() && ftok->function() && ftok->function()->nestedIn && ftok->function()->nestedIn->type != Scope::eGlobal) { globalFunction = false; } } // don't warn for nonglobal functions (class methods, functions hidden in namespaces) since they can't be configured yet // FIXME: when methods and namespaces can be configured properly, remove the "globalFunction" check if (globalFunction) { reportError(endScopeToken->previous(), Severity::information, "checkLibraryNoReturn", "--check-library: Function " + unknownFunc + "() should have configuration"); } } return ret; } //--------------------------------------------------------------------------- bool Tokenizer::isFunctionParameterPassedByValue(const Token *fpar) const { // TODO: If symbol database is available, use it. const Token *ftok; // Look at function call, what parameter number is it? int parameter = 1; for (ftok = fpar->previous(); ftok; ftok = ftok->previous()) { if (ftok->str() == "(") break; else if (ftok->str() == ")") ftok = ftok->link(); else if (ftok->str() == ",") ++parameter; else if (Token::Match(ftok, "[;{}]")) break; } // Is this a function call? if (ftok && Token::Match(ftok->tokAt(-2), "[;{}=] %name% (")) { const std::string& functionName(ftok->previous()->str()); if (functionName == "return") return true; // Locate function declaration.. for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->str() == "{") tok = tok->link(); else if (Token::Match(tok, "%type% (") && tok->str() == functionName) { // Goto parameter tok = tok->tokAt(2); int par = 1; while (tok && par < parameter) { if (tok->str() == ")") break; if (tok->str() == ",") ++par; tok = tok->next(); } if (!tok) return false; // If parameter was found, determine if it's passed by value if (par == parameter) { bool knowntype = false; while (tok && tok->isName()) { knowntype |= tok->isStandardType(); knowntype |= (tok->str() == "struct"); tok = tok->next(); } if (!tok || !knowntype) return false; if (tok->str() != "," && tok->str() != ")") return false; return true; } } } } return false; } //--------------------------------------------------------------------------- void Tokenizer::eraseDeadCode(Token *begin, const Token *end) { if (!begin) return; const bool isgoto = Token::Match(begin->tokAt(-2), "goto %name% ;"); int indentlevel = 1; int indentcase = 0; int indentswitch = 0; int indentlabel = 0; int roundbraces = 0; int indentcheck = 0; std::vector switchindents; bool checklabel = false; Token *tok = begin; Token *tokcheck = nullptr; while (tok->next() && tok->next() != end) { if (tok->next()->str() == "(") { ++roundbraces; tok->deleteNext(); continue; } else if (tok->next()->str() == ")") { if (!roundbraces) break; //too many ending round parentheses --roundbraces; tok->deleteNext(); continue; } if (roundbraces) { tok->deleteNext(); continue; } if (Token::Match(tok, "[{};] switch (")) { if (!checklabel) { if (!indentlabel) { //remove 'switch ( ... )' Token::eraseTokens(tok, tok->linkAt(2)->next()); } else { tok = tok->linkAt(2); } if (tok->next()->str() == "{") { ++indentswitch; indentcase = indentlevel + 1; switchindents.push_back(indentcase); } } else { tok = tok->linkAt(2); if (Token::simpleMatch(tok, ") {")) { ++indentswitch; indentcase = indentlevel + 1; switchindents.push_back(indentcase); } } } else if (tok->next()->str() == "{") { ++indentlevel; if (!checklabel) { checklabel = true; tokcheck = tok; indentcheck = indentlevel; indentlabel = 0; } tok = tok->next(); } else if (tok->next()->str() == "}") { --indentlevel; if (!indentlevel) break; if (!checklabel) { tok->deleteNext(); } else { if (indentswitch && indentlevel == indentcase) --indentlevel; if (indentlevel < indentcheck) { const Token *end2 = tok->next(); tok = end2->link()->previous(); //return to initial '{' if (indentswitch && Token::simpleMatch(tok, ") {") && Token::Match(tok->link()->tokAt(-2), "[{};] switch (")) tok = tok->link()->tokAt(-2); //remove also 'switch ( ... )' Token::eraseTokens(tok, end2->next()); checklabel = false; tokcheck = nullptr; indentcheck = 0; } else { tok = tok->next(); } } if (indentswitch && indentlevel <= indentcase) { --indentswitch; switchindents.pop_back(); if (!indentswitch) indentcase = 0; else indentcase = switchindents[indentswitch-1]; } } else if (Token::Match(tok, "[{};:] case")) { const Token *tok2 = Token::findsimplematch(tok->next(), ": ;", end); if (!tok2) { tok->deleteNext(); continue; } if (indentlevel == 1) break; //it seems like the function was called inside a case-default block. if (indentlevel == indentcase) ++indentlevel; tok2 = tok2->next(); if (!checklabel || !indentswitch) { Token::eraseTokens(tok, tok2->next()); } else { tok = const_cast(tok2); } } else if (Token::Match(tok, "[{};] default : ;")) { if (indentlevel == 1) break; //it seems like the function was called inside a case-default block. if (indentlevel == indentcase) ++indentlevel; if (!checklabel || !indentswitch) { tok->deleteNext(3); } else { tok = tok->tokAt(3); } } else if (Token::Match(tok, "[{};] %name% : ;") && tok->next()->str() != "default") { if (checklabel) { indentlabel = indentlevel; tok = tokcheck->next(); checklabel = false; indentlevel = indentcheck; } else { if (indentswitch) { //Before stopping the function, since the 'switch()' //instruction is removed, there's no sense to keep the //case instructions. Remove them, if there are any. Token *tok2 = tok->tokAt(3); int indentlevel2 = indentlevel; while (tok2->next() && tok2->next() != end) { if (Token::Match(tok2->next(), "{|[|(")) { tok2 = tok2->next()->link(); } else if (Token::Match(tok2, "[{};:] case")) { const Token *tok3 = Token::findsimplematch(tok2->next(), ": ;", end); if (!tok3) { tok2 = tok2->next(); continue; } Token::eraseTokens(tok2, tok3->next()); } else if (Token::Match(tok2, "[{};] default : ;")) { tok2->deleteNext(3); } else if (tok2->next()->str() == "}") { --indentlevel2; if (indentlevel2 <= indentcase) break; tok2 = tok2->next(); } else { tok2 = tok2->next(); } } } break; //stop removing tokens, we arrived to the label. } } else if (isgoto && Token::Match(tok, "[{};] do|while|for|BOOST_FOREACH")) { //it's possible that code inside loop is not dead, //because of the possible presence of the label pointed by 'goto' const Token *start = tok->tokAt(2); if (start && start->str() == "(") start = start->link()->next(); if (start && start->str() == "{") { std::string labelpattern = "[{};] " + begin->previous()->str() + " : ;"; bool simplify = true; for (Token *tok2 = start->next(); tok2 != start->link(); tok2 = tok2->next()) { if (Token::Match(tok2, labelpattern.c_str())) { simplify = false; break; } } //bailout for now if (!simplify) break; } tok->deleteNext(); } else { // no need to keep the other strings, remove them. if (tok->strAt(1) == "while") { if (tok->str() == "}" && tok->link()->strAt(-1) == "do") tok->link()->previous()->deleteThis(); } tok->deleteNext(); } } } //--------------------------------------------------------------------------- void Tokenizer::syntaxError(const Token *tok, const std::string &code) const { printDebugOutput(0); throw InternalError(tok, code.empty() ? "syntax error" : "syntax error: " + code, InternalError::SYNTAX); } void Tokenizer::unmatchedToken(const Token *tok) const { printDebugOutput(0); throw InternalError(tok, "Unmatched '" + tok->str() + "'. Configuration: '" + mConfiguration + "'.", InternalError::SYNTAX); } void Tokenizer::syntaxErrorC(const Token *tok, const std::string &what) const { printDebugOutput(0); throw InternalError(tok, "Code '"+what+"' is invalid C code. Use --std or --language to configure the language.", InternalError::SYNTAX); } void Tokenizer::unknownMacroError(const Token *tok1) const { printDebugOutput(0); throw InternalError(tok1, "There is an unknown macro here somewhere. Configuration is required. If " + tok1->str() + " is a macro then please configure it.", InternalError::UNKNOWN_MACRO); } void Tokenizer::unhandled_macro_class_x_y(const Token *tok) const { reportError(tok, Severity::information, "class_X_Y", "The code '" + tok->str() + " " + tok->strAt(1) + " " + tok->strAt(2) + " " + tok->strAt(3) + "' is not handled. You can use -I or --include to add handling of this code."); } void Tokenizer::macroWithSemicolonError(const Token *tok, const std::string ¯oName) const { reportError(tok, Severity::information, "macroWithSemicolon", "Ensure that '" + macroName + "' is defined either using -I, --include or -D."); } void Tokenizer::cppcheckError(const Token *tok) const { printDebugOutput(0); throw InternalError(tok, "Analysis failed. If the code is valid then please report this failure.", InternalError::INTERNAL); } /** * Helper function to check whether number is equal to integer constant X * or floating point pattern X.0 * @param s the string to check * @param intConstant the integer constant to check against * @param floatConstant the string with stringified float constant to check against * @return true in case s is equal to X or X.0 and false otherwise. */ static bool isNumberOneOf(const std::string &s, const MathLib::bigint& intConstant, const char* floatConstant) { if (MathLib::isInt(s)) { if (MathLib::toLongNumber(s) == intConstant) return true; } else if (MathLib::isFloat(s)) { if (MathLib::toString(MathLib::toDoubleNumber(s)) == floatConstant) return true; } return false; } // ------------------------------------------------------------------------ // Helper function to check whether number is zero (0 or 0.0 or 0E+0) or not? // @param s the string to check // @return true in case s is zero and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isZeroNumber(const std::string &s) { return isNumberOneOf(s, 0L, "0.0"); } // ------------------------------------------------------------------------ // Helper function to check whether number is one (1 or 0.1E+1 or 1E+0) or not? // @param s the string to check // @return true in case s is one and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isOneNumber(const std::string &s) { if (!MathLib::isPositive(s)) return false; return isNumberOneOf(s, 1L, "1.0"); } // ------------------------------------------------------------------------ // Helper function to check whether number is two (2 or 0.2E+1 or 2E+0) or not? // @param s the string to check // @return true in case s is two and false otherwise. // ------------------------------------------------------------------------ bool Tokenizer::isTwoNumber(const std::string &s) { if (!MathLib::isPositive(s)) return false; return isNumberOneOf(s, 2L, "2.0"); } // ------------------------------------------------------ // Simplify math functions. // It simplifies the following functions: atol(), fmin(), // fminl(), fminf(), fmax(), fmaxl(), fmaxf(), pow(), // powf(), powl(), cbrt(), cbrtl(), cbtrf(), sqrt(), // sqrtf(), sqrtl(), exp(), expf(), expl(), exp2(), // exp2f(), exp2l(), log2(), log2f(), log2l(), log1p(), // log1pf(), log1pl(), log10(), log10l(), log10f(), // log(), logf(), logl(), logb(), logbf(), logbl(), acosh() // acoshf(), acoshl(), acos(), acosf(), acosl(), cosh() // coshf(), coshf(), cos(), cosf(), cosl(), erfc(), // erfcf(), erfcl(), ilogb(), ilogbf(), ilogbf(), erf(), // erfl(), erff(), asin(), asinf(), asinf(), asinh(), // asinhf(), asinhl(), tan(), tanf(), tanl(), tanh(), // tanhf(), tanhl(), atan(), atanf(), atanl(), atanh(), // atanhf(), atanhl(), expm1(), expm1l(), expm1f(), sin(), // sinf(), sinl(), sinh(), sinhf(), sinhl() // in the tokenlist. // // Reference: // - http://www.cplusplus.com/reference/cmath/ // ------------------------------------------------------ void Tokenizer::simplifyMathFunctions() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->isName() && !tok->varId() && tok->strAt(1) == "(") { // precondition for function bool simplifcationMade = false; if (Token::Match(tok, "atol ( %str% )")) { //@todo Add support for atoll() if (Token::simpleMatch(tok->tokAt(-2), "std ::")) { tok = tok->tokAt(-2);// set token index two steps back tok->deleteNext(2); // delete "std ::" } const std::string& strNumber = tok->tokAt(2)->strValue(); // get number const bool isNotAnInteger = (!MathLib::isInt(strNumber));// check: is not an integer if (strNumber.empty() || isNotAnInteger) { // Ignore strings which we can't convert continue; } // Convert string into a number and insert into token list tok->str(MathLib::toString(MathLib::toLongNumber(strNumber))); // remove ( %num% ) tok->deleteNext(3); simplifcationMade = true; } else if (Token::Match(tok, "sqrt|sqrtf|sqrtl|cbrt|cbrtf|cbrtl ( %num% )")) { // Simplify: sqrt(0) = 0 and cbrt(0) == 0 // sqrt(1) = 1 and cbrt(1) == 1 // get number string const std::string& parameter(tok->strAt(2)); // is parameter 0 ? if (isZeroNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("0"); // insert result into token list simplifcationMade = true; } else if (isOneNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("1"); // insert result into token list simplifcationMade = true; } } else if (Token::Match(tok, "exp|expf|expl|exp2|exp2f|exp2l|cos|cosf|cosl|cosh|coshf|coshl|erfc|erfcf|erfcl ( %num% )")) { // Simplify: exp[f|l](0) = 1 and exp2[f|l](0) = 1 // cosh[f|l](0) = 1 and cos[f|l](0) = 1 // erfc[f|l](0) = 1 // get number string const std::string& parameter(tok->strAt(2)); // is parameter 0 ? if (isZeroNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("1"); // insert result into token list simplifcationMade = true; } } else if (Token::Match(tok, "log1p|log1pf|log1pl|sin|sinf|sinl|sinh|sinhf|sinhl|erf|erff|erfl|asin|asinf|asinl|asinh|asinhf|asinhl|tan|tanf|tanl|tanh|tanhf|tanhl|atan|atanf|atanl|atanh|atanhf|atanhl|expm1|expm1f|expm1l ( %num% )")) { // Simplify: log1p[f|l](0) = 0 and sin[f|l](0) = 0 // sinh[f|l](0) = 0 and erf[f|l](0) = 0 // asin[f|l](0) = 0 and sinh[f|l](0) = 0 // tan[f|l](0) = 0 and tanh[f|l](0) = 0 // atan[f|l](0) = 0 and atanh[f|l](0)= 0 // expm1[f|l](0) = 0 // get number string const std::string& parameter(tok->strAt(2)); // is parameter 0 ? if (isZeroNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("0"); // insert result into token list simplifcationMade = true; } } else if (Token::Match(tok, "log2|log2f|log2l|log|logf|logl|log10|log10f|log10l|logb|logbf|logbl|acosh|acoshf|acoshl|acos|acosf|acosl|ilogb|ilogbf|ilogbl ( %num% )")) { // Simplify: log2[f|l](1) = 0 , log10[f|l](1) = 0 // log[f|l](1) = 0 , logb10[f|l](1) = 0 // acosh[f|l](1) = 0 , acos[f|l](1) = 0 // ilogb[f|l](1) = 0 // get number string const std::string& parameter(tok->strAt(2)); // is parameter 1 ? if (isOneNumber(parameter)) { tok->deleteNext(3); // delete tokens tok->str("0"); // insert result into token list simplifcationMade = true; } } else if (Token::Match(tok, "fmin|fminl|fminf ( %num% , %num% )")) { // @todo if one of the parameters is NaN the other is returned // e.g. printf ("fmin (NaN, -1.0) = %f\n", fmin(NaN,-1.0)); // e.g. printf ("fmin (-1.0, NaN) = %f\n", fmin(-1.0,NaN)); const std::string& strLeftNumber(tok->strAt(2)); const std::string& strRightNumber(tok->strAt(4)); const bool isLessEqual = MathLib::isLessEqual(strLeftNumber, strRightNumber); // case: left <= right ==> insert left if (isLessEqual) { tok->str(strLeftNumber); // insert e.g. -1.0 tok->deleteNext(5); // delete e.g. fmin ( -1.0, 1.0 ) simplifcationMade = true; } else { // case left > right ==> insert right tok->str(strRightNumber); // insert e.g. 0.0 tok->deleteNext(5); // delete e.g. fmin ( 1.0, 0.0 ) simplifcationMade = true; } } else if (Token::Match(tok, "fmax|fmaxl|fmaxf ( %num% , %num% )")) { // @todo if one of the parameters is NaN the other is returned // e.g. printf ("fmax (NaN, -1.0) = %f\n", fmax(NaN,-1.0)); // e.g. printf ("fmax (-1.0, NaN) = %f\n", fmax(-1.0,NaN)); const std::string& strLeftNumber(tok->strAt(2)); const std::string& strRightNumber(tok->strAt(4)); const bool isLessEqual = MathLib::isLessEqual(strLeftNumber, strRightNumber); // case: left <= right ==> insert right if (isLessEqual) { tok->str(strRightNumber);// insert e.g. 1.0 tok->deleteNext(5); // delete e.g. fmax ( -1.0, 1.0 ) simplifcationMade = true; } else { // case left > right ==> insert left tok->str(strLeftNumber); // insert e.g. 1.0 tok->deleteNext(5); // delete e.g. fmax ( 1.0, 0.0 ) simplifcationMade = true; } } else if (Token::Match(tok, "pow|powf|powl (")) { if (Token::Match(tok->tokAt(2), "%num% , %num% )")) { // In case of pow ( 0 , anyNumber > 0): It can be simplified to 0 // In case of pow ( 0 , 0 ): It simplified to 1 // In case of pow ( 1 , anyNumber ): It simplified to 1 const std::string& leftNumber(tok->strAt(2)); // get the left parameter const std::string& rightNumber(tok->strAt(4)); // get the right parameter const bool isLeftNumberZero = isZeroNumber(leftNumber); const bool isLeftNumberOne = isOneNumber(leftNumber); const bool isRightNumberZero = isZeroNumber(rightNumber); if (isLeftNumberZero && !isRightNumberZero && MathLib::isPositive(rightNumber)) { // case: 0^(y) = 0 and y > 0 tok->deleteNext(5); // delete tokens tok->str("0"); // insert simplified result simplifcationMade = true; } else if (isLeftNumberZero && isRightNumberZero) { // case: 0^0 = 1 tok->deleteNext(5); // delete tokens tok->str("1"); // insert simplified result simplifcationMade = true; } else if (isLeftNumberOne) { // case 1^(y) = 1 tok->deleteNext(5); // delete tokens tok->str("1"); // insert simplified result simplifcationMade = true; } } if (Token::Match(tok->tokAt(2), "%any% , %num% )")) { // In case of pow( x , 1 ): It can be simplified to x. const std::string& leftParameter(tok->strAt(2)); // get the left parameter const std::string& rightNumber(tok->strAt(4)); // get right number if (isOneNumber(rightNumber)) { // case: x^(1) = x tok->str(leftParameter); // insert simplified result tok->deleteNext(5); // delete tokens simplifcationMade = true; } else if (isZeroNumber(rightNumber)) { // case: x^(0) = 1 tok->deleteNext(5); // delete tokens tok->str("1"); // insert simplified result simplifcationMade = true; } } } // Jump back to begin of statement if a simplification was performed if (simplifcationMade) { while (tok->previous() && tok->str() != ";") { tok = tok->previous(); } } } } } void Tokenizer::simplifyComma() { bool inReturn = false; for (Token *tok = list.front(); tok; tok = tok->next()) { // skip enums if (Token::Match(tok, "enum class|struct| %name%| :|{")) { skipEnumBody(&tok); } if (!tok) syntaxError(nullptr); // invalid code like in #4195 if (Token::Match(tok, "(|[") || Token::Match(tok->previous(), "%name%|= {")) { tok = tok->link(); continue; } if (Token::simpleMatch(tok, "= (") && Token::simpleMatch(tok->linkAt(1), ") {")) { tok = tok->linkAt(1)->linkAt(1); continue; } // Skip unhandled template specifiers.. if (tok->link() && tok->str() == "<") tok = tok->link(); if (tok->str() == "return" && Token::Match(tok->previous(), "[;{}]")) inReturn = true; if (inReturn && Token::Match(tok, "[;{}?:]")) inReturn = false; if (!tok->next() || tok->str() != ",") continue; // We must not accept just any keyword, e.g. accepting int // would cause function parameters to corrupt. if (isCPP() && tok->strAt(1) == "delete") { // Handle "delete a, delete b;" tok->str(";"); } if (isCPP() && Token::Match(tok->tokAt(-2), "delete %name% , %name% ;") && tok->next()->varId() != 0) { // Handle "delete a, b;" - convert to delete a; b; tok->str(";"); } else if (!inReturn && tok->tokAt(-2)) { bool replace = false; for (Token *tok2 = tok->previous(); tok2; tok2 = tok2->previous()) { if (tok2->str() == "=") { // Handle "a = 0, b = 0;" replace = true; } else if (isCPP() && (Token::Match(tok2, "delete %name%") || Token::Match(tok2, "delete [ ] %name%"))) { // Handle "delete a, a = 0;" replace = true; } else if (Token::Match(tok2, "[?:;,{}()]")) { if (replace && Token::Match(tok2, "[;{}]")) tok->str(";"); break; } } } // find token where return ends and also count commas if (inReturn) { Token *startFrom = nullptr; // "[;{}]" token before "return" Token *endAt = nullptr; // first ";" token after "[;{}] return" // find "; return" pattern before comma for (Token *tok2 = tok->previous(); tok2; tok2 = tok2->previous()) { if (tok2->str() == "return") { startFrom = tok2->previous(); break; } } if (!startFrom) // to be very sure... return; int commaCounter = 0; for (Token *tok2 = startFrom->next(); tok2; tok2 = tok2->next()) { if (tok2->str() == ";") { endAt = tok2; break; } else if (Token::Match(tok2, "(|[") || (tok2->str() == "{" && tok2->previous() && tok2->previous()->str() == "=")) { tok2 = tok2->link(); } else if (tok2->str() == ",") { ++commaCounter; } } if (!endAt) //probably a syntax error return; if (commaCounter) { // change tokens: // "; return a ( ) , b ( ) , c ;" // to // "; a ( ) ; b ( ) ; return c ;" // remove "return" startFrom->deleteNext(); for (Token *tok2 = startFrom->next(); tok2 != endAt; tok2 = tok2->next()) { if (Token::Match(tok2, "(|[") || (tok2->str() == "{" && tok2->previous() && tok2->previous()->str() == "=")) { tok2 = tok2->link(); } else if (tok2->str() == ",") { tok2->str(";"); --commaCounter; if (commaCounter == 0) { tok2->insertToken("return"); } } } tok = endAt; } } } } void Tokenizer::checkConfiguration() const { if (!mSettings->checkConfiguration) return; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; if (tok->isControlFlowKeyword()) continue; for (const Token *tok2 = tok->tokAt(2); tok2 && tok2->str() != ")"; tok2 = tok2->next()) { if (tok2->str() == ";") { macroWithSemicolonError(tok, tok->str()); break; } if (Token::Match(tok2, "(|{")) tok2 = tok2->link(); } } } void Tokenizer::validateC() const { if (isCPP()) return; for (const Token *tok = tokens(); tok; tok = tok->next()) { // That might trigger false positives, but it's much faster to have this truncated pattern if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) syntaxErrorC(tok, "C++ cast <..."); // Template function.. if (Token::Match(tok, "%name% < %name% > (")) { const Token *tok2 = tok->tokAt(5); while (tok2 && !Token::Match(tok2, "[()]")) tok2 = tok2->next(); if (Token::simpleMatch(tok2, ") {")) syntaxErrorC(tok, tok->str() + '<' + tok->strAt(2) + ">() {}"); } if (tok->previous() && !Token::Match(tok->previous(), "[;{}]")) continue; if (Token::Match(tok, "using namespace %name% ;")) syntaxErrorC(tok, "using namespace " + tok->strAt(2)); if (Token::Match(tok, "template < class|typename %name% [,>]")) syntaxErrorC(tok, "template<..."); if (Token::Match(tok, "%name% :: %name%")) syntaxErrorC(tok, tok->str() + tok->strAt(1) + tok->strAt(2)); if (Token::Match(tok, "class|namespace %name% [:{]")) syntaxErrorC(tok, tok->str() + tok->strAt(1) + tok->strAt(2)); } } void Tokenizer::validate() const { std::stack linkTokens; const Token *lastTok = nullptr; for (const Token *tok = tokens(); tok; tok = tok->next()) { lastTok = tok; if (Token::Match(tok, "[{([]") || (tok->str() == "<" && tok->link())) { if (tok->link() == nullptr) cppcheckError(tok); linkTokens.push(tok); } else if (Token::Match(tok, "[})]]") || (Token::Match(tok, ">|>>") && tok->link())) { if (tok->link() == nullptr) cppcheckError(tok); if (linkTokens.empty() == true) cppcheckError(tok); if (tok->link() != linkTokens.top()) cppcheckError(tok); if (tok != tok->link()->link()) cppcheckError(tok); linkTokens.pop(); } else if (tok->link() != nullptr) cppcheckError(tok); } if (!linkTokens.empty()) cppcheckError(linkTokens.top()); // Validate that the Tokenizer::list.back() is updated correctly during simplifications if (lastTok != list.back()) cppcheckError(lastTok); } static const Token *findUnmatchedTernaryOp(const Token * const begin, const Token * const end, int depth = 0) { std::stack ternaryOp; for (const Token *tok = begin; tok != end && tok->str() != ";"; tok = tok->next()) { if (tok->str() == "?") ternaryOp.push(tok); else if (!ternaryOp.empty() && tok->str() == ":") ternaryOp.pop(); else if (depth < 100 && Token::Match(tok,"(|[")) { const Token *inner = findUnmatchedTernaryOp(tok->next(), tok->link(), depth+1); if (inner) return inner; tok = tok->link(); } } return ternaryOp.empty() ? nullptr : ternaryOp.top(); } static bool isCPPAttribute(const Token * tok) { return Token::simpleMatch(tok, "[ [") && tok->link() && tok->link()->previous() == tok->linkAt(1); } void Tokenizer::findGarbageCode() const { const bool isCPP11 = isCPP() && mSettings->standards.cpp >= Standards::CPP11; // initialization: = { for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "= {")) continue; if (Token::simpleMatch(tok->linkAt(1), "} (")) syntaxError(tok->linkAt(1)); } // Inside [] there can't be ; or various keywords for (const Token *tok = tokens(); tok; tok = tok->next()) { if (tok->str() != "[") continue; for (const Token *inner = tok->next(); inner != tok->link(); inner = inner->next()) { if (Token::Match(inner, "(|[")) inner = inner->link(); else if (Token::Match(inner, ";|goto|return|typedef")) syntaxError(inner); } } // UNKNOWN_MACRO(return) for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "throw|return )") && Token::Match(tok->linkAt(1)->previous(), "%name% (")) unknownMacroError(tok->linkAt(1)->previous()); } // UNKNOWN_MACRO(return) for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "%name% throw|return") && std::isupper(tok->str()[0])) unknownMacroError(tok); } // Assign/increment/decrement literal for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "!!) %num%|%str%|%char% %assign%|++|--")) syntaxError(tok, tok->next()->str() + " " + tok->strAt(2)); } for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "if|while|for|switch")) { // if|while|for|switch (EXPR) { ... } if (tok->previous() && !Token::Match(tok->previous(), "%name%|:|;|{|}|)")) { if (Token::Match(tok->previous(), "[,(]")) { const Token *prev = tok->previous(); while (prev && prev->str() != "(") { if (prev->str() == ")") prev = prev->link(); prev = prev->previous(); } if (prev && Token::Match(prev->previous(), "%name% (")) unknownMacroError(prev->previous()); } if (!Token::simpleMatch(tok->tokAt(-2), "operator \"\" if")) syntaxError(tok); } if (!Token::Match(tok->next(), "( !!)")) syntaxError(tok); if (tok->str() != "for") { if (isGarbageExpr(tok->next(), tok->linkAt(1), mSettings->standards.cpp>=Standards::cppstd_t::CPP17)) syntaxError(tok); } } } // keyword keyword const std::set nonConsecutiveKeywords{"break", "continue", "for", "goto", "if", "return", "switch", "throw", "typedef", "while"}; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!tok->isName() || nonConsecutiveKeywords.count(tok->str()) == 0) continue; if (Token::Match(tok, "%name% %name%") && nonConsecutiveKeywords.count(tok->next()->str()) == 1) syntaxError(tok); const Token *prev = tok; while (prev && prev->isName()) prev = prev->previous(); if (Token::Match(prev, "%op%|%num%|%str%|%char%")) { if (!Token::simpleMatch(tok->tokAt(-2), "operator \"\" if") && !Token::simpleMatch(tok->tokAt(-2), "extern \"C\"")) syntaxError(tok, prev == tok->previous() ? (prev->str() + " " + tok->str()) : (prev->str() + " .. " + tok->str())); } } // case keyword must be inside switch for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "switch (")) { if (Token::simpleMatch(tok->linkAt(1), ") {")) { tok = tok->linkAt(1)->linkAt(1); continue; } const Token *switchToken = tok; tok = tok->linkAt(1); if (!tok) syntaxError(switchToken); // Look for the end of the switch statement, i.e. the first semi-colon or '}' for (; tok ; tok = tok->next()) { if (tok->str() == "{") { tok = tok->link(); } if (Token::Match(tok, ";|}")) { // We're at the end of the switch block if (tok->str() == "}" && tok->strAt(-1) == ":") // Invalid case syntaxError(switchToken); break; } } if (!tok) break; } else if (tok->str() == "(") { tok = tok->link(); } else if (tok->str() == "case") { syntaxError(tok); } } for (const Token *tok = tokens(); tok ; tok = tok->next()) { if (!Token::simpleMatch(tok, "for (")) // find for loops continue; // count number of semicolons int semicolons = 0; const Token* const startTok = tok; tok = tok->next()->link()->previous(); // find ")" of the for-loop // walk backwards until we find the beginning (startTok) of the for() again for (; tok != startTok; tok = tok->previous()) { if (tok->str() == ";") { // do the counting semicolons++; } else if (tok->str() == ")") { // skip pairs of ( ) tok = tok->link(); } } // if we have an invalid number of semicolons inside for( ), assume syntax error if ((semicolons == 1) || (semicolons > 2)) { syntaxError(tok); } } // Operators without operands.. const Token *templateEndToken = nullptr; for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!templateEndToken) { if (tok->str() == "<" && isCPP()) templateEndToken = tok->findClosingBracket(); } else { if (templateEndToken == tok) templateEndToken = nullptr; if (Token::Match(tok, "> %cop%")) continue; } // skip C++ attributes [[...]] if (isCPP11 && isCPPAttribute(tok)) { tok = tok->link(); continue; } { bool match1 = Token::Match(tok, "%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|++|--|::|sizeof"); bool match2 = Token::Match(tok->next(), "{|if|else|while|do|for|return|switch|break"); if (isCPP()) { match1 = match1 || Token::Match(tok, "::|throw|decltype|typeof"); match2 = match2 || Token::Match(tok->next(), "try|catch|namespace"); } if (match1 && match2) syntaxError(tok); } if (Token::Match(tok, "%comp%|+|-|/|% )|]|}")) { if (isC()) syntaxError(tok, tok->str() + tok->next()->str()); if (tok->str() != ">" && !Token::simpleMatch(tok->previous(), "operator")) syntaxError(tok, tok->str() + " " + tok->next()->str()); } if (Token::Match(tok, "( %any% )") && tok->next()->isKeyword() && !Token::simpleMatch(tok->next(), "void")) syntaxError(tok); if (Token::Match(tok, "%num%|%bool%|%char%|%str% %num%|%bool%|%char%|%str%") && !Token::Match(tok, "%str% %str%")) syntaxError(tok); if (Token::Match(tok, "%assign% typename|class %assign%")) syntaxError(tok); if (Token::Match(tok, "%cop%|=|,|[ %or%|%oror%|/|%")) syntaxError(tok); if (Token::Match(tok, ";|(|[ %comp%")) syntaxError(tok); if (Token::Match(tok, "%cop%|= ]") && !(isCPP() && Token::Match(tok->previous(), "[|,|%num% &|=|> ]"))) syntaxError(tok); if (Token::Match(tok, "[+-] [;,)]}]") && !(isCPP() && Token::Match(tok->previous(), "operator [+-] ;"))) syntaxError(tok); if (Token::simpleMatch(tok, ",") && !Token::Match(tok->tokAt(-2), "[ = , &|%name%")) { if (Token::Match(tok->previous(), "(|[|{|<|%assign%|%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|::|sizeof|throw|decltype|typeof")) syntaxError(tok); if (Token::Match(tok->next(), ")|]|>|%assign%|%or%|%oror%|==|!=|/|>=|<=|&&")) syntaxError(tok); } if (Token::simpleMatch(tok, ".") && !Token::simpleMatch(tok->previous(), ".") && !Token::simpleMatch(tok->next(), ".") && !Token::Match(tok->previous(), "{|, . %name% [=.]") && !Token::Match(tok->previous(), ", . %name%")) { if (!Token::Match(tok->previous(), "%name%|)|]|>|}")) syntaxError(tok, tok->strAt(-1) + " " + tok->str() + " " + tok->strAt(1)); if (!Token::Match(tok->next(), "%name%|*|~")) syntaxError(tok, tok->strAt(-1) + " " + tok->str() + " " + tok->strAt(1)); } if (Token::Match(tok, "[!|+-/%^~] )|]")) syntaxError(tok); } // ternary operator without : if (const Token *ternaryOp = findUnmatchedTernaryOp(tokens(), nullptr)) syntaxError(ternaryOp); // Code must not start with an arithmetical operand if (Token::Match(list.front(), "%cop%")) syntaxError(list.front()); // Code must end with } ; ) NAME if (!Token::Match(list.back(), "%name%|;|}|)")) syntaxError(list.back()); if (list.back()->str() == ")" && !Token::Match(list.back()->link()->previous(), "%name%|> (")) syntaxError(list.back()); for (const Token *end = list.back(); end && end->isName(); end = end->previous()) { if (Token::Match(end, "void|char|short|int|long|float|double|const|volatile|static|inline|struct|class|enum|union|template|sizeof|case|break|continue|typedef")) syntaxError(list.back()); } if ((list.back()->str()==")" || list.back()->str()=="}") && list.back()->previous() && list.back()->previous()->isControlFlowKeyword()) syntaxError(list.back()->previous()); // Garbage templates.. if (isCPP()) { for (const Token *tok = tokens(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "template <")) continue; if (tok->previous() && !Token::Match(tok->previous(), "[:;{})>]")) { if (tok->previous()->isUpperCaseName()) unknownMacroError(tok->previous()); else syntaxError(tok); } const Token * const tok1 = tok; tok = tok->next()->findClosingBracket(); if (!tok) syntaxError(tok1); if (!Token::Match(tok, ">|>> ::|...| %name%") && !Token::Match(tok, ">|>> [ [ %name%") && !Token::Match(tok, "> >|*")) syntaxError(tok->next() ? tok->next() : tok1); } } // Objective C/C++ for (const Token *tok = tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] [ %name% %name% ] ;")) syntaxError(tok->next()); } } bool Tokenizer::isGarbageExpr(const Token *start, const Token *end, bool allowSemicolon) { for (const Token *tok = start; tok != end; tok = tok->next()) { if (tok->isControlFlowKeyword()) return true; if (!allowSemicolon && tok->str() == ";") return true; if (tok->str() == "{") tok = tok->link(); } return false; } std::string Tokenizer::simplifyString(const std::string &source) { std::string str = source; for (std::string::size_type i = 0; i + 1U < str.size(); ++i) { if (str[i] != '\\') continue; int c = 'a'; // char int sz = 0; // size of stringdata if (str[i+1] == 'x') { sz = 2; while (sz < 4 && std::isxdigit((unsigned char)str[i+sz])) sz++; if (sz > 2) { std::istringstream istr(str.substr(i+2, sz-2)); istr >> std::hex >> c; } } else if (MathLib::isOctalDigit(str[i+1])) { sz = 2; while (sz < 4 && MathLib::isOctalDigit(str[i+sz])) sz++; std::istringstream istr(str.substr(i+1, sz-1)); istr >> std::oct >> c; str = str.substr(0,i) + (char)c + str.substr(i+sz); continue; } if (sz <= 2) i++; else if (i+sz < str.size()) str.replace(i, sz, std::string(1U, (char)c)); else str.replace(i, str.size() - i - 1U, "a"); } return str; } void Tokenizer::simplifyWhile0() { for (Token *tok = list.front(); tok; tok = tok->next()) { // while (0) const bool while0(Token::Match(tok->previous(), "[{};] while ( 0|false )")); // for (0) - not banal, ticket #3140 const bool for0((Token::Match(tok->previous(), "[{};] for ( %name% = %num% ; %name% < %num% ;") && tok->strAt(2) == tok->strAt(6) && tok->strAt(4) == tok->strAt(8)) || (Token::Match(tok->previous(), "[{};] for ( %type% %name% = %num% ; %name% < %num% ;") && tok->strAt(3) == tok->strAt(7) && tok->strAt(5) == tok->strAt(9))); if (!while0 && !for0) continue; if (while0 && tok->previous()->str() == "}") { // find "do" Token *tok2 = tok->previous()->link(); tok2 = tok2->previous(); if (tok2 && tok2->str() == "do") { const bool flowmatch = Token::findmatch(tok2, "continue|break", tok) != nullptr; // delete "do ({)" tok2->deleteThis(); if (!flowmatch) tok2->deleteThis(); // delete "(}) while ( 0 ) (;)" tok = tok->previous(); tok->deleteNext(4); // while ( 0 ) if (tok->next() && tok->next()->str() == ";") tok->deleteNext(); // ; if (!flowmatch) tok->deleteThis(); // } continue; } } // remove "while (0) { .. }" if (Token::simpleMatch(tok->next()->link(), ") {")) { Token *end = tok->next()->link(), *old_prev = tok->previous(); end = end->next()->link(); if (Token::Match(tok, "for ( %name% =")) old_prev = end->link(); eraseDeadCode(old_prev, end->next()); if (old_prev && old_prev->next()) tok = old_prev->next(); else break; } } } void Tokenizer::simplifyErrNoInWhile() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "errno") continue; Token *endpar = nullptr; if (Token::Match(tok->previous(), "&& errno == EINTR ) { ;| }")) endpar = tok->tokAt(3); else if (Token::Match(tok->tokAt(-2), "&& ( errno == EINTR ) ) { ;| }")) endpar = tok->tokAt(4); else continue; if (Token::simpleMatch(endpar->link()->previous(), "while (")) { Token *tok1 = tok->previous(); if (tok1->str() == "(") tok1 = tok1->previous(); // erase "&& errno == EINTR" tok1 = tok1->previous(); Token::eraseTokens(tok1, endpar); // tok is invalid.. move to endpar tok = endpar; } } } void Tokenizer::simplifyFuncInWhile() { int count = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!Token::Match(tok, "while ( %name% ( %name% ) ) {")) continue; Token *func = tok->tokAt(2); const Token * const var = tok->tokAt(4); Token * const end = tok->next()->link()->next()->link(); const int varid = ++mVarId; // Create new variable const std::string varname("cppcheck:r" + MathLib::toString(++count)); tok->str("int"); tok->next()->insertToken(varname); tok->tokAt(2)->varId(varid); tok->insertToken("while"); tok->insertToken(";"); tok->insertToken(")"); tok->insertToken(var->str()); tok->next()->varId(var->varId()); tok->insertToken("("); tok->insertToken(func->str()); tok->insertToken("="); tok->insertToken(varname); tok->next()->varId(varid); Token::createMutualLinks(tok->tokAt(4), tok->tokAt(6)); end->previous()->insertToken(varname); end->previous()->varId(varid); end->previous()->insertToken("="); Token::move(func, func->tokAt(3), end->previous()); end->previous()->insertToken(";"); tok = end; } } void Tokenizer::simplifyStructDecl() { const bool cpp = isCPP(); // A counter that is used when giving unique names for anonymous structs. int count = 0; // Skip simplification of unions in class definition std::stack skip; // true = in function, false = not in function skip.push(false); // Add names for anonymous structs for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName()) continue; // check for anonymous struct/union if (Token::Match(tok, "struct|union {")) { if (Token::Match(tok->next()->link(), "} const| *|&| const| %type% ,|;|[|(|{|=")) { tok->insertToken("Anonymous" + MathLib::toString(count++)); } } // check for derived anonymous class/struct else if (cpp && Token::Match(tok, "class|struct :")) { const Token *tok1 = Token::findsimplematch(tok, "{"); if (tok1 && Token::Match(tok1->link(), "} const| *|&| const| %type% ,|;|[|(|{")) { tok->insertToken("Anonymous" + MathLib::toString(count++)); } } // check for anonymous enum else if ((Token::simpleMatch(tok, "enum {") && !Token::Match(tok->tokAt(-3), "using %name% =") && Token::Match(tok->next()->link(), "} (| %type%| )| ,|;|[|(|{")) || (Token::Match(tok, "enum : %type% {") && Token::Match(tok->linkAt(3), "} (| %type%| )| ,|;|[|(|{"))) { Token *start = tok->strAt(1) == ":" ? tok->linkAt(3) : tok->linkAt(1); if (start && Token::Match(start->next(), "( %type% )")) { start->next()->link()->deleteThis(); start->next()->deleteThis(); } tok->insertToken("Anonymous" + MathLib::toString(count++)); } } for (Token *tok = list.front(); tok; tok = tok->next()) { // check for start of scope and determine if it is in a function if (tok->str() == "{") skip.push(Token::Match(tok->previous(), "const|)")); // end of scope else if (tok->str() == "}" && !skip.empty()) skip.pop(); // check for named struct/union else if (Token::Match(tok, "class|struct|union|enum %type% :|{")) { Token *start = tok; while (Token::Match(start->previous(), "%type%")) start = start->previous(); const Token * const type = tok->next(); Token *next = tok->tokAt(2); while (next && next->str() != "{") next = next->next(); if (!next) continue; skip.push(false); tok = next->link(); if (!tok) break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code) Token *restart = next; // check for named type if (Token::Match(tok->next(), "const| *|&| const| (| %type% )| ,|;|[|=|(|{")) { tok->insertToken(";"); tok = tok->next(); while (!Token::Match(start, "struct|class|union|enum")) { tok->insertToken(start->str()); tok = tok->next(); start->deleteThis(); } if (!tok) break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code) tok->insertToken(type->str()); if (start->str() != "class") { tok->insertToken(start->str()); tok = tok->next(); } tok = tok->tokAt(2); if (Token::Match(tok, "( %type% )")) { tok->link()->deleteThis(); tok->deleteThis(); } // check for initialization if (tok && (tok->next()->str() == "(" || tok->next()->str() == "{")) { tok->insertToken("="); tok = tok->next(); if (start->str() == "enum") { if (tok->next()->str() == "{") { tok->next()->str("("); tok->linkAt(1)->str(")"); } } } } tok = restart; } // check for anonymous struct/union else if (Token::Match(tok, "struct|union {")) { const bool inFunction = skip.top(); skip.push(false); Token *tok1 = tok; Token *restart = tok->next(); tok = tok->next()->link(); // unnamed anonymous struct/union so possibly remove it if (tok && tok->next() && tok->next()->str() == ";") { if (inFunction && tok1->str() == "union") { // Try to create references in the union.. Token *tok2 = tok1->tokAt(2); while (tok2) { if (Token::Match(tok2, "%type% %name% ;")) tok2 = tok2->tokAt(3); else break; } if (!Token::simpleMatch(tok2, "} ;")) continue; Token *vartok = nullptr; tok2 = tok1->tokAt(2); while (Token::Match(tok2, "%type% %name% ;")) { if (!vartok) { vartok = tok2->next(); tok2 = tok2->tokAt(3); } else { tok2->insertToken("&"); tok2 = tok2->tokAt(2); tok2->insertToken(vartok->str()); tok2->next()->varId(vartok->varId()); tok2->insertToken("="); tok2 = tok2->tokAt(4); } } } // don't remove unnamed anonymous unions from a class, struct or union if (!(!inFunction && tok1->str() == "union") && !Token::Match(tok1->tokAt(-3), "using %name% =")) { skip.pop(); tok1->deleteThis(); if (tok1->next() == tok) { tok1->deleteThis(); tok = tok1; } else tok1->deleteThis(); restart = tok1->previous(); tok->deleteThis(); if (tok->next()) tok->deleteThis(); } } if (!restart) { simplifyStructDecl(); return; } else if (!restart->next()) return; tok = restart; } } } void Tokenizer::simplifyCallingConvention() { const bool windows = mSettings->isWindowsPlatform(); for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::Match(tok, "__cdecl|__stdcall|__fastcall|__thiscall|__clrcall|__syscall|__pascal|__fortran|__far|__near") || (windows && Token::Match(tok, "WINAPI|APIENTRY|CALLBACK"))) { tok->deleteThis(); } } } void Tokenizer::simplifyDeclspec() { for (Token *tok = list.front(); tok; tok = tok->next()) { while (Token::Match(tok, "__declspec|_declspec (") && tok->next()->link() && tok->next()->link()->next()) { if (Token::Match(tok->tokAt(2), "noreturn|nothrow")) { Token *tok1 = tok->next()->link()->next(); while (tok1 && !Token::Match(tok1, "%name%")) { tok1 = tok1->next(); } if (tok1) { if (tok->strAt(2) == "noreturn") tok1->isAttributeNoreturn(true); else tok1->isAttributeNothrow(true); } } else if (tok->strAt(2) == "property") tok->next()->link()->insertToken("__property"); Token::eraseTokens(tok, tok->next()->link()->next()); tok->deleteThis(); } } } void Tokenizer::simplifyAttribute() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%type% (") && !mSettings->library.isNotLibraryFunction(tok)) { if (mSettings->library.isFunctionConst(tok->str(), true)) tok->isAttributePure(true); if (mSettings->library.isFunctionConst(tok->str(), false)) tok->isAttributeConst(true); } while (Token::Match(tok, "__attribute__|__attribute (") && tok->next()->link() && tok->next()->link()->next()) { if (Token::Match(tok->tokAt(2), "( constructor|__constructor__")) { // prototype for constructor is: void func(void); if (!tok->next()->link()->next()) syntaxError(tok); if (tok->next()->link()->next()->str() == "void") { // __attribute__((constructor)) void func() {} if (!tok->next()->link()->next()->next()) syntaxError(tok); tok->next()->link()->next()->next()->isAttributeConstructor(true); } else if (tok->next()->link()->next()->str() == ";" && tok->linkAt(-1) && tok->previous()->link()->previous()) // void func() __attribute__((constructor)); tok->previous()->link()->previous()->isAttributeConstructor(true); else // void __attribute__((constructor)) func() {} tok->next()->link()->next()->isAttributeConstructor(true); } else if (Token::Match(tok->tokAt(2), "( destructor|__destructor__")) { // prototype for destructor is: void func(void); if (!tok->next()->link()->next()) syntaxError(tok); if (tok->next()->link()->next()->str() == "void") // __attribute__((destructor)) void func() {} tok->next()->link()->next()->next()->isAttributeDestructor(true); else if (tok->next()->link()->next()->str() == ";" && tok->linkAt(-1) && tok->previous()->link()->previous()) // void func() __attribute__((destructor)); tok->previous()->link()->previous()->isAttributeDestructor(true); else // void __attribute__((destructor)) func() {} tok->next()->link()->next()->isAttributeDestructor(true); } else if (Token::Match(tok->tokAt(2), "( unused|__unused__|used|__used__ )")) { Token *vartok = nullptr; // check if after variable name if (Token::Match(tok->next()->link()->next(), ";|=")) { if (Token::Match(tok->previous(), "%type%")) vartok = tok->previous(); } // check if before variable name else if (Token::Match(tok->next()->link()->next(), "%type%")) vartok = tok->next()->link()->next(); if (vartok) { const std::string &attribute(tok->strAt(3)); if (attribute.find("unused") != std::string::npos) vartok->isAttributeUnused(true); else vartok->isAttributeUsed(true); } } else if (Token::Match(tok->tokAt(2), "( pure|__pure__|const|__const__|noreturn|__noreturn__|nothrow|__nothrow__|warn_unused_result )")) { Token *functok = nullptr; // type func(...) __attribute__((attribute)); if (tok->previous() && tok->previous()->link() && Token::Match(tok->previous()->link()->previous(), "%name% (")) functok = tok->previous()->link()->previous(); // type __attribute__((attribute)) func() { } else { Token *tok2 = tok->next()->link(); while (Token::Match(tok2, ") __attribute__|__attribute (")) tok2 = tok2->linkAt(2); if (Token::Match(tok2, ") %name% (")) functok = tok2->next(); } if (functok) { const std::string &attribute(tok->strAt(3)); if (attribute.find("pure") != std::string::npos) functok->isAttributePure(true); else if (attribute.find("const") != std::string::npos) functok->isAttributeConst(true); else if (attribute.find("noreturn") != std::string::npos) functok->isAttributeNoreturn(true); else if (attribute.find("nothrow") != std::string::npos) functok->isAttributeNothrow(true); else if (attribute.find("warn_unused_result") != std::string::npos) functok->isAttributeNodiscard(true); } } else if (Token::simpleMatch(tok->previous(), "} __attribute__ ( ( packed )")) { tok->previous()->isAttributePacked(true); } Token::eraseTokens(tok, tok->next()->link()->next()); tok->deleteThis(); } } } void Tokenizer::simplifyCppcheckAttribute() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "(") continue; if (!tok->previous()) continue; const std::string &attr = tok->previous()->str(); if (attr.compare(0, 11, "__cppcheck_") != 0) // TODO: starts_with("__cppcheck_") continue; if (attr.compare(attr.size()-2, 2, "__") != 0) // TODO: ends_with("__") continue; Token *vartok = tok->link(); while (Token::Match(vartok->next(), "%name%|*|&|::")) { vartok = vartok->next(); if (Token::Match(vartok, "%name% (") && vartok->str().compare(0,11,"__cppcheck_") == 0) vartok = vartok->linkAt(1); } if (vartok->isName()) { if (Token::Match(tok->previous(), "__cppcheck_low__ ( %num% )")) vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(tok->next()->str())); else if (Token::Match(tok->previous(), "__cppcheck_high__ ( %num% )")) vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(tok->next()->str())); } // Delete cppcheck attribute.. if (tok->tokAt(-2)) { tok = tok->tokAt(-2); Token::eraseTokens(tok, tok->linkAt(2)->next()); } else { tok = tok->previous(); Token::eraseTokens(tok, tok->linkAt(1)->next()); tok->str(";"); } } } void Tokenizer::simplifyCPPAttribute() { if (mSettings->standards.cpp < Standards::CPP11 || isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!isCPPAttribute(tok)) continue; if (Token::Match(tok->tokAt(2), "noreturn|nodiscard")) { const Token * head = tok->link()->next(); while (Token::Match(head, "%name%|::|*|&")) head = head->next(); if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { if (tok->strAt(2) == "noreturn") head->previous()->isAttributeNoreturn(true); else head->previous()->isAttributeNodiscard(true); } } else if (Token::Match(tok->previous(), ") [ [ expects|ensures|assert default|audit|axiom| : %name% <|<=|>|>= %num% ] ]")) { const Token *vartok = tok->tokAt(4); if (vartok->str() == ":") vartok = vartok->next(); Token *argtok = tok->tokAt(-2); while (argtok && argtok->str() != "(") { if (argtok->str() == vartok->str()) break; if (argtok->str() == ")") argtok = argtok->link(); argtok = argtok->previous(); } if (argtok && argtok->str() == vartok->str()) { if (vartok->next()->str() == ">=") argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))); else if (vartok->next()->str() == ">") argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(vartok->strAt(2))+1); else if (vartok->next()->str() == "<=") argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))); else if (vartok->next()->str() == "<") argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(vartok->strAt(2))-1); } } Token::eraseTokens(tok, tok->link()->next()); tok->deleteThis(); } } static const std::set keywords = { "inline" , "_inline" , "__inline" , "__forceinline" , "register" , "__restrict" , "__restrict__" , "__thread" }; // Remove "inline", "register", "restrict", "override", "static" and "constexpr" // "restrict" keyword // - New to 1999 ANSI/ISO C standard // - Not in C++ standard yet void Tokenizer::simplifyKeyword() { // FIXME: There is a risk that "keywords" are removed by mistake. This // code should be fixed so it doesn't remove variables etc. Nonstandard // keywords should be defined with a library instead. For instance the // linux kernel code at least uses "_inline" as struct member name at some // places. const bool c99 = isC() && mSettings->standards.c >= Standards::C99; const bool cpp11 = isCPP() && mSettings->standards.cpp >= Standards::CPP11; for (Token *tok = list.front(); tok; tok = tok->next()) { if (keywords.find(tok->str()) != keywords.end()) { // Don't remove struct members if (!Token::simpleMatch(tok->previous(), ".")) tok->deleteThis(); // Simplify.. } if (isC() || mSettings->standards.cpp == Standards::CPP03) { if (tok->str() == "auto") tok->deleteThis(); } // simplify static keyword: // void foo( int [ static 5 ] ); ==> void foo( int [ 5 ] ); if (Token::Match(tok, "[ static %num%")) tok->deleteNext(); if (c99) { while (tok->str() == "restrict") tok->deleteThis(); if (mSettings->standards.c >= Standards::C11) { while (tok->str() == "_Atomic") tok->deleteThis(); } } else if (cpp11) { if (tok->str() == "constexpr") { tok->originalName(tok->str()); tok->str("const"); } // final: // 1) struct name final { }; <- struct is final if (Token::Match(tok->previous(), "struct|class|union %type% final [:{]")) { tok->deleteNext(); } // noexcept -> noexcept(true) // 2) void f() noexcept; -> void f() noexcept(true); else if (Token::Match(tok, ") noexcept :|{|;|const|override|final")) { // Insertion is done in inverse order // The brackets are linked together accordingly afterwards Token * tokNoExcept = tok->next(); tokNoExcept->insertToken(")"); Token * braceEnd = tokNoExcept->next(); tokNoExcept->insertToken("true"); tokNoExcept->insertToken("("); Token * braceStart = tokNoExcept->next(); tok = tok->tokAt(3); Token::createMutualLinks(braceStart, braceEnd); } // 3) thread_local -> static // on single thread thread_local has the effect of static else if (tok->str() == "thread_local") { tok->originalName(tok->str()); tok->str("static"); } } } } void Tokenizer::simplifyAssignmentInFunctionCall() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "(") tok = tok->link(); // Find 'foo(var='. Exclude 'assert(var=' to allow tests to check that assert(...) does not contain side-effects else if (Token::Match(tok, "[;{}] %name% ( %name% =") && Token::simpleMatch(tok->linkAt(2), ") ;") && !Token::Match(tok->next(), "assert|while")) { const std::string& funcname(tok->next()->str()); Token* const vartok = tok->tokAt(3); // Goto ',' or ')'.. for (Token *tok2 = vartok->tokAt(2); tok2; tok2 = tok2->next()) { if (tok2->link() && Token::Match(tok2, "(|[|{")) tok2 = tok2->link(); else if (tok2->str() == ";") break; else if (Token::Match(tok2, ")|,")) { tok2 = tok2->previous(); tok2->insertToken(vartok->str()); tok2->next()->varId(vartok->varId()); tok2->insertToken("("); Token::createMutualLinks(tok2->next(), tok->linkAt(2)); tok2->insertToken(funcname); tok2->insertToken(";"); Token::eraseTokens(tok, vartok); break; } } } } } void Tokenizer::simplifyAssignmentBlock() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "[;{}] %name% = ( {")) { const std::string &varname = tok->next()->str(); // goto the "} )" int indentlevel = 0; Token *tok2 = tok; while (nullptr != (tok2 = tok2->next())) { if (Token::Match(tok2, "(|{")) ++indentlevel; else if (Token::Match(tok2, ")|}")) { if (indentlevel <= 2) break; --indentlevel; } else if (indentlevel == 2 && tok2->str() == varname && Token::Match(tok2->previous(), "%type%|*")) // declaring variable in inner scope with same name as lhs variable break; } if (indentlevel == 2 && Token::simpleMatch(tok2, "} )")) { tok2 = tok2->tokAt(-3); if (Token::Match(tok2, "[;{}] %num%|%name% ;")) { tok2->insertToken("="); tok2->insertToken(tok->next()->str()); tok2->next()->varId(tok->next()->varId()); tok->deleteNext(3); tok2->tokAt(5)->deleteNext(); } } } } } // Remove __asm.. void Tokenizer::simplifyAsm() { std::string instruction; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "__asm|_asm|asm {") && tok->next()->link()->next()) { instruction = tok->tokAt(2)->stringifyList(tok->next()->link()); Token::eraseTokens(tok, tok->next()->link()->next()); } else if (Token::Match(tok, "asm|__asm|__asm__ volatile|__volatile|__volatile__| (")) { // Goto "(" Token *partok = tok->next(); if (partok->str() != "(") partok = partok->next(); instruction = partok->next()->stringifyList(partok->link()); Token::eraseTokens(tok, partok->link()->next()); } else if (Token::Match(tok, "_asm|__asm")) { Token *endasm = tok->next(); const Token *firstSemiColon = nullptr; int comment = 0; while (Token::Match(endasm, "%num%|%name%|,|:|;") || (endasm && endasm->linenr() == comment)) { if (Token::Match(endasm, "_asm|__asm|__endasm")) break; if (endasm->str() == ";") { comment = endasm->linenr(); if (!firstSemiColon) firstSemiColon = endasm; } endasm = endasm->next(); } if (Token::simpleMatch(endasm, "__endasm")) { instruction = tok->next()->stringifyList(endasm); Token::eraseTokens(tok, endasm->next()); if (!Token::simpleMatch(tok->next(), ";")) tok->insertToken(";"); } else if (firstSemiColon) { instruction = tok->next()->stringifyList(firstSemiColon); Token::eraseTokens(tok, firstSemiColon); } else if (!endasm) { instruction = tok->next()->stringifyList(endasm); Token::eraseTokens(tok, endasm); tok->insertToken(";"); } else continue; } else continue; // insert "asm ( "instruction" )" tok->str("asm"); if (tok->strAt(1) != ";" && tok->strAt(1) != "{") tok->insertToken(";"); tok->insertToken(")"); tok->insertToken("\"" + instruction + "\""); tok->insertToken("("); tok = tok->next(); Token::createMutualLinks(tok, tok->tokAt(2)); //move the new tokens in the same line as ";" if available tok = tok->tokAt(2); if (tok->next() && tok->next()->str() == ";" && tok->next()->linenr() != tok->linenr()) { const int endposition = tok->next()->linenr(); tok = tok->tokAt(-3); for (int i = 0; i < 4; ++i) { tok = tok->next(); tok->linenr(endposition); } } } } void Tokenizer::simplifyAsm2() { // Block declarations: ^{} // A C extension used to create lambda like closures. // Put ^{} statements in asm() for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() != "^") continue; if (Token::simpleMatch(tok, "^ {") || (Token::simpleMatch(tok->linkAt(1), ") {") && tok->strAt(-1) != "operator")) { Token * start = tok; while (start && !Token::Match(start, "[,(;{}=]")) { if (start->link() && Token::Match(start, ")|]|>")) start = start->link(); start = start->previous(); } const Token *last = tok->next()->link(); if (Token::simpleMatch(last, ") {")) last = last->linkAt(1); last = last->next(); while (last && !Token::Match(last, "%cop%|,|;|{|}|)")) { if (Token::Match(last, "(|[")) last = last->link(); last = last->next(); } if (start && last) { std::string asmcode; while (start->next() != last) { asmcode += start->next()->str(); start->deleteNext(); } if (last->str() == "}") start->insertToken(";"); start->insertToken(")"); start->insertToken("\"" + asmcode + "\""); start->insertToken("("); start->insertToken("asm"); start->tokAt(2)->link(start->tokAt(4)); start->tokAt(4)->link(start->tokAt(2)); tok = start->tokAt(4); } } } } void Tokenizer::simplifyAt() { std::set var; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "%name% @ %num% ;")) { var.insert(tok->str()); tok->isAtAddress(true); Token::eraseTokens(tok,tok->tokAt(3)); } if (Token::Match(tok, "%name% @ %num% : %num% ;")) { var.insert(tok->str()); tok->isAtAddress(true); Token::eraseTokens(tok,tok->tokAt(5)); } if (Token::Match(tok, "%name% @ %name% : %num% ;") && var.find(tok->strAt(2)) != var.end()) { var.insert(tok->str()); tok->isAtAddress(true); Token::eraseTokens(tok,tok->tokAt(5)); } // array declaration if (Token::Match(tok, "] @ %num% ;")) { tok->isAtAddress(true); Token::eraseTokens(tok,tok->tokAt(3)); } // keywords in compiler from cosmic software for STM8 // TODO: Should use platform configuration. if (Token::Match(tok, "@ builtin|eeprom|far|inline|interrupt|near|noprd|nostack|nosvf|packed|stack|svlreg|tiny|vector")) { tok->str(tok->next()->str() + "@"); tok->deleteNext(); } } } // Simplify bitfields void Tokenizer::simplifyBitfields() { bool goback = false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (goback) { goback = false; tok = tok->previous(); } Token *last = nullptr; if (!Token::Match(tok, ";|{|}|public:|protected:|private:")) continue; bool isEnum = false; if (tok->str() == "}") { const Token *type = tok->link()->previous(); while (type && type->isName()) { if (type->str() == "enum") { isEnum = true; break; } type = type->previous(); } } if (Token::Match(tok->next(), "const| %type% %name% :") && !Token::Match(tok->next(), "case|public|protected|private|class|struct") && !Token::simpleMatch(tok->tokAt(2), "default :")) { Token *tok1 = (tok->next()->str() == "const") ? tok->tokAt(3) : tok->tokAt(2); if (Token::Match(tok1, "%name% : %num% ;")) tok1->setBits(MathLib::toLongNumber(tok1->strAt(2))); if (tok1 && tok1->tokAt(2) && (Token::Match(tok1->tokAt(2), "%bool%|%num%") || !Token::Match(tok1->tokAt(2), "public|protected|private| %type% ::|<|,|{|;"))) { while (tok1->next() && !Token::Match(tok1->next(), "[;,)]{}]")) { if (Token::Match(tok1->next(), "[([]")) Token::eraseTokens(tok1, tok1->next()->link()); tok1->deleteNext(); } last = tok1->next(); } } else if (isEnum && Token::Match(tok, "} %name%| : %num% ;")) { if (tok->next()->str() == ":") { tok->deleteNext(2); tok->insertToken("Anonymous"); } else { tok->next()->deleteNext(2); } } else if (Token::Match(tok->next(), "const| %type% : %num%|%bool% ;") && tok->next()->str() != "default") { const int offset = (tok->next()->str() == "const") ? 1 : 0; if (!Token::Match(tok->tokAt(3 + offset), "[{};()]")) { tok->deleteNext(4 + offset); goback = true; } } if (last && last->str() == ",") { Token * tok1 = last; tok1->str(";"); const Token *const tok2 = tok->next(); tok1->insertToken(tok2->str()); tok1 = tok1->next(); tok1->isSigned(tok2->isSigned()); tok1->isUnsigned(tok2->isUnsigned()); tok1->isLong(tok2->isLong()); } } } // Types and objects in std namespace that are neither functions nor templates static const std::set stdTypes = { "string", "wstring", "u16string", "u32string", "iostream", "ostream", "ofstream", "ostringstream", "istream", "ifstream", "istringstream", "fstream", "stringstream", "wstringstream", "wistringstream", "wostringstream", "wstringbuf", "stringbuf", "streambuf", "ios", "filebuf", "ios_base", "exception", "bad_exception", "bad_alloc", "logic_error", "domain_error", "invalid_argument_", "length_error", "out_of_range", "runtime_error", "range_error", "overflow_error", "underflow_error", "locale", "cout", "cerr", "clog", "cin", "wcerr", "wcin", "wclog", "wcout", "endl", "ends", "flush", "boolalpha", "noboolalpha", "showbase", "noshowbase", "showpoint", "noshowpoint", "showpos", "noshowpos", "skipws", "noskipws", "unitbuf", "nounitbuf", "uppercase", "nouppercase", "dec", "hex", "oct", "fixed", "scientific", "internal", "left", "right", "fpos", "streamoff", "streampos", "streamsize" }; static const std::set stdTemplates = { "array", "basic_string", "bitset", "deque", "list", "map", "multimap", "priority_queue", "queue", "set", "multiset", "stack", "vector", "pair", "iterator", "iterator_traits", "unordered_map", "unordered_multimap", "unordered_set", "unordered_multiset", "tuple", "function" }; static const std::set stdFunctions = { "getline", "for_each", "find", "find_if", "find_end", "find_first_of", "adjacent_find", "count", "count_if", "mismatch", "equal", "search", "search_n", "copy", "copy_backward", "swap", "swap_ranges", "iter_swap", "transform", "replace", "replace_if", "replace_copy", "replace_copy_if", "fill", "fill_n", "generate", "generate_n", "remove", "remove_if", "remove_copy", "remove_copy_if", "unique", "unique_copy", "reverse", "reverse_copy", "rotate", "rotate_copy", "random_shuffle", "partition", "stable_partition", "sort", "stable_sort", "partial_sort", "partial_sort_copy", "nth_element", "lower_bound", "upper_bound", "equal_range", "binary_search", "merge", "inplace_merge", "includes", "set_union", "set_intersection", "set_difference", "set_symmetric_difference", "push_heap", "pop_heap", "make_heap", "sort_heap", "min", "max", "min_element", "max_element", "lexicographical_compare", "next_permutation", "prev_permutation", "advance", "back_inserter", "distance", "front_inserter", "inserter", "make_pair", "make_shared", "make_tuple" }; // Add std:: in front of std classes, when using namespace std; was given void Tokenizer::simplifyNamespaceStd() { if (!isCPP()) return; const bool isCPP11 = mSettings->standards.cpp == Standards::CPP11; std::set userFunctions; for (const Token* tok = Token::findsimplematch(list.front(), "using namespace std ;"); tok; tok = tok->next()) { bool insert = false; if (Token::Match(tok, "enum class|struct| %name%| :|{")) { // Don't replace within enum definitions skipEnumBody(&tok); } if (!Token::Match(tok->previous(), ".|::")) { if (Token::Match(tok, "%name% (")) { if (isFunctionHead(tok->next(), "{")) userFunctions.insert(tok->str()); else if (isFunctionHead(tok->next(), ";")) { const Token *start = tok; while (Token::Match(start->previous(), "%type%|*|&")) start = start->previous(); if (start != tok && start->isName() && (!start->previous() || Token::Match(start->previous(), "[;{}]"))) userFunctions.insert(tok->str()); } if (userFunctions.find(tok->str()) == userFunctions.end() && stdFunctions.find(tok->str()) != stdFunctions.end()) insert = true; } else if (Token::Match(tok, "%name% <") && stdTemplates.find(tok->str()) != stdTemplates.end()) insert = true; else if (tok->isName() && !tok->varId() && !Token::Match(tok->next(), "(|<") && stdTypes.find(tok->str()) != stdTypes.end()) insert = true; } if (insert) { tok->previous()->insertToken("std"); tok->previous()->linenr(tok->linenr()); // For stylistic reasons we put the std:: in the same line as the following token tok->previous()->fileIndex(tok->fileIndex()); tok->previous()->insertToken("::"); } else if (isCPP11 && Token::Match(tok, "!!:: tr1 ::")) tok->next()->str("std"); } for (Token* tok = list.front(); tok; tok = tok->next()) { if (isCPP11 && Token::simpleMatch(tok, "std :: tr1 ::")) Token::eraseTokens(tok, tok->tokAt(3)); else if (Token::simpleMatch(tok, "using namespace std ;")) { Token::eraseTokens(tok, tok->tokAt(4)); tok->deleteThis(); } } } void Tokenizer::simplifyMicrosoftMemoryFunctions() { // skip if not Windows if (!mSettings->isWindowsPlatform()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->strAt(1) != "(") continue; if (Token::Match(tok, "CopyMemory|RtlCopyMemory|RtlCopyBytes")) { tok->str("memcpy"); } else if (Token::Match(tok, "MoveMemory|RtlMoveMemory")) { tok->str("memmove"); } else if (Token::Match(tok, "FillMemory|RtlFillMemory|RtlFillBytes")) { // FillMemory(dst, len, val) -> memset(dst, val, len) tok->str("memset"); Token *tok1 = tok->tokAt(2); if (tok1) tok1 = tok1->nextArgument(); // Second argument if (tok1) { Token *tok2 = tok1->nextArgument(); // Third argument if (tok2) Token::move(tok1->previous(), tok2->tokAt(-2), tok->next()->link()->previous()); // Swap third with second argument } } else if (Token::Match(tok, "ZeroMemory|RtlZeroMemory|RtlZeroBytes|RtlSecureZeroMemory")) { // ZeroMemory(dst, len) -> memset(dst, 0, len) tok->str("memset"); Token *tok1 = tok->tokAt(2); if (tok1) tok1 = tok1->nextArgument(); // Second argument if (tok1) { tok1 = tok1->previous(); tok1->insertToken("0"); tok1 = tok1->next(); tok1->insertToken(","); } } else if (Token::simpleMatch(tok, "RtlCompareMemory")) { // RtlCompareMemory(src1, src2, len) -> memcmp(src1, src2, len) tok->str("memcmp"); // For the record, when memcmp returns 0, both strings are equal. // When RtlCompareMemory returns len, both strings are equal. // It might be needed to improve this replacement by something // like ((len - memcmp(src1, src2, len)) % (len + 1)) to // respect execution path (if required) } } } namespace { struct triplet { triplet(const char* m, const char* u) : mbcs(m), unicode(u) {} std::string mbcs, unicode; }; const std::map apis = { std::make_pair("_topen", triplet("open", "_wopen")), std::make_pair("_tsopen_s", triplet("_sopen_s", "_wsopen_s")), std::make_pair("_tfopen", triplet("fopen", "_wfopen")), std::make_pair("_tfopen_s", triplet("fopen_s", "_wfopen_s")), std::make_pair("_tfreopen", triplet("freopen", "_wfreopen")), std::make_pair("_tfreopen_s", triplet("freopen_s", "_wfreopen_s")), std::make_pair("_tcscat", triplet("strcat", "wcscat")), std::make_pair("_tcschr", triplet("strchr", "wcschr")), std::make_pair("_tcscmp", triplet("strcmp", "wcscmp")), std::make_pair("_tcsdup", triplet("strdup", "wcsdup")), std::make_pair("_tcscpy", triplet("strcpy", "wcscpy")), std::make_pair("_tcslen", triplet("strlen", "wcslen")), std::make_pair("_tcsncat", triplet("strncat", "wcsncat")), std::make_pair("_tcsncpy", triplet("strncpy", "wcsncpy")), std::make_pair("_tcsnlen", triplet("strnlen", "wcsnlen")), std::make_pair("_tcsrchr", triplet("strrchr", "wcsrchr")), std::make_pair("_tcsstr", triplet("strstr", "wcsstr")), std::make_pair("_tcstok", triplet("strtok", "wcstok")), std::make_pair("_ftprintf", triplet("fprintf", "fwprintf")), std::make_pair("_tprintf", triplet("printf", "wprintf")), std::make_pair("_stprintf", triplet("sprintf", "swprintf")), std::make_pair("_sntprintf", triplet("_snprintf", "_snwprintf")), std::make_pair("_ftscanf", triplet("fscanf", "fwscanf")), std::make_pair("_tscanf", triplet("scanf", "wscanf")), std::make_pair("_stscanf", triplet("sscanf", "swscanf")), std::make_pair("_ftprintf_s", triplet("fprintf_s", "fwprintf_s")), std::make_pair("_tprintf_s", triplet("printf_s", "wprintf_s")), std::make_pair("_stprintf_s", triplet("sprintf_s", "swprintf_s")), std::make_pair("_sntprintf_s", triplet("_snprintf_s", "_snwprintf_s")), std::make_pair("_ftscanf_s", triplet("fscanf_s", "fwscanf_s")), std::make_pair("_tscanf_s", triplet("scanf_s", "wscanf_s")), std::make_pair("_stscanf_s", triplet("sscanf_s", "swscanf_s")) }; } void Tokenizer::simplifyMicrosoftStringFunctions() { // skip if not Windows if (!mSettings->isWindowsPlatform()) return; const bool ansi = mSettings->platformType == Settings::Win32A; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->strAt(1) != "(") continue; const std::map::const_iterator match = apis.find(tok->str()); if (match!=apis.end()) { tok->str(ansi ? match->second.mbcs : match->second.unicode); tok->originalName(match->first); } else if (Token::Match(tok, "_T|_TEXT|TEXT ( %char%|%str% )")) { tok->deleteNext(); tok->deleteThis(); tok->deleteNext(); if (!ansi) { tok->isLong(true); if (tok->str()[0] != 'L') tok->str("L" + tok->str()); } while (Token::Match(tok->next(), "_T|_TEXT|TEXT ( %char%|%str% )")) { tok->next()->deleteNext(); tok->next()->deleteThis(); tok->next()->deleteNext(); tok->concatStr(tok->next()->str()); tok->deleteNext(); } } } } // Remove Borland code void Tokenizer::simplifyBorland() { // skip if not Windows if (!mSettings->isWindowsPlatform()) return; if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "( __closure * %name% )")) { tok->deleteNext(); } } // I think that these classes are always declared at the outer scope // I save some time by ignoring inner classes. for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{" && !Token::Match(tok->tokAt(-2), "namespace %type%")) { tok = tok->link(); if (!tok) break; } else if (Token::Match(tok, "class %name% :|{")) { while (tok && tok->str() != "{" && tok->str() != ";") tok = tok->next(); if (!tok) break; if (tok->str() == ";") continue; const Token* end = tok->link()->next(); for (Token *tok2 = tok->next(); tok2 != end; tok2 = tok2->next()) { if (tok2->str() == "__property" && Token::Match(tok2->previous(), ";|{|}|protected:|public:|__published:")) { while (tok2->next() && !Token::Match(tok2->next(), "{|;")) tok2->deleteNext(); tok2->deleteThis(); if (tok2->str() == "{") { Token::eraseTokens(tok2, tok2->link()); tok2->deleteNext(); tok2->deleteThis(); // insert "; __property ;" tok2->previous()->insertToken(";"); tok2->previous()->insertToken("__property"); tok2->previous()->insertToken(";"); } } } } } } // Remove Qt signals and slots void Tokenizer::simplifyQtSignalsSlots() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { // check for emit which can be outside of class if (Token::Match(tok, "emit|Q_EMIT %name% (") && Token::simpleMatch(tok->linkAt(2), ") ;")) { tok->deleteThis(); } else if (!Token::Match(tok, "class %name% :")) continue; if (tok->previous() && tok->previous()->str() == "enum") { tok = tok->tokAt(2); continue; } // count { and } for tok2 int indentlevel = 0; for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { if (tok2->str() == "{") { ++indentlevel; if (indentlevel == 1) tok = tok2; else tok2 = tok2->link(); } else if (tok2->str() == "}") { if (indentlevel<2) break; else --indentlevel; } if (tok2->strAt(1) == "Q_OBJECT") tok2->deleteNext(); if (Token::Match(tok2->next(), "public|protected|private slots|Q_SLOTS :")) { tok2 = tok2->next(); tok2->str(tok2->str() + ":"); tok2->deleteNext(2); tok2 = tok2->previous(); } else if (Token::Match(tok2->next(), "signals|Q_SIGNALS :")) { tok2 = tok2->next(); tok2->str("protected:"); tok2->deleteNext(); } else if (Token::Match(tok2->next(), "emit|Q_EMIT %name% (") && Token::simpleMatch(tok2->linkAt(3), ") ;")) { tok2->deleteNext(); } } } } void Tokenizer::createSymbolDatabase() { if (!mSymbolDatabase) mSymbolDatabase = new SymbolDatabase(this, mSettings, mErrorLogger); mSymbolDatabase->validate(); } void Tokenizer::deleteSymbolDatabase() { delete mSymbolDatabase; mSymbolDatabase = nullptr; } bool Tokenizer::operatorEnd(const Token * tok) const { if (tok && tok->str() == ")") { if (isFunctionHead(tok, "{|;|?|:|[")) return true; tok = tok->next(); while (tok && !Token::Match(tok, "[=;{),]")) { if (Token::Match(tok, "const|volatile|override")) { tok = tok->next(); } else if (tok->str() == "noexcept") { tok = tok->next(); if (tok && tok->str() == "(") { tok = tok->link()->next(); } } else if (tok->str() == "throw" && tok->next() && tok->next()->str() == "(") { tok = tok->next()->link()->next(); } // unknown macros ") MACRO {" and ") MACRO(...) {" else if (tok->isUpperCaseName()) { tok = tok->next(); if (tok && tok->str() == "(") { tok = tok->link()->next(); } } else if (Token::Match(tok, "%op% !!(") || (Token::Match(tok, "%op% (") && !isFunctionHead(tok->next(), "{"))) break; else return false; } return true; } return false; } void Tokenizer::simplifyOperatorName() { if (isC()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "using|:: operator %op%|%name% ;")) { tok->next()->str("operator" + tok->strAt(2)); tok->next()->deleteNext(); continue; } if (tok->str() != "operator") continue; // operator op std::string op; Token *par = tok->next(); bool done = false; while (!done && par) { done = true; if (par->isName()) { op += par->str(); par = par->next(); // merge namespaces eg. 'operator std :: string () const {' if (Token::Match(par, ":: %name%|%op%|.")) { op += par->str(); par = par->next(); } done = false; } else if (Token::Match(par, ".|%op%|,")) { // check for operator in template if (par->str() == "," && !op.empty()) break; if (!(Token::Match(par, "<|>") && !op.empty())) { op += par->str(); par = par->next(); done = false; } } else if (Token::simpleMatch(par, "[ ]")) { op += "[]"; par = par->tokAt(2); done = false; } else if (Token::Match(par, "( *| )")) { // break out and simplify.. if (operatorEnd(par->next())) break; while (par->str() != ")") { op += par->str(); par = par->next(); } op += ")"; par = par->next(); done = false; } else if (Token::Match(par, "\"\" %name% (|;|<")) { op += "\"\""; op += par->strAt(1); par = par->tokAt(2); done = true; } else if (par->str() == "::") { op += par->str(); par = par->next(); done = false; } else if (par->str() == ";" || par->str() == ")") { done = true; } else if (par->str() != "(") { syntaxError(par, "operator"); } } if (par && !op.empty()) { tok->str("operator" + op); Token::eraseTokens(tok, par); } if (!op.empty()) tok->isOperatorKeyword(true); } if (mSettings->debugwarnings) { const Token *tok = list.front(); while ((tok = Token::findsimplematch(tok, "operator")) != nullptr) { reportError(tok, Severity::debug, "debug", "simplifyOperatorName: found unsimplified operator name"); tok = tok->next(); } } } // remove unnecessary member qualification.. void Tokenizer::removeUnnecessaryQualification() { if (isC()) return; std::vector classInfo; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "class|struct|namespace %type% :|{") && (!tok->previous() || tok->previous()->str() != "enum")) { Space info; info.isNamespace = tok->str() == "namespace"; tok = tok->next(); info.className = tok->str(); tok = tok->next(); while (tok && tok->str() != "{") tok = tok->next(); if (!tok) return; info.bodyEnd = tok->link(); classInfo.push_back(info); } else if (!classInfo.empty()) { if (tok == classInfo.back().bodyEnd) classInfo.pop_back(); else if (tok->str() == classInfo.back().className && !classInfo.back().isNamespace && tok->previous()->str() != ":" && (Token::Match(tok, "%type% :: ~| %type% (") || Token::Match(tok, "%type% :: operator"))) { const Token *tok1 = tok->tokAt(3); if (tok->strAt(2) == "operator") { // check for operator () if (tok1->str() == "(") tok1 = tok1->next(); while (tok1 && tok1->str() != "(") { if (tok1->str() == ";") break; tok1 = tok1->next(); } if (!tok1 || tok1->str() != "(") continue; } else if (tok->strAt(2) == "~") tok1 = tok1->next(); if (!tok1 || !Token::Match(tok1->link(), ") const| {|;|:")) { continue; } const bool isConstructorOrDestructor = Token::Match(tok, "%type% :: ~| %type%") && (tok->strAt(2) == tok->str() || (tok->strAt(2) == "~" && tok->strAt(3) == tok->str())); if (!isConstructorOrDestructor) { bool isPrependedByType = Token::Match(tok->previous(), "%type%"); if (!isPrependedByType) { const Token* tok2 = tok->tokAt(-2); isPrependedByType = Token::Match(tok2, "%type% *|&"); } if (!isPrependedByType) { const Token* tok3 = tok->tokAt(-3); isPrependedByType = Token::Match(tok3, "%type% * *|&"); } if (!isPrependedByType) { // It's not a constructor declaration and it's not a function declaration so // this is a function call which can have all the qualifiers just fine - skip. continue; } } } } } } void Tokenizer::simplifyReturnStrncat() { for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "return strncat (") && Token::simpleMatch(tok->linkAt(2), ") ;") && tok->strAt(3) != ")" && tok->strAt(3) != ",") { //first argument Token *tok2 = tok->tokAt(3); //check if there are at least three arguments for (int i = 0; i < 2; ++i) { tok2 = tok2->nextArgument(); if (!tok2) { tok = tok->linkAt(2)->next(); break; } } if (!tok2) continue; tok2 = tok2->nextArgument(); //we want only three arguments if (tok2) { tok = tok->linkAt(2)->next(); continue; } // Remove 'return' tok->deleteThis(); // Add 'return arg1 ;' after 'strncat(arg1, arg2, arg3);' tok = tok->next(); tok2 = tok->link()->next(); tok2->insertToken(";"); //the last token of the first argument before ',' const Token * const end = tok->next()->nextArgument()->tokAt(-2); //all the first argument is copied TokenList::copyTokens(tok2, tok->next(), end); tok2->insertToken("return"); } } } void Tokenizer::printUnknownTypes() const { if (!mSymbolDatabase) return; std::multimap unknowns; for (int i = 1; i <= mVarId; ++i) { const Variable *var = mSymbolDatabase->getVariableFromVarId(i); if (!var) continue; // is unknown type? if (var->type() || var->typeStartToken()->isStandardType()) continue; std::string name; const Token * nameTok; // single token type? if (var->typeStartToken() == var->typeEndToken()) { nameTok = var->typeStartToken(); name = nameTok->str(); } // complicated type else { const Token *tok = var->typeStartToken(); int level = 0; nameTok = tok; while (tok) { // skip pointer and reference part of type if (level == 0 && Token::Match(tok, "*|&")) break; name += tok->str(); if (Token::Match(tok, "struct|union|enum")) name += " "; // pointers and references are OK in template else if (tok->str() == "<") ++level; else if (tok->str() == ">") --level; if (tok == var->typeEndToken()) break; tok = tok->next(); } } unknowns.insert(std::pair(name, nameTok)); } if (!unknowns.empty()) { std::string last; int count = 0; for (std::multimap::const_iterator it = unknowns.begin(); it != unknowns.end(); ++it) { // skip types is std namespace because they are not interesting if (it->first.find("std::") != 0) { if (it->first != last) { last = it->first; count = 1; reportError(it->second, Severity::debug, "debug", "Unknown type \'" + it->first + "\'."); } else { if (count < 3) // limit same type to 3 reportError(it->second, Severity::debug, "debug", "Unknown type \'" + it->first + "\'."); count++; } } } } } void Tokenizer::simplifyMathExpressions() { for (Token *tok = list.front(); tok; tok = tok->next()) { //simplify Pythagorean trigonometric identity: pow(sin(x),2)+pow(cos(x),2) = 1 // pow(cos(x),2)+pow(sin(x),2) = 1 // @todo: sin(x) * sin(x) + cos(x) * cos(x) = 1 // cos(x) * cos(x) + sin(x) * sin(x) = 1 //simplify Hyperbolic identity: pow(sinh(x),2)-pow(cosh(x),2) = -1 // pow(cosh(x),2)-pow(sinh(x),2) = -1 // @todo: sinh(x) * sinh(x) - cosh(x) * cosh(x) = -1 // cosh(x) * cosh(x) - sinh(x) * sinh(x) = -1 if (Token::Match(tok, "pow|powf|powl (")) { if (Token::Match(tok->tokAt(2), "sin|sinf|sinl (")) { Token * const tok2 = tok->linkAt(3); if (!Token::Match(tok2, ") , %num% ) + pow|powf|powl ( cos|cosf|cosl (")) continue; const std::string& leftExponent = tok2->strAt(2); if (!isTwoNumber(leftExponent)) continue; // left exponent is not 2 const Token * const tok3 = tok2->tokAt(8); Token * const tok4 = tok3->link(); if (!Token::Match(tok4, ") , %num% )")) continue; const std::string& rightExponent = tok4->strAt(2); if (!isTwoNumber(rightExponent)) continue; // right exponent is not 2 if (tok->tokAt(3)->stringifyList(tok2->next()) == tok3->stringifyList(tok4->next())) { Token::eraseTokens(tok, tok4->tokAt(4)); tok->str("1"); } } else if (Token::Match(tok->tokAt(2), "cos|cosf|cosl (")) { Token * const tok2 = tok->linkAt(3); if (!Token::Match(tok2, ") , %num% ) + pow|powf|powl ( sin|sinf|sinl (")) continue; const std::string& leftExponent = tok2->strAt(2); if (!isTwoNumber(leftExponent)) continue; // left exponent is not 2 const Token * const tok3 = tok2->tokAt(8); Token * const tok4 = tok3->link(); if (!Token::Match(tok4, ") , %num% )")) continue; const std::string& rightExponent = tok4->strAt(2); if (!isTwoNumber(rightExponent)) continue; // right exponent is not 2 if (tok->tokAt(3)->stringifyList(tok2->next()) == tok3->stringifyList(tok4->next())) { Token::eraseTokens(tok, tok4->tokAt(4)); tok->str("1"); } } else if (Token::Match(tok->tokAt(2), "sinh|sinhf|sinhl (")) { Token * const tok2 = tok->linkAt(3); if (!Token::Match(tok2, ") , %num% ) - pow|powf|powl ( cosh|coshf|coshl (")) continue; const std::string& leftExponent = tok2->strAt(2); if (!isTwoNumber(leftExponent)) continue; // left exponent is not 2 const Token * const tok3 = tok2->tokAt(8); Token * const tok4 = tok3->link(); if (!Token::Match(tok4, ") , %num% )")) continue; const std::string& rightExponent = tok4->strAt(2); if (!isTwoNumber(rightExponent)) continue; // right exponent is not 2 if (tok->tokAt(3)->stringifyList(tok2->next()) == tok3->stringifyList(tok4->next())) { Token::eraseTokens(tok, tok4->tokAt(4)); tok->str("-1"); } } else if (Token::Match(tok->tokAt(2), "cosh|coshf|coshl (")) { Token * const tok2 = tok->linkAt(3); if (!Token::Match(tok2, ") , %num% ) - pow|powf|powl ( sinh|sinhf|sinhl (")) continue; const std::string& leftExponent = tok2->strAt(2); if (!isTwoNumber(leftExponent)) continue; // left exponent is not 2 const Token * const tok3 = tok2->tokAt(8); Token * const tok4 = tok3->link(); if (!Token::Match(tok4, ") , %num% )")) continue; const std::string& rightExponent = tok4->strAt(2); if (!isTwoNumber(rightExponent)) continue; // right exponent is not 2 if (tok->tokAt(3)->stringifyList(tok2->next()) == tok3->stringifyList(tok4->next())) { Token::eraseTokens(tok, tok4->tokAt(4)); tok->str("-1"); } } } } } bool Tokenizer::simplifyStrlen() { // replace strlen(str) bool modified=false; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "strlen ( %str% )")) { tok->str(MathLib::toString(Token::getStrLength(tok->tokAt(2)))); tok->deleteNext(3); modified=true; } } return modified; } void Tokenizer::prepareTernaryOpForAST() { // http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator: // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." // The AST parser relies on this function to add such parentheses where necessary. for (Token* tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "?") { bool parenthesesNeeded = false; int depth = 0; Token* tok2 = tok->next(); for (; tok2; tok2 = tok2->next()) { if (tok2->link() && Token::Match(tok2, "[|(|<")) tok2 = tok2->link(); else if (tok2->str() == ":") { if (depth == 0) break; depth--; } else if (tok2->str() == ";" || (tok2->link() && tok2->str() != "{" && tok2->str() != "}")) break; else if (tok2->str() == ",") parenthesesNeeded = true; else if (tok2->str() == "<") parenthesesNeeded = true; else if (tok2->str() == "?") { depth++; parenthesesNeeded = true; } } if (parenthesesNeeded && tok2 && tok2->str() == ":") { tok->insertToken("("); tok2->insertToken(")", emptyString, true); Token::createMutualLinks(tok->next(), tok2->previous()); } } } } void Tokenizer::reportError(const Token* tok, const Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive) const { const std::list callstack(1, tok); reportError(callstack, severity, id, msg, inconclusive); } void Tokenizer::reportError(const std::list& callstack, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive) const { const ErrorLogger::ErrorMessage errmsg(callstack, &list, severity, id, msg, inconclusive); if (mErrorLogger) mErrorLogger->reportErr(errmsg); else Check::reportError(errmsg); } void Tokenizer::setPodTypes() { if (!mSettings) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->isName()) continue; // pod type const struct Library::PodType *podType = mSettings->library.podtype(tok->str()); if (podType) { const Token *prev = tok->previous(); while (prev && prev->isName()) prev = prev->previous(); if (prev && !Token::Match(prev, ";|{|}|,|(")) continue; tok->isStandardType(true); } } } const Token *Tokenizer::findSQLBlockEnd(const Token *tokSQLStart) { const Token *tokLastEnd = nullptr; for (const Token *tok = tokSQLStart->tokAt(2); tok != nullptr; tok = tok->next()) { if (tokLastEnd == nullptr && tok->str() == ";") tokLastEnd = tok; else if (tok->str() == "__CPPCHECK_EMBEDDED_SQL_EXEC__") { if (Token::simpleMatch(tok->tokAt(-2), "END - __CPPCHECK_EMBEDDED_SQL_EXEC__ ;")) return tok->next(); return tokLastEnd; } else if (Token::Match(tok, "{|}|==|&&|!|^|<<|>>|++|+=|-=|/=|*=|>>=|<<=|~")) break; // We are obviously outside the SQL block } return tokLastEnd; } void Tokenizer::simplifyNestedNamespace() { if (!isCPP()) return; for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::Match(tok, "namespace %name% ::") && tok->strAt(-1) != "using") { Token * tok2 = tok->tokAt(2); // validate syntax while (Token::Match(tok2, ":: %name%")) tok2 = tok2->tokAt(2); if (!tok2 || tok2->str() != "{") return; // syntax error std::stack links; tok2 = tok->tokAt(2); while (tok2->str() == "::") { links.push(tok2); tok2->str("{"); tok2->insertToken("namespace"); tok2 = tok2->tokAt(3); } tok = tok2; if (!links.empty() && tok2->str() == "{") { tok2 = tok2->link(); while (!links.empty()) { tok2->insertToken("}"); tok2 = tok2->next(); Token::createMutualLinks(links.top(), tok2); links.pop(); } } } } } static bool sameTokens(const Token *first, const Token *last, const Token *other) { while (other && first->str() == other->str()) { if (first == last) return true; first = first->next(); other = other->next(); } return false; } static Token * deleteAlias(Token * tok) { Token::eraseTokens(tok, Token::findsimplematch(tok, ";")); // delete first token tok->deleteThis(); // delete ';' if not last token tok->deleteThis(); return tok; } void Tokenizer::simplifyNamespaceAliases() { if (!isCPP()) return; int scope = 0; for (Token *tok = list.front(); tok; tok = tok->next()) { if (tok->str() == "{") scope++; else if (tok->str() == "}") scope--; else if (Token::Match(tok, "namespace %name% =")) { const std::string name(tok->next()->str()); Token * tokNameStart = tok->tokAt(3); Token * tokNameEnd = tokNameStart; while (tokNameEnd && tokNameEnd->next() && tokNameEnd->next()->str() != ";") tokNameEnd = tokNameEnd->next(); if (!tokNameEnd) return; // syntax error int endScope = scope; Token * tokLast = tokNameEnd->next(); Token * tokNext = tokLast->next(); Token * tok2 = tokNext; while (tok2 && endScope >= scope) { if (Token::simpleMatch(tok2, "{")) endScope++; else if (Token::simpleMatch(tok2, "}")) endScope--; else if (tok2->str() == name) { if (Token::Match(tok2->previous(), "namespace %name% =")) { // check for possible duplicate aliases if (sameTokens(tokNameStart, tokNameEnd, tok2->tokAt(2))) { // delete duplicate tok2 = deleteAlias(tok2->previous()); continue; } else { // conflicting declaration (syntax error) if (endScope == scope) { // delete conflicting declaration tok2 = deleteAlias(tok2->previous()); } // new declaration else { // TODO: use the new alias in this scope tok2 = deleteAlias(tok2->previous()); } continue; } } tok2->str(tokNameStart->str()); Token * tok3 = tokNameStart; while (tok3 != tokNameEnd) { tok2->insertToken(tok3->next()->str()); tok2 = tok2->next(); tok3 = tok3->next(); } } tok2 = tok2->next(); } if (tok->previous() && tokNext) { Token::eraseTokens(tok->previous(), tokNext); tok = tokNext->previous(); } else if (tok->previous()) { Token::eraseTokens(tok->previous(), tokLast); tok = tokLast; } else if (tokNext) { Token::eraseTokens(tok, tokNext); tok->deleteThis(); } else { Token::eraseTokens(tok, tokLast); tok->deleteThis(); } } } } Tokenizer::VariableMap::VariableMap() : mVarId(0) {} void Tokenizer::VariableMap::enterScope() { mScopeInfo.push(std::list>()); } bool Tokenizer::VariableMap::leaveScope() { if (mScopeInfo.empty()) return false; for (const std::pair &outerVariable : mScopeInfo.top()) { if (outerVariable.second != 0) mVariableId[outerVariable.first] = outerVariable.second; else mVariableId.erase(outerVariable.first); } mScopeInfo.pop(); return true; } void Tokenizer::VariableMap::addVariable(const std::string &varname) { if (mScopeInfo.empty()) { mVariableId[varname] = ++mVarId; return; } std::map::iterator it = mVariableId.find(varname); if (it == mVariableId.end()) { mScopeInfo.top().push_back(std::pair(varname, 0)); mVariableId[varname] = ++mVarId; return; } mScopeInfo.top().push_back(std::pair(varname, it->second)); it->second = ++mVarId; } bool Tokenizer::VariableMap::hasVariable(const std::string &varname) const { return mVariableId.find(varname) != mVariableId.end(); } cppcheck-1.90/lib/tokenize.h000066400000000000000000000645121357737443600160250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef tokenizeH #define tokenizeH //--------------------------------------------------------------------------- #include "config.h" #include "errorlogger.h" #include "tokenlist.h" #include #include #include #include #include class Settings; class SymbolDatabase; class TimerResults; class Token; class TemplateSimplifier; namespace simplecpp { class TokenList; } /// @addtogroup Core /// @{ /** @brief The main purpose is to tokenize the source code. It also has functions that simplify the token list */ class CPPCHECKLIB Tokenizer { friend class TestSimplifyTokens; friend class TestSimplifyTypedef; friend class TestSimplifyUsing; friend class TestTokenizer; friend class SymbolDatabase; friend class TestSimplifyTemplate; friend class TemplateSimplifier; /** Class used in Tokenizer::setVarIdPass1 */ class VariableMap { private: std::map mVariableId; std::stack > > mScopeInfo; mutable nonneg int mVarId; public: VariableMap(); void enterScope(); bool leaveScope(); void addVariable(const std::string &varname); bool hasVariable(const std::string &varname) const; std::map::const_iterator find(const std::string &varname) const { return mVariableId.find(varname); } std::map::const_iterator end() const { return mVariableId.end(); } const std::map &map() const { return mVariableId; } nonneg int *getVarId() const { return &mVarId; } }; public: Tokenizer(); Tokenizer(const Settings * settings, ErrorLogger *errorLogger); ~Tokenizer(); void setTimerResults(TimerResults *tr) { mTimerResults = tr; } /** Is the code C. Used for bailouts */ bool isC() const { return list.isC(); } /** Is the code CPP. Used for bailouts */ bool isCPP() const { return list.isCPP(); } /** * Check if inner scope ends with a call to a noreturn function * \param endScopeToken The '}' token * \param unknown set to true if it's unknown if the scope is noreturn * \return true if scope ends with a function call that might be 'noreturn' */ bool isScopeNoReturn(const Token *endScopeToken, bool *unknown = nullptr) const; bool createTokens(std::istream &code, const std::string& FileName); void createTokens(const simplecpp::TokenList *tokenList); bool simplifyTokens1(const std::string &configuration); /** * Tokenize code * @param code input stream for code, e.g. * \code * #file "p.h" * class Foo * { * private: * void Bar(); * }; * * #endfile * void Foo::Bar() * { * } * \endcode * * @param FileName The filename * @param configuration E.g. "A" for code where "#ifdef A" is true * @return false if source code contains syntax errors */ bool tokenize(std::istream &code, const char FileName[], const std::string &configuration = emptyString); /** Set variable id */ void setVarId(); void setVarIdPass1(); void setVarIdPass2(); /** * Basic simplification of tokenlist * * @param FileName The filename to run; used to do * markup checks. * * @return false if there is an error that requires aborting * the checking of this file. */ bool simplifyTokenList1(const char FileName[]); /** * Most aggressive simplification of tokenlist * * @return false if there is an error that requires aborting * the checking of this file. */ bool simplifyTokenList2(); /** * If --check-headers=no has been given; then remove unneeded code in headers. * - All executable code. * - Unused types/variables/etc */ void simplifyHeaders(); /** * Deletes dead code between 'begin' and 'end'. * In general not everything can be erased, such as: * - code after labels; * - code outside the scope where the function is called; * - code after a change of scope caused by 'switch(...);' * instructions, like 'case %any%;' or 'default;' * Also, if the dead code contains a 'switch' block * and inside it there's a label, the function removes all * the 'switch(..)' tokens and every occurrence of 'case %any%; | default;' * expression, such as the 'switch' block is reduced to a simple block. * * @param begin Tokens after this have a possibility to be erased. * @param end Tokens before this have a possibility to be erased. */ static void eraseDeadCode(Token *begin, const Token *end); /** * Simplify '* & ( %name% ) =' or any combination of '* &' and '()' * parentheses around '%name%' to '%name% =' */ void simplifyMulAndParens(); /** * Calculates sizeof value for given type. * @param type Token which will contain e.g. "int", "*", or string. * @return sizeof for given type, or 0 if it can't be calculated. */ nonneg int sizeOfType(const Token *type) const; /** * Try to determine if function parameter is passed by value by looking * at the function declaration. * @param fpar token for function parameter in the function call * @return true if the parameter is passed by value. if unsure, false is returned */ bool isFunctionParameterPassedByValue(const Token *fpar) const; /** Simplify assignment in function call "f(x=g());" => "x=g();f(x);" */ void simplifyAssignmentInFunctionCall(); /** Simplify assignment where rhs is a block : "x=({123;});" => "{x=123;}" */ void simplifyAssignmentBlock(); /** * Simplify constant calculations such as "1+2" => "3" * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifyCalculations(); /** * Simplify dereferencing a pointer offset by a number: * "*(ptr + num)" => "ptr[num]" * "*(ptr - num)" => "ptr[-num]" */ void simplifyOffsetPointerDereference(); /** * Simplify referencing a pointer offset: * "Replace "&str[num]" => "(str + num)" */ void simplifyOffsetPointerReference(); /** Insert array size where it isn't given */ void arraySize(); /** Simplify labels and 'case|default' syntaxes. */ void simplifyLabelsCaseDefault(); /** simplify case ranges (gcc extension) */ void simplifyCaseRange(); /** Remove macros in global scope */ void removeMacrosInGlobalScope(); void addSemicolonAfterUnknownMacro(); /** Remove undefined macro in class definition: * class DLLEXPORT Fred { }; * class Fred FINAL : Base { }; */ void removeMacroInClassDef(); /** Remove unknown macro in variable declarations: PROGMEM char x; */ void removeMacroInVarDecl(); /** Remove redundant assignment */ void removeRedundantAssignment(); /** Simplifies some realloc usage like * 'x = realloc (0, n);' => 'x = malloc(n);' * 'x = realloc (y, 0);' => 'x = 0; free(y);' */ void simplifyRealloc(); /** Add parentheses for sizeof: sizeof x => sizeof(x) */ void sizeofAddParentheses(); /** * Replace sizeof() to appropriate size. * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifySizeof(); /** * Simplify variable declarations (split up) * \param only_k_r_fpar Only simplify K&R function parameters */ void simplifyVarDecl(const bool only_k_r_fpar); void simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, const bool only_k_r_fpar); /** * Simplify variable initialization * '; int *p(0);' => '; int *p = 0;' */ void simplifyInitVar(); Token * initVar(Token * tok); /** * Simplify easy constant '?:' operation * Example: 0 ? (2/0) : 0 => 0 * @return true if something is modified * false if nothing is done. */ bool simplifyConstTernaryOp(); /** * Simplify compound assignments * Example: ";a+=b;" => ";a=a+b;" */ void simplifyCompoundAssignment(); /** * Simplify the location of "static" and "const" qualifiers in * a variable declaration or definition. * Example: "int static const a;" => "static const a;" * Example: "long long const static b;" => "static const long long b;" */ void simplifyStaticConst(); /** * Simplify assignments in "if" and "while" conditions * Example: "if(a=b);" => "a=b;if(a);" * Example: "while(a=b) { f(a); }" => "a = b; while(a){ f(a); a = b; }" * Example: "do { f(a); } while(a=b);" => "do { f(a); a = b; } while(a);" */ void simplifyIfAndWhileAssign(); /** * Simplify multiple assignments. * Example: "a = b = c = 0;" => "a = 0; b = 0; c = 0;" */ void simplifyVariableMultipleAssign(); /** * Simplify the 'C Alternative Tokens' * Examples: * "if(s and t)" => "if(s && t)" * "while((r bitand s) and not t)" => while((r & s) && !t)" * "a and_eq b;" => "a &= b;" */ bool simplifyCAlternativeTokens(); /** * Simplify comma into a semicolon when possible: * - "delete a, delete b" => "delete a; delete b;" * - "a = 0, b = 0;" => "a = 0; b = 0;" * - "return a(), b;" => "a(); return b;" */ void simplifyComma(); /** Add braces to an if-block, for-block, etc. * @return true if no syntax errors */ bool simplifyAddBraces(); /** Add braces to an if-block, for-block, etc. * for command starting at token including else-block * @return last token of command * or input token in case of an error where no braces are added * or NULL when syntaxError is called */ Token * simplifyAddBracesToCommand(Token * tok); /** Add pair of braces to an single if-block, else-block, for-block, etc. * for command starting at token * @return last token of command * or input token in case of an error where no braces are added * or NULL when syntaxError is called */ Token * simplifyAddBracesPair(Token *tok, bool commandWithCondition); /** * typedef A mytype; * mytype c; * * Becomes: * typedef A mytype; * A c; */ void simplifyTypedef(); /** */ bool isMemberFunction(const Token *openParen) const; /** */ bool simplifyUsing(); /** * Simplify casts */ void simplifyCasts(); /** * Change (multiple) arrays to (multiple) pointers. */ void simplifyUndefinedSizeArray(); /** * A simplify function that replaces a variable with its value in cases * when the value is known. e.g. "x=10; if(x)" => "x=10;if(10)" * * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifyKnownVariables(); /** * Utility function for simplifyKnownVariables. Get data about an * assigned variable. */ static bool simplifyKnownVariablesGetData(nonneg int varid, Token **_tok2, Token **_tok3, std::string &value, nonneg int &valueVarId, bool &valueIsPointer, bool floatvar); /** * utility function for simplifyKnownVariables. Perform simplification * of a given variable */ bool simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, nonneg int varid, const std::string &structname, std::string &value, nonneg int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel) const; /** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/ void simplifyEmptyNamespaces(); /** Simplify redundant code placed after control flow statements : * 'return', 'throw', 'goto', 'break' and 'continue' */ void simplifyFlowControl(); /** Expand nested strcat() calls. */ void simplifyNestedStrcat(); /** Simplify "if else" */ void elseif(); /** Simplify conditions * @return true if something is modified * false if nothing is done. */ bool simplifyConditions(); /** Remove redundant code, e.g. if( false ) { int a; } should be * removed, because it is never executed. * @return true if something is modified * false if nothing is done. */ bool removeRedundantConditions(); /** * Remove redundant for: * "for (x=0;x<1;x++) { }" => "{ x = 1; }" */ void removeRedundantFor(); /** * Reduces "; ;" to ";", except in "( ; ; )" */ void removeRedundantSemicolons(); /** Simplify function calls - constant return value * @return true if something is modified * false if nothing is done. */ bool simplifyFunctionReturn(); /** Struct simplification * "struct S { } s;" => "struct S { }; S s;" */ void simplifyStructDecl(); /** * Remove redundant parentheses: * - "((x))" => "(x)" * - "(function())" => "function()" * - "(delete x)" => "delete x" * - "(delete [] x)" => "delete [] x" * @return true if modifications to token-list are done. * false if no modifications are done. */ bool simplifyRedundantParentheses(); void simplifyCharAt(); /** Simplify references */ void simplifyReference(); /** * Simplify functions like "void f(x) int x; {" * into "void f(int x) {" */ void simplifyFunctionParameters(); /** * Simplify templates */ void simplifyTemplates(); void simplifyDoublePlusAndDoubleMinus(); void simplifyRedundantConsecutiveBraces(); void simplifyArrayAccessSyntax(); void simplifyParameterVoid(); void fillTypeSizes(); void combineOperators(); void combineStringAndCharLiterals(); void concatenateNegativeNumberAndAnyPositive(); void simplifyExternC(); void simplifyRoundCurlyParentheses(); void simplifyTypeIntrinsics(); void simplifySQL(); void checkForEnumsWithTypedef(); void findComplicatedSyntaxErrorsInTemplates(); /** * Simplify e.g. 'atol("0")' into '0' */ void simplifyMathFunctions(); /** * Simplify e.g. 'sin(0)' into '0' */ void simplifyMathExpressions(); /** * Modify strings in the token list by replacing hex and oct * values. E.g. "\x61" -> "a" and "\000" -> "\0" * @param source The string to be modified, e.g. "\x61" * @return Modified string, e.g. "a" */ static std::string simplifyString(const std::string &source); /** * is token pointing at function head? * @param tok A '(' or ')' token in a possible function head * @param endsWith string after function head * @return token matching with endsWith if syntax seems to be a function head else nullptr */ const Token * isFunctionHead(const Token *tok, const std::string &endsWith) const; /** * is token pointing at function head? * @param tok A '(' or ')' token in a possible function head * @param endsWith string after function head * @param cpp c++ code * @return token matching with endsWith if syntax seems to be a function head else nullptr */ static const Token * isFunctionHead(const Token *tok, const std::string &endsWith, bool cpp); private: /** * simplify "while (0)" */ void simplifyWhile0(); /** * Simplify while(func() && errno==EINTR) */ void simplifyErrNoInWhile(); /** * Simplify while(func(f)) */ void simplifyFuncInWhile(); /** * Remove "std::" before some function names */ void simplifyStd(); /** Simplify pointer to standard type (C only) */ void simplifyPointerToStandardType(); /** Simplify function pointers */ void simplifyFunctionPointers(); /** * Send error message to error logger about internal bug. * @param tok the token that this bug concerns. */ void cppcheckError(const Token *tok) const; /** * Setup links for tokens so that one can call Token::link(). */ void createLinks(); /** * Setup links between < and >. */ void createLinks2(); public: /** Syntax error */ void syntaxError(const Token *tok, const std::string &code = "") const; /** Syntax error. Unmatched character. */ void unmatchedToken(const Token *tok) const; /** Syntax error. C++ code in C file. */ void syntaxErrorC(const Token *tok, const std::string &what) const; /** Warn about unknown macro(s), configuration is recommended */ void unknownMacroError(const Token *tok1) const; private: /** Report that there is an unhandled "class x y {" code */ void unhandled_macro_class_x_y(const Token *tok) const; /** Check configuration (unknown macros etc) */ void checkConfiguration() const; void macroWithSemicolonError(const Token *tok, const std::string ¯oName) const; /** * Is there C++ code in C file? */ void validateC() const; /** * assert that tokens are ok - used during debugging for example * to catch problems in simplifyTokenList1/2. */ void validate() const; /** Detect garbage code and call syntaxError() if found. */ void findGarbageCode() const; /** Detect garbage expression */ static bool isGarbageExpr(const Token *start, const Token *end, bool allowSemicolon); /** * Remove __declspec() */ void simplifyDeclspec(); /** * Remove calling convention */ void simplifyCallingConvention(); /** * Remove \__attribute\__ ((?)) */ void simplifyAttribute(); /** * Remove \__cppcheck\__ ((?)) */ void simplifyCppcheckAttribute(); /** * Remove keywords "volatile", "inline", "register", and "restrict" */ void simplifyKeyword(); /** * Remove __asm */ void simplifyAsm(); /** * asm heuristics, Put ^{} statements in asm() */ void simplifyAsm2(); /** * Simplify \@… (compiler extension) */ void simplifyAt(); /** * Simplify bitfields - the field width is removed as we don't use it. */ void simplifyBitfields(); /** * Remove unnecessary member qualification */ void removeUnnecessaryQualification(); /** * Add std:: in front of std classes, when using namespace std; was given */ void simplifyNamespaceStd(); /** * Convert Microsoft memory functions * CopyMemory(dst, src, len) -> memcpy(dst, src, len) * FillMemory(dst, len, val) -> memset(dst, val, len) * MoveMemory(dst, src, len) -> memmove(dst, src, len) * ZeroMemory(dst, len) -> memset(dst, 0, len) */ void simplifyMicrosoftMemoryFunctions(); /** * Convert Microsoft string functions * _tcscpy -> strcpy */ void simplifyMicrosoftStringFunctions(); /** * Remove Borland code */ void simplifyBorland(); /** * Remove Qt signals and slots */ void simplifyQtSignalsSlots(); /** * Collapse operator name tokens into single token * operator = => operator= */ void simplifyOperatorName(); /** * Remove [[attribute]] (C++11 and later) from TokenList */ void simplifyCPPAttribute(); /** * Replace strlen(str) * @return true if any replacement took place, false else * */ bool simplifyStrlen(); /** * Convert namespace aliases */ void simplifyNamespaceAliases(); /** * Convert C++17 style nested namespace to older style */ void simplifyNestedNamespace(); /** * Prepare ternary operators with parentheses so that the AST can be created * */ void prepareTernaryOpForAST(); /** * check for duplicate enum definition */ static bool duplicateDefinition(Token **tokPtr); /** * report error message */ void reportError(const Token* tok, const Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive = false) const; void reportError(const std::list& callstack, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive = false) const; bool duplicateTypedef(Token **tokPtr, const Token *name, const Token *typeDef) const; void unsupportedTypedef(const Token *tok) const; void setVarIdClassDeclaration(const Token * const startToken, const VariableMap &variableMap, const nonneg int scopeStartVarId, std::map >& structMembers); void setVarIdStructMembers(Token **tok1, std::map >& structMembers, nonneg int *varId); void setVarIdClassFunction(const std::string &classname, Token * const startToken, const Token * const endToken, const std::map &varlist, std::map >& structMembers, nonneg int *varId_); /** * Simplify e.g. 'return(strncat(temp,"a",1));' into * strncat(temp,"a",1); return temp; */ void simplifyReturnStrncat(); /** * Output list of unknown types. */ void printUnknownTypes() const; /** Find end of SQL (or PL/SQL) block */ static const Token *findSQLBlockEnd(const Token *tokSQLStart); bool operatorEnd(const Token * tok) const; public: /** Was there templates in the code? */ bool codeWithTemplates() const { return mCodeWithTemplates; } void setSettings(const Settings *settings) { mSettings = settings; list.setSettings(settings); } const SymbolDatabase *getSymbolDatabase() const { return mSymbolDatabase; } void createSymbolDatabase(); void deleteSymbolDatabase(); /** print --debug output if debug flags match the simplification: * 0=unknown/both simplifications * 1=1st simplifications * 2=2nd simplifications */ void printDebugOutput(int simplification) const; void dump(std::ostream &out) const; Token *deleteInvalidTypedef(Token *typeDef); /** * Get variable count. * @return number of variables */ nonneg int varIdCount() const { return mVarId; } /** * Token list: stores all tokens. */ TokenList list; // Implement tokens() as a wrapper for convenience when using the TokenList const Token* tokens() const { return list.front(); } /** * Helper function to check whether number is zero (0 or 0.0 or 0E+0) or not? * @param s the string to check * @return true in case is is zero and false otherwise. */ static bool isZeroNumber(const std::string &s); /** * Helper function to check whether number is one (1 or 0.1E+1 or 1E+0) or not? * @param s the string to check * @return true in case is is one and false otherwise. */ static bool isOneNumber(const std::string &s); /** * Helper function to check whether number is two (2 or 0.2E+1 or 2E+0) or not? * @param s the string to check * @return true in case is is two and false otherwise. */ static bool isTwoNumber(const std::string &s); /** * Helper function to check for start of function execution scope. * Do not use this in checks. Use the symbol database. * @param tok pointer to end parentheses of parameter list * @return pointer to start brace of function scope or nullptr if not start. */ static const Token * startOfExecutableScope(const Token * tok); #ifdef MAXTIME bool isMaxTime() const { return (std::time(0) > mMaxTime); #else static bool isMaxTime() { return false; #endif } const Settings *getSettings() const { return mSettings; } void calculateScopes(); /** Disable copy constructor */ Tokenizer(const Tokenizer &) = delete; /** Disable assignment operator */ Tokenizer &operator=(const Tokenizer &) = delete; private: Token *processFunc(Token *tok2, bool inOperator) const; /** * Get new variable id. * @return new variable id */ nonneg int newVarId() { return ++mVarId; } /** Set pod types */ void setPodTypes(); /** settings */ const Settings * mSettings; /** errorlogger */ ErrorLogger* const mErrorLogger; /** Symbol database that all checks etc can use */ SymbolDatabase *mSymbolDatabase; TemplateSimplifier *mTemplateSimplifier; /** E.g. "A" for code where "#ifdef A" is true. This is used to print additional information in error situations. */ std::string mConfiguration; /** sizeof information for known types */ std::map mTypeSize; /** variable count */ nonneg int mVarId; /** unnamed count "Unnamed0", "Unnamed1", "Unnamed2", ... */ nonneg int mUnnamedCount; /** * was there any templates? templates that are "unused" are * removed from the token list */ bool mCodeWithTemplates; /** * TimerResults */ TimerResults *mTimerResults; #ifdef MAXTIME /** Tokenizer maxtime */ const std::time_t mMaxTime; #endif }; /// @} //--------------------------------------------------------------------------- #endif // tokenizeH cppcheck-1.90/lib/tokenlist.cpp000066400000000000000000001710161357737443600165420ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #include "tokenlist.h" #include "errorlogger.h" #include "mathlib.h" #include "path.h" #include "settings.h" #include "token.h" #include #include #include #include #include // How many compileExpression recursions are allowed? // For practical code this could be endless. But in some special torture test // there needs to be a limit. static const int AST_MAX_DEPTH = 50; TokenList::TokenList(const Settings* settings) : mTokensFrontBack(), mSettings(settings), mIsC(false), mIsCpp(false) { } TokenList::~TokenList() { deallocateTokens(); } //--------------------------------------------------------------------------- const std::string& TokenList::getSourceFilePath() const { if (getFiles().empty()) { return emptyString; } return getFiles()[0]; } //--------------------------------------------------------------------------- // Deallocate lists.. void TokenList::deallocateTokens() { deleteTokens(mTokensFrontBack.front); mTokensFrontBack.front = nullptr; mTokensFrontBack.back = nullptr; mFiles.clear(); } int TokenList::appendFileIfNew(const std::string &fileName) { // Has this file been tokenized already? for (int i = 0; i < mFiles.size(); ++i) if (Path::sameFileName(mFiles[i], fileName)) return i; // The "mFiles" vector remembers what files have been tokenized.. mFiles.push_back(fileName); // Update mIsC and mIsCpp properties if (mFiles.size() == 1) { // Update only useful if first file added to _files if (!mSettings) { mIsC = Path::isC(getSourceFilePath()); mIsCpp = Path::isCPP(getSourceFilePath()); } else { mIsC = mSettings->enforcedLang == Settings::C || (mSettings->enforcedLang == Settings::None && Path::isC(getSourceFilePath())); mIsCpp = mSettings->enforcedLang == Settings::CPP || (mSettings->enforcedLang == Settings::None && Path::isCPP(getSourceFilePath())); } } return mFiles.size() - 1; } void TokenList::deleteTokens(Token *tok) { while (tok) { Token *next = tok->next(); delete tok; tok = next; } } //--------------------------------------------------------------------------- // add a token. //--------------------------------------------------------------------------- void TokenList::addtoken(std::string str, const nonneg int lineno, const nonneg int fileno, bool split) { if (str.empty()) return; // If token contains # characters, split it up if (split) { size_t begin = 0; size_t end = 0; while ((end = str.find("##", begin)) != std::string::npos) { addtoken(str.substr(begin, end - begin), lineno, fileno, false); addtoken("##", lineno, fileno, false); begin = end+2; } if (begin != 0) { addtoken(str.substr(begin), lineno, fileno, false); return; } } // Replace hexadecimal value with decimal const bool isHex = MathLib::isIntHex(str) ; if (isHex || MathLib::isOct(str) || MathLib::isBin(str)) { // TODO: It would be better if TokenList didn't simplify hexadecimal numbers std::string suffix; if (isHex && str.size() == (2 + mSettings->int_bit / 4) && (str[2] >= '8') && // includes A-F and a-f MathLib::getSuffix(str).empty() ) suffix = "U"; str = MathLib::value(str).str() + suffix; } if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(str); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(str); } if (isCPP() && str == "delete") mTokensFrontBack.back->isKeyword(true); mTokensFrontBack.back->linenr(lineno); mTokensFrontBack.back->fileIndex(fileno); } void TokenList::addtoken(std::string str, const Token *locationTok) { if (str.empty()) return; // Replace hexadecimal value with decimal const bool isHex = MathLib::isIntHex(str) ; if (isHex || MathLib::isOct(str) || MathLib::isBin(str)) { // TODO: It would be better if TokenList didn't simplify hexadecimal numbers std::string suffix; if (isHex && str.size() == (2 + mSettings->int_bit / 4) && (str[2] >= '8') && // includes A-F and a-f MathLib::getSuffix(str).empty() ) suffix = "U"; str = MathLib::value(str).str() + suffix; } if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(str); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(str); } if (isCPP() && str == "delete") mTokensFrontBack.back->isKeyword(true); mTokensFrontBack.back->linenr(locationTok->linenr()); mTokensFrontBack.back->column(locationTok->column()); mTokensFrontBack.back->fileIndex(locationTok->fileIndex()); } void TokenList::addtoken(const Token * tok, const nonneg int lineno, const nonneg int fileno) { if (tok == nullptr) return; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(tok->str()); if (!tok->originalName().empty()) mTokensFrontBack.back->originalName(tok->originalName()); } mTokensFrontBack.back->linenr(lineno); mTokensFrontBack.back->fileIndex(fileno); mTokensFrontBack.back->flags(tok->flags()); } void TokenList::addtoken(const Token *tok, const Token *locationTok) { if (tok == nullptr || locationTok == nullptr) return; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(tok->str()); if (!tok->originalName().empty()) mTokensFrontBack.back->originalName(tok->originalName()); } mTokensFrontBack.back->flags(tok->flags()); mTokensFrontBack.back->linenr(locationTok->linenr()); mTokensFrontBack.back->column(locationTok->column()); mTokensFrontBack.back->fileIndex(locationTok->fileIndex()); } void TokenList::addtoken(const Token *tok) { if (tok == nullptr) return; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(tok->str()); if (!tok->originalName().empty()) mTokensFrontBack.back->originalName(tok->originalName()); } mTokensFrontBack.back->flags(tok->flags()); mTokensFrontBack.back->linenr(tok->linenr()); mTokensFrontBack.back->column(tok->column()); mTokensFrontBack.back->fileIndex(tok->fileIndex()); } //--------------------------------------------------------------------------- // copyTokens - Copy and insert tokens //--------------------------------------------------------------------------- Token *TokenList::copyTokens(Token *dest, const Token *first, const Token *last, bool one_line) { std::stack links; Token *tok2 = dest; int linenr = dest->linenr(); const int commonFileIndex = dest->fileIndex(); for (const Token *tok = first; tok != last->next(); tok = tok->next()) { tok2->insertToken(tok->str()); tok2 = tok2->next(); tok2->fileIndex(commonFileIndex); tok2->linenr(linenr); tok2->tokType(tok->tokType()); tok2->flags(tok->flags()); tok2->varId(tok->varId()); // Check for links and fix them up if (Token::Match(tok2, "(|[|{")) links.push(tok2); else if (Token::Match(tok2, ")|]|}")) { if (links.empty()) return tok2; Token * link = links.top(); tok2->link(link); link->link(tok2); links.pop(); } if (!one_line && tok->next()) linenr += tok->next()->linenr() - tok->linenr(); } return tok2; } //--------------------------------------------------------------------------- // InsertTokens - Copy and insert tokens //--------------------------------------------------------------------------- void TokenList::insertTokens(Token *dest, const Token *src, nonneg int n) { std::stack link; while (n > 0) { dest->insertToken(src->str(), src->originalName()); dest = dest->next(); // Set links if (Token::Match(dest, "(|[|{")) link.push(dest); else if (!link.empty() && Token::Match(dest, ")|]|}")) { Token::createMutualLinks(dest, link.top()); link.pop(); } dest->fileIndex(src->fileIndex()); dest->linenr(src->linenr()); dest->column(src->column()); dest->varId(src->varId()); dest->tokType(src->tokType()); dest->flags(src->flags()); src = src->next(); --n; } } //--------------------------------------------------------------------------- // Tokenize - tokenizes a given file. //--------------------------------------------------------------------------- bool TokenList::createTokens(std::istream &code, const std::string& file0) { appendFileIfNew(file0); simplecpp::OutputList outputList; simplecpp::TokenList tokens(code, mFiles, file0, &outputList); createTokens(&tokens); return outputList.empty(); } //--------------------------------------------------------------------------- void TokenList::createTokens(const simplecpp::TokenList *tokenList) { if (tokenList->cfront()) mOrigFiles = mFiles = tokenList->cfront()->location.files; else mFiles.clear(); mIsC = mIsCpp = false; if (!mFiles.empty()) { mIsC = Path::isC(getSourceFilePath()); mIsCpp = Path::isCPP(getSourceFilePath()); } if (mSettings && mSettings->enforcedLang != Settings::None) { mIsC = (mSettings->enforcedLang == Settings::C); mIsCpp = (mSettings->enforcedLang == Settings::CPP); } for (const simplecpp::Token *tok = tokenList->cfront(); tok; tok = tok->next) { std::string str = tok->str(); // Replace hexadecimal value with decimal // TODO: Remove this const bool isHex = MathLib::isIntHex(str) ; if (isHex || MathLib::isOct(str) || MathLib::isBin(str)) { // TODO: It would be better if TokenList didn't simplify hexadecimal numbers std::string suffix; if (isHex && mSettings && str.size() == (2 + mSettings->int_bit / 4) && (str[2] >= '8') && // includes A-F and a-f MathLib::getSuffix(str).empty() ) suffix = "U"; str = MathLib::value(str).str() + suffix; } // Float literal if (str.size() > 1 && str[0] == '.' && std::isdigit(str[1])) str = '0' + str; if (mTokensFrontBack.back) { mTokensFrontBack.back->insertToken(str); } else { mTokensFrontBack.front = new Token(&mTokensFrontBack); mTokensFrontBack.back = mTokensFrontBack.front; mTokensFrontBack.back->str(str); } if (isCPP() && mTokensFrontBack.back->str() == "delete") mTokensFrontBack.back->isKeyword(true); mTokensFrontBack.back->fileIndex(tok->location.fileIndex); mTokensFrontBack.back->linenr(tok->location.line); mTokensFrontBack.back->column(tok->location.col); mTokensFrontBack.back->isExpandedMacro(!tok->macro.empty()); } if (mSettings && mSettings->relativePaths) { for (std::string & mFile : mFiles) mFile = Path::getRelativePath(mFile, mSettings->basePaths); } Token::assignProgressValues(mTokensFrontBack.front); } //--------------------------------------------------------------------------- unsigned long long TokenList::calculateChecksum() const { unsigned long long checksum = 0; for (const Token* tok = front(); tok; tok = tok->next()) { const unsigned int subchecksum1 = tok->flags() + tok->varId() + tok->tokType(); unsigned int subchecksum2 = 0; for (char i : tok->str()) subchecksum2 += (unsigned int)i; if (!tok->originalName().empty()) { for (char i : tok->originalName()) subchecksum2 += (unsigned int) i; } checksum ^= ((static_cast(subchecksum1) << 32) | subchecksum2); const bool bit1 = (checksum & 1) != 0; checksum >>= 1; if (bit1) checksum |= (1ULL << 63); } return checksum; } //--------------------------------------------------------------------------- struct AST_state { std::stack op; int depth; int inArrayAssignment; bool cpp; int assign; bool inCase; // true from case to : const Token *functionCallEndPar; explicit AST_state(bool cpp) : depth(0), inArrayAssignment(0), cpp(cpp), assign(0), inCase(false), functionCallEndPar(nullptr) {} }; static Token * skipDecl(Token *tok) { if (!Token::Match(tok->previous(), "( %name%")) return tok; Token *vartok = tok; while (Token::Match(vartok, "%name%|*|&|::|<")) { if (vartok->str() == "<") { if (vartok->link()) vartok = vartok->link(); else return tok; } else if (Token::Match(vartok, "%name% [:=]")) { return vartok; } vartok = vartok->next(); } return tok; } static bool iscast(const Token *tok) { if (!Token::Match(tok, "( ::| %name%")) return false; if (Token::simpleMatch(tok->link(), ") ( )")) return false; if (tok->previous() && tok->previous()->isName() && tok->previous()->str() != "return") return false; if (Token::simpleMatch(tok->previous(), ">") && tok->previous()->link()) return false; if (Token::Match(tok, "( (| typeof (") && Token::Match(tok->link(), ") %num%")) return true; if (Token::Match(tok->link(), ") }|)|]|;")) return false; if (Token::Match(tok->link(), ") %cop%") && !Token::Match(tok->link(), ") [&*+-~]")) return false; if (Token::Match(tok->previous(), "= ( %name% ) {") && tok->next()->varId() == 0) return true; bool type = false; for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId() != 0) return false; while (tok2->link() && Token::Match(tok2, "(|[|<")) tok2 = tok2->link()->next(); if (tok2->str() == ")") { if (Token::simpleMatch(tok2, ") (") && Token::simpleMatch(tok2->linkAt(1), ") .")) return true; return type || tok2->strAt(-1) == "*" || Token::simpleMatch(tok2, ") ~") || (Token::Match(tok2, ") %any%") && !tok2->next()->isOp() && !Token::Match(tok2->next(), "[[]);,?:.]")); } if (!Token::Match(tok2, "%name%|*|&|::")) return false; if (tok2->isStandardType() && (tok2->next()->str() != "(" || Token::Match(tok2->next(), "( * *| )"))) type = true; } return false; } static Token* findTypeEnd(Token* tok) { while (Token::Match(tok, "%name%|.|::|*|&|<|(|template|decltype|sizeof")) { if (Token::Match(tok, "(|<")) tok = tok->link(); if (!tok) return nullptr; tok = tok->next(); } return tok; } static const Token* findTypeEnd(const Token* tok) { return findTypeEnd(const_cast(tok)); } static const Token * findLambdaEndScope(const Token *tok) { if (!Token::simpleMatch(tok, "[")) return nullptr; tok = tok->link(); if (!Token::Match(tok, "] (|{")) return nullptr; tok = tok->linkAt(1); if (Token::simpleMatch(tok, "}")) return tok; if (Token::simpleMatch(tok, ") {")) return tok->linkAt(1); if (!Token::simpleMatch(tok, ")")) return nullptr; tok = tok->next(); while (Token::Match(tok, "mutable|constexpr|constval|noexcept|.")) { if (Token::simpleMatch(tok, "noexcept (")) tok = tok->linkAt(1); if (Token::simpleMatch(tok, ".")) { tok = findTypeEnd(tok); break; } tok = tok->next(); } if (Token::simpleMatch(tok, "{")) return tok->link(); return nullptr; } // int(1), int*(2), .. static Token * findCppTypeInitPar(Token *tok) { if (!tok || !Token::Match(tok->previous(), "[,()] %name%")) return nullptr; bool istype = false; while (Token::Match(tok, "%name%|::|<")) { if (tok->str() == "<") { tok = tok->link(); if (!tok) return nullptr; } istype |= tok->isStandardType(); tok = tok->next(); } if (!istype) return nullptr; if (!Token::Match(tok, "[*&]")) return nullptr; while (Token::Match(tok, "[*&]")) tok = tok->next(); return (tok && tok->str() == "(") ? tok : nullptr; } // X{} X{} etc static bool iscpp11init_impl(const Token * const tok); static bool iscpp11init(const Token * const tok) { if (tok->isCpp11init() == TokenImpl::Cpp11init::UNKNOWN) tok->setCpp11init(iscpp11init_impl(tok)); return tok->isCpp11init() == TokenImpl::Cpp11init::CPP11INIT; } static bool iscpp11init_impl(const Token * const tok) { if (Token::simpleMatch(tok, "{") && Token::simpleMatch(tok->link()->previous(), "; }")) return false; const Token *nameToken = tok; while (nameToken && nameToken->str() == "{") { if (nameToken->isCpp11init() != TokenImpl::Cpp11init::UNKNOWN) return nameToken->isCpp11init() == TokenImpl::Cpp11init::CPP11INIT; nameToken = nameToken->previous(); if (nameToken && nameToken->str() == "," && Token::simpleMatch(nameToken->previous(), "} ,")) nameToken = nameToken->linkAt(-1); } if (!nameToken) return false; if (nameToken->str() == ">" && nameToken->link()) nameToken = nameToken->link()->previous(); const Token *endtok = nullptr; if (Token::Match(nameToken, "%name%|return {") && (!Token::simpleMatch(nameToken->tokAt(2), "[") || findLambdaEndScope(nameToken->tokAt(2)))) endtok = nameToken->linkAt(1); else if (Token::Match(nameToken,"%name% <") && Token::simpleMatch(nameToken->linkAt(1),"> {")) endtok = nameToken->linkAt(1)->linkAt(1); else if (Token::Match(nameToken->previous(), "%name% ( {")) endtok = nameToken->linkAt(1); else return false; if (Token::Match(nameToken, "else|try|do|const|override|volatile|&|&&")) return false; if (Token::simpleMatch(nameToken->previous(), "namespace")) return false; if (Token::Match(nameToken, "%any% {")) { // If there is semicolon between {..} this is not a initlist for (const Token *tok2 = nameToken->next(); tok2 != endtok; tok2 = tok2->next()) { if (tok2->str() == ";") return false; const Token * lambdaEnd = findLambdaEndScope(tok2); if (lambdaEnd) tok2 = lambdaEnd; } } // There is no initialisation for example here: 'class Fred {};' if (!Token::simpleMatch(endtok, "} ;")) return true; const Token *prev = nameToken; while (Token::Match(prev, "%name%|::|:|<|>")) { if (Token::Match(prev, "class|struct")) return false; prev = prev->previous(); } return true; } static bool isQualifier(const Token* tok) { while (Token::Match(tok, "&|&&|*")) tok = tok->next(); if (!Token::Match(tok, "{|;")) return false; return true; } static void compileUnaryOp(Token *&tok, AST_state& state, void(*f)(Token *&tok, AST_state& state)) { Token *unaryop = tok; if (f) { tok = tok->next(); state.depth++; if (tok && state.depth <= AST_MAX_DEPTH) f(tok, state); state.depth--; } if (!state.op.empty()) { unaryop->astOperand1(state.op.top()); state.op.pop(); } state.op.push(unaryop); } static void compileBinOp(Token *&tok, AST_state& state, void(*f)(Token *&tok, AST_state& state)) { Token *binop = tok; if (f) { tok = tok->next(); state.depth++; if (tok && state.depth <= AST_MAX_DEPTH) f(tok, state); state.depth--; } // TODO: Should we check if op is empty. // * Is it better to add assertion that it isn't? // * Write debug warning if it's empty? if (!state.op.empty()) { binop->astOperand2(state.op.top()); state.op.pop(); } if (!state.op.empty()) { binop->astOperand1(state.op.top()); state.op.pop(); } state.op.push(binop); } static void compileExpression(Token *&tok, AST_state& state); static void compileTerm(Token *&tok, AST_state& state) { if (!tok) return; if (Token::Match(tok, "L %str%|%char%")) tok = tok->next(); if (state.inArrayAssignment && Token::Match(tok->previous(), "[{,] . %name%")) { // Jump over . in C style struct initialization state.op.push(tok); tok->astOperand1(tok->next()); tok = tok->tokAt(2); } if (state.inArrayAssignment && Token::Match(tok->previous(), "[{,] [ %num%|%name% ]")) { state.op.push(tok); tok->astOperand1(tok->next()); tok = tok->tokAt(3); } if (tok->isLiteral()) { state.op.push(tok); do { tok = tok->next(); } while (Token::Match(tok, "%name%|%str%")); } else if (tok->isName()) { if (Token::Match(tok, "return|case") || (state.cpp && tok->str() == "throw")) { if (tok->str() == "case") state.inCase = true; compileUnaryOp(tok, state, compileExpression); state.op.pop(); if (state.inCase && Token::simpleMatch(tok, ": ;")) { state.inCase = false; tok = tok->next(); } } else if (Token::Match(tok, "sizeof !!(")) { compileUnaryOp(tok, state, compileExpression); state.op.pop(); } else if (state.cpp && findCppTypeInitPar(tok)) { // int(0), int*(123), .. tok = findCppTypeInitPar(tok); state.op.push(tok); tok = tok->tokAt(2); } else if (state.cpp && iscpp11init(tok)) { // X{} X{} etc state.op.push(tok); tok = tok->next(); if (tok->str() == "<") tok = tok->link()->next(); if (Token::Match(tok, "{ . %name% =")) { const int inArrayAssignment = state.inArrayAssignment; state.inArrayAssignment = 1; compileBinOp(tok, state, compileExpression); state.inArrayAssignment = inArrayAssignment; } } else if (!state.cpp || !Token::Match(tok, "new|delete %name%|*|&|::|(|[")) { tok = skipDecl(tok); while (tok->next() && tok->next()->isName()) tok = tok->next(); state.op.push(tok); if (Token::Match(tok, "%name% <") && tok->linkAt(1)) tok = tok->linkAt(1); else if (Token::Match(tok, "%name% ...")) tok = tok->next(); tok = tok->next(); if (Token::Match(tok, "%str%")) { while (Token::Match(tok, "%name%|%str%")) tok = tok->next(); } } } else if (tok->str() == "{") { const Token *prev = tok->previous(); if (Token::simpleMatch(prev, ") {") && iscast(prev->link())) prev = prev->link()->previous(); if (Token::simpleMatch(tok->link(),"} [")) { tok = tok->next(); } else if (state.cpp && iscpp11init(tok)) { if (state.op.empty() || Token::Match(tok->previous(), "[{,]") || Token::Match(tok->tokAt(-2), "%name% (")) compileUnaryOp(tok, state, compileExpression); else compileBinOp(tok, state, compileExpression); if (Token::Match(tok, "} ,|:")) tok = tok->next(); } else if (state.cpp && Token::Match(tok->tokAt(-2), "%name% ( {") && !Token::findsimplematch(tok, ";", tok->link())) { if (Token::simpleMatch(tok, "{ }")) tok = tok->tokAt(2); else { Token *tok1 = tok; state.inArrayAssignment++; compileUnaryOp(tok, state, compileExpression); state.inArrayAssignment--; tok = tok1->link()->next(); } } else if (!state.inArrayAssignment && !Token::simpleMatch(prev, "=")) { state.op.push(tok); tok = tok->link()->next(); } else { if (tok->link() != tok->next()) { state.inArrayAssignment++; compileUnaryOp(tok, state, compileExpression); while (Token::Match(tok, "} [,};]") && state.inArrayAssignment > 0) { tok = tok->next(); state.inArrayAssignment--; } } else { state.op.push(tok); tok = tok->tokAt(2); } } } } static void compileScope(Token *&tok, AST_state& state) { compileTerm(tok, state); while (tok) { if (tok->str() == "::") { Token *binop = tok; tok = tok->next(); if (tok && tok->str() == "~") // Jump over ~ of destructor definition tok = tok->next(); if (tok) compileTerm(tok, state); if (binop->previous() && (binop->previous()->isName() || (binop->previous()->link() && binop->strAt(-1) == ">"))) compileBinOp(binop, state, nullptr); else compileUnaryOp(binop, state, nullptr); } else break; } } static bool isPrefixUnary(const Token* tok, bool cpp) { if (!tok->previous() || ((Token::Match(tok->previous(), "(|[|{|%op%|;|}|?|:|,|.|return|::") || (cpp && tok->strAt(-1) == "throw")) && (tok->previous()->tokType() != Token::eIncDecOp || tok->tokType() == Token::eIncDecOp))) return true; if (tok->str() == "*" && tok->previous()->tokType() == Token::eIncDecOp && isPrefixUnary(tok->previous(), cpp)) return true; return tok->strAt(-1) == ")" && iscast(tok->linkAt(-1)); } static void compilePrecedence2(Token *&tok, AST_state& state) { compileScope(tok, state); while (tok) { if (tok->tokType() == Token::eIncDecOp && !isPrefixUnary(tok, state.cpp)) { compileUnaryOp(tok, state, compileScope); } else if (tok->str() == "...") { state.op.push(tok); tok = tok->next(); break; } else if (tok->str() == "." && tok->strAt(1) != "*") { if (tok->strAt(1) == ".") { state.op.push(tok); tok = tok->tokAt(3); break; } else compileBinOp(tok, state, compileScope); } else if (tok->str() == "[") { if (state.cpp && isPrefixUnary(tok, state.cpp) && Token::Match(tok->link(), "] (|{")) { // Lambda // What we do here: // - Nest the round bracket under the square bracket. // - Nest what follows the lambda (if anything) with the lambda opening [ // - Compile the content of the lambda function as separate tree (this is done later) // this must be consistent with isLambdaCaptureList Token* const squareBracket = tok; if (Token::simpleMatch(squareBracket->link(), "] (")) { Token* const roundBracket = squareBracket->link()->next(); Token* curlyBracket = roundBracket->link()->next(); if (Token::Match(curlyBracket, "mutable|const")) curlyBracket = curlyBracket->next(); if (curlyBracket && curlyBracket->originalName() == "->") { curlyBracket = findTypeEnd(curlyBracket->next()); } if (curlyBracket && curlyBracket->str() == "{") { squareBracket->astOperand1(roundBracket); roundBracket->astOperand1(curlyBracket); state.op.push(squareBracket); tok = curlyBracket->link()->next(); continue; } } else { Token* const curlyBracket = squareBracket->link()->next(); squareBracket->astOperand1(curlyBracket); state.op.push(squareBracket); tok = curlyBracket->link()->next(); continue; } } const Token* const tok2 = tok; if (tok->strAt(1) != "]") compileBinOp(tok, state, compileExpression); else compileUnaryOp(tok, state, compileExpression); tok = tok2->link()->next(); } else if (tok->str() == "(" && (!iscast(tok) || Token::Match(tok->previous(), "if|while|for|switch|catch"))) { Token* tok2 = tok; tok = tok->next(); const bool opPrevTopSquare = !state.op.empty() && state.op.top() && state.op.top()->str() == "["; const std::size_t oldOpSize = state.op.size(); compileExpression(tok, state); tok = tok2; if ((tok->previous() && tok->previous()->isName() && (!Token::Match(tok->previous(), "return|case") && (!state.cpp || !Token::Match(tok->previous(), "throw|delete")))) || (tok->strAt(-1) == "]" && (!state.cpp || !Token::Match(tok->linkAt(-1)->previous(), "new|delete"))) || (tok->strAt(-1) == ">" && tok->linkAt(-1)) || (tok->strAt(-1) == ")" && !iscast(tok->linkAt(-1))) // Don't treat brackets to clarify precedence as function calls || (tok->strAt(-1) == "}" && opPrevTopSquare)) { const bool operandInside = oldOpSize < state.op.size(); if (operandInside) compileBinOp(tok, state, nullptr); else compileUnaryOp(tok, state, nullptr); } tok = tok->link()->next(); } else if (iscast(tok) && Token::simpleMatch(tok->link(), ") {") && Token::simpleMatch(tok->link()->linkAt(1), "} [")) { Token *cast = tok; tok = tok->link()->next(); Token *tok1 = tok; compileUnaryOp(tok, state, compileExpression); cast->astOperand1(tok1); tok = tok1->link()->next(); } else if (state.cpp && tok->str() == "{" && iscpp11init(tok)) { if (Token::simpleMatch(tok, "{ }")) compileUnaryOp(tok, state, compileExpression); else compileBinOp(tok, state, compileExpression); while (Token::simpleMatch(tok, "}")) tok = tok->next(); } else break; } } static void compilePrecedence3(Token *&tok, AST_state& state) { compilePrecedence2(tok, state); while (tok) { if ((Token::Match(tok, "[+-!~*&]") || tok->tokType() == Token::eIncDecOp) && isPrefixUnary(tok, state.cpp)) { if (Token::Match(tok, "* [*,)]")) { Token* tok2 = tok->next(); while (tok2->next() && tok2->str() == "*") tok2 = tok2->next(); if (Token::Match(tok2, "[>),]")) { tok = tok2; continue; } } compileUnaryOp(tok, state, compilePrecedence3); } else if (tok->str() == "(" && iscast(tok)) { Token* castTok = tok; castTok->isCast(true); tok = tok->link()->next(); compilePrecedence3(tok, state); compileUnaryOp(castTok, state, nullptr); } else if (state.cpp && Token::Match(tok, "new %name%|::|(")) { Token* newtok = tok; tok = tok->next(); bool innertype = false; if (tok->str() == "(") { if (Token::Match(tok, "( &| %name%") && Token::Match(tok->link(), ") ( %type%") && Token::simpleMatch(tok->link()->linkAt(1), ") (")) tok = tok->link()->next(); if (Token::Match(tok->link(), ") ::| %type%")) tok = tok->link()->next(); else if (Token::Match(tok, "( %type%") && Token::Match(tok->link(), ") [();,[]")) { tok = tok->next(); innertype = true; } else if (Token::Match(tok, "( &| %name%") && Token::simpleMatch(tok->link(), ") (")) { tok = tok->next(); innertype = true; } else { /* bad code */ continue; } } state.op.push(tok); while (Token::Match(tok, "%name%|*|&|<|::")) { if (tok->link()) tok = tok->link(); tok = tok->next(); } if (Token::Match(tok, "( const| %type% ) (")) { state.op.push(tok->next()); tok = tok->link()->next(); compileBinOp(tok, state, compilePrecedence2); } else if (tok && (tok->str() == "[" || tok->str() == "(" || tok->str() == "{")) compilePrecedence2(tok, state); else if (innertype && Token::simpleMatch(tok, ") [")) { tok = tok->next(); compilePrecedence2(tok, state); } compileUnaryOp(newtok, state, nullptr); if (innertype && Token::simpleMatch(tok, ") ,")) tok = tok->next(); } else if (state.cpp && Token::Match(tok, "delete %name%|*|&|::|(|[")) { Token* tok2 = tok; tok = tok->next(); if (tok && tok->str() == "[") tok = tok->link()->next(); compilePrecedence3(tok, state); compileUnaryOp(tok2, state, nullptr); } // TODO: Handle sizeof else break; } } static void compilePointerToElem(Token *&tok, AST_state& state) { compilePrecedence3(tok, state); while (tok) { if (Token::simpleMatch(tok, ". *")) { compileBinOp(tok, state, compilePrecedence3); } else break; } } static void compileMulDiv(Token *&tok, AST_state& state) { compilePointerToElem(tok, state); while (tok) { if (Token::Match(tok, "[/%]") || (tok->str() == "*" && !tok->astOperand1() && !isQualifier(tok))) { if (Token::Match(tok, "* [*,)]")) { Token* tok2 = tok->next(); while (tok2->next() && tok2->str() == "*") tok2 = tok2->next(); if (Token::Match(tok2, "[>),]")) { tok = tok2; break; } } compileBinOp(tok, state, compilePointerToElem); } else break; } } static void compileAddSub(Token *&tok, AST_state& state) { compileMulDiv(tok, state); while (tok) { if (Token::Match(tok, "+|-") && !tok->astOperand1()) { compileBinOp(tok, state, compileMulDiv); } else break; } } static void compileShift(Token *&tok, AST_state& state) { compileAddSub(tok, state); while (tok) { if (Token::Match(tok, "<<|>>")) { compileBinOp(tok, state, compileAddSub); } else break; } } static void compileRelComp(Token *&tok, AST_state& state) { compileShift(tok, state); while (tok) { if (Token::Match(tok, "<|<=|>=|>") && !tok->link()) { compileBinOp(tok, state, compileShift); } else break; } } static void compileEqComp(Token *&tok, AST_state& state) { compileRelComp(tok, state); while (tok) { if (Token::Match(tok, "==|!=")) { compileBinOp(tok, state, compileRelComp); } else break; } } static void compileAnd(Token *&tok, AST_state& state) { compileEqComp(tok, state); while (tok) { if (tok->str() == "&" && !tok->astOperand1() && !isQualifier(tok)) { Token* tok2 = tok->next(); if (!tok2) break; if (tok2->str() == "&") tok2 = tok2->next(); if (state.cpp && Token::Match(tok2, ",|)")) { tok = tok2; break; // rValue reference } compileBinOp(tok, state, compileEqComp); } else break; } } static void compileXor(Token *&tok, AST_state& state) { compileAnd(tok, state); while (tok) { if (tok->str() == "^") { compileBinOp(tok, state, compileAnd); } else break; } } static void compileOr(Token *&tok, AST_state& state) { compileXor(tok, state); while (tok) { if (tok->str() == "|") { compileBinOp(tok, state, compileXor); } else break; } } static void compileLogicAnd(Token *&tok, AST_state& state) { compileOr(tok, state); while (tok) { if (tok->str() == "&&" && !isQualifier(tok)) { if (!tok->astOperand1()) { Token* tok2 = tok->next(); if (!tok2) break; if (state.cpp && Token::Match(tok2, ",|)")) { tok = tok2; break; // rValue reference } } compileBinOp(tok, state, compileOr); } else break; } } static void compileLogicOr(Token *&tok, AST_state& state) { compileLogicAnd(tok, state); while (tok) { if (tok->str() == "||") { compileBinOp(tok, state, compileLogicAnd); } else break; } } static void compileAssignTernary(Token *&tok, AST_state& state) { compileLogicOr(tok, state); while (tok) { if (tok->isAssignmentOp()) { state.assign++; compileBinOp(tok, state, compileAssignTernary); if (state.assign > 0) state.assign--; } else if (tok->str() == "?") { // http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator: // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." // Hence, we rely on Tokenizer::prepareTernaryOpForAST() to add such parentheses where necessary. if (tok->strAt(1) == ":") { state.op.push(nullptr); } const int assign = state.assign; state.assign = 0; compileBinOp(tok, state, compileAssignTernary); state.assign = assign; } else if (tok->str() == ":") { if (state.depth == 1U && state.inCase) { state.inCase = false; tok = tok->next(); break; } if (state.assign > 0) break; compileBinOp(tok, state, compileAssignTernary); } else break; } } static void compileComma(Token *&tok, AST_state& state) { compileAssignTernary(tok, state); while (tok) { if (tok->str() == ",") { if (Token::simpleMatch(tok, ", }")) tok = tok->next(); else compileBinOp(tok, state, compileAssignTernary); } else if (tok->str() == ";" && state.functionCallEndPar && tok->index() < state.functionCallEndPar->index()) { compileBinOp(tok, state, compileAssignTernary); } else break; } } static void compileExpression(Token *&tok, AST_state& state) { if (state.depth > AST_MAX_DEPTH) return; // ticket #5592 if (tok) compileComma(tok, state); } static bool isLambdaCaptureList(const Token * tok) { // a lambda expression '[x](y){}' is compiled as: // [ // `-( <<-- optional // `-{ // see compilePrecedence2 if (tok->str() != "[") return false; if (!Token::Match(tok->link(), "] (|{")) return false; if (Token::simpleMatch(tok->astOperand1(), "{") && tok->astOperand1() == tok->link()->next()) return true; if (!tok->astOperand1() || tok->astOperand1()->str() != "(") return false; const Token * params = tok->astOperand1(); if (!params || !params->astOperand1() || params->astOperand1()->str() != "{") return false; return true; } static Token * createAstAtToken(Token *tok, bool cpp); // Compile inner expressions inside inner ({..}) and lambda bodies static void createAstAtTokenInner(Token * const tok1, const Token *endToken, bool cpp) { for (Token *tok = tok1; tok && tok != endToken; tok = tok ? tok->next() : nullptr) { 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(); for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) tok = createAstAtToken(tok, cpp); } else if (cpp && tok->str() == "[") { if (isLambdaCaptureList(tok)) { tok = tok->astOperand1(); if (tok->str() == "(") tok = tok->astOperand1(); const Token * const endToken2 = tok->link(); for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) tok = createAstAtToken(tok, cpp); } else if (Token::simpleMatch(tok->link(), "] (") && Token::Match(tok->link()->linkAt(1), ") .|{")) { Token *bodyStart = tok->link()->linkAt(1)->next(); if (Token::Match(bodyStart, ". %name%") && bodyStart->originalName() == "->") { bodyStart = bodyStart->next(); while (Token::Match(bodyStart, "%name%|::")) bodyStart = bodyStart->next(); if (Token::simpleMatch(bodyStart, "<") && Token::simpleMatch(bodyStart->link(), "> {")) bodyStart = bodyStart->link()->next(); } if (Token::simpleMatch(bodyStart, "{")) { tok = bodyStart; const Token * const endToken2 = tok->link(); for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) tok = createAstAtToken(tok, cpp); } } } } } static Token * findAstTop(Token *tok1, Token *tok2) { for (Token *tok = tok1; tok && (tok != tok2); tok = tok->next()) { if (tok->astParent() || tok->astOperand1() || tok->astOperand2()) { while (tok->astParent() && tok->astParent()->index() >= tok1->index() && tok->astParent()->index() <= tok2->index()) tok = tok->astParent(); return tok; } if (Token::simpleMatch(tok, "( {")) tok = tok->link(); } for (Token *tok = tok1; tok && (tok != tok2); tok = tok->next()) { if (tok->isName() || tok->isNumber()) return tok; if (Token::simpleMatch(tok, "( {")) tok = tok->link(); } return nullptr; } static Token * createAstAtToken(Token *tok, bool cpp) { if (Token::simpleMatch(tok, "for (")) { Token *tok2 = skipDecl(tok->tokAt(2)); Token *init1 = nullptr; Token * const endPar = tok->next()->link(); while (tok2 && tok2 != endPar && tok2->str() != ";") { if (tok2->str() == "<" && tok2->link()) { tok2 = tok2->link(); if (!tok2) break; } else if (Token::Match(tok2, "%name% %op%|(|[|.|:|::") || Token::Match(tok2->previous(), "[(;{}] %cop%|(")) { init1 = tok2; AST_state state1(cpp); compileExpression(tok2, state1); if (Token::Match(tok2, ";|)")) break; init1 = nullptr; } if (!tok2) // #7109 invalid code return nullptr; tok2 = tok2->next(); } if (!tok2 || tok2->str() != ";") { if (tok2 == endPar && init1) { tok->next()->astOperand2(init1); tok->next()->astOperand1(tok); } return tok2; } Token * const init = init1 ? init1 : tok2; Token * const semicolon1 = tok2; tok2 = tok2->next(); AST_state state2(cpp); compileExpression(tok2, state2); Token * const semicolon2 = tok2; if (!semicolon2) return nullptr; // invalid code #7235 tok2 = tok2->next(); AST_state state3(cpp); if (Token::simpleMatch(tok2, "( {")) { state3.op.push(tok2->next()); tok2 = tok2->link()->next(); } compileExpression(tok2, state3); if (init != semicolon1) semicolon1->astOperand1(init->astTop()); tok2 = findAstTop(semicolon1->next(), semicolon2); if (tok2) semicolon2->astOperand1(tok2); tok2 = findAstTop(semicolon2->next(), endPar); if (tok2) semicolon2->astOperand2(tok2); else if (!state3.op.empty()) semicolon2->astOperand2(state3.op.top()); semicolon1->astOperand2(semicolon2); tok->next()->astOperand1(tok); tok->next()->astOperand2(semicolon1); createAstAtTokenInner(endPar->link(), endPar, cpp); return endPar; } if (cpp && Token::Match(tok, "if|switch (")) { Token *semicolon = nullptr; Token *tok2; for (tok2 = tok->tokAt(2); tok2 && tok2->str() != ")"; tok2 = tok2->next()) { if (tok2->str() == ";") { if (semicolon) break; semicolon = tok2; } if (tok2->str() == "(") tok2 = tok2->link(); } if (semicolon && tok2 == tok->linkAt(1)) { tok2 = skipDecl(tok->tokAt(2)); Token *init1 = tok2; AST_state state1(cpp); compileExpression(tok2, state1); tok2 = semicolon->next(); Token *expr1 = tok2; AST_state state2(cpp); compileExpression(tok2, state2); semicolon->astOperand1(findAstTop(init1, semicolon->previous())); semicolon->astOperand2(findAstTop(expr1, tok2)); tok->next()->astOperand1(tok); tok->next()->astOperand2(semicolon); } } if (Token::simpleMatch(tok, "( {")) return tok; if (Token::Match(tok, "%type% <") && tok->linkAt(1) && !Token::Match(tok->linkAt(1), "> [({]")) return tok->linkAt(1); if (Token::Match(tok, "%type% %name%|*|&|::") && tok->str() != "return") { bool decl = false; Token *typetok = tok; while (Token::Match(typetok, "%type%|::|*|&")) { if (typetok->isStandardType() || Token::Match(typetok, "struct|const|static")) decl = true; typetok = typetok->next(); } if (decl && Token::Match(typetok->previous(), "[*&] %var% =")) tok = typetok; } if (Token::Match(tok, "return|case") || (cpp && tok->str() == "throw") || !tok->previous() || Token::Match(tok, "%name% %op%|(|[|.|::|<|?|;") || Token::Match(tok->previous(), "[;{}] %cop%|++|--|( !!{")) { if (cpp && (Token::Match(tok->tokAt(-2), "[;{}] new|delete %name%") || Token::Match(tok->tokAt(-3), "[;{}] :: new|delete %name%"))) tok = tok->previous(); Token * const tok1 = tok; AST_state state(cpp); if (Token::Match(tok, "%name% (")) state.functionCallEndPar = tok->linkAt(1); compileExpression(tok, state); const Token * const endToken = tok; if (endToken == tok1 || !endToken) return tok1; createAstAtTokenInner(tok1->next(), endToken, cpp); return endToken->previous(); } if (cpp && tok->str() == "{" && iscpp11init(tok)) { AST_state state(cpp); compileExpression(tok, state); return tok; } return tok; } void TokenList::createAst() { for (Token *tok = mTokensFrontBack.front; tok; tok = tok ? tok->next() : nullptr) { tok = createAstAtToken(tok, isCPP()); } } void TokenList::validateAst() const { // Check for some known issues in AST to avoid crash/hang later on std::set < const Token* > safeAstTokens; // list of "safe" AST tokens without endless recursion for (const Token *tok = mTokensFrontBack.front; tok; tok = tok->next()) { // Syntax error if binary operator only has 1 operand if ((tok->isAssignmentOp() || tok->isComparisonOp() || Token::Match(tok,"[|^/%]")) && tok->astOperand1() && !tok->astOperand2()) throw InternalError(tok, "Syntax Error: AST broken, binary operator has only one operand.", InternalError::AST); // Syntax error if we encounter "?" with operand2 that is not ":" if (tok->astOperand2() && tok->str() == "?" && tok->astOperand2()->str() != ":") throw InternalError(tok, "Syntax Error: AST broken, ternary operator lacks ':'.", InternalError::AST); // Check for endless recursion const Token* parent = tok->astParent(); if (parent) { std::set < const Token* > astTokens; // list of anchestors astTokens.insert(tok); do { if (safeAstTokens.find(parent) != safeAstTokens.end()) break; if (astTokens.find(parent) != astTokens.end()) throw InternalError(tok, "AST broken: endless recursion from '" + tok->str() + "'", InternalError::AST); astTokens.insert(parent); } while ((parent = parent->astParent()) != nullptr); safeAstTokens.insert(astTokens.begin(), astTokens.end()); } else if (tok->str() == ";") { safeAstTokens.clear(); } else { safeAstTokens.insert(tok); } // Check binary operators if (Token::Match(tok, "%or%|%oror%|%assign%|%comp%")) { // Skip lambda captures if (Token::Match(tok, "= ,|]")) continue; // Don't check templates if (tok->link()) continue; // Skip pure virtual functions if (Token::simpleMatch(tok->previous(), ") = 0")) continue; // Skip operator definitions if (Token::simpleMatch(tok->previous(), "operator")) continue; // Skip incomplete code if (!tok->astOperand1() && !tok->astOperand2() && !tok->astParent()) continue; // Skip lambda assignment and/or initializer if (Token::Match(tok, "= {|^|[")) continue; // FIXME: Workaround broken AST assignment in type aliases if (Token::Match(tok->previous(), "%name% = %name%")) continue; if (!tok->astOperand1() || !tok->astOperand2()) throw InternalError(tok, "Syntax Error: AST broken, binary operator '" + tok->str() + "' doesn't have two operands.", InternalError::AST); } // Check control blocks if (Token::Match(tok->previous(), "if|while|for|switch (")) { if (!tok->astOperand1() || !tok->astOperand2()) throw InternalError(tok, "Syntax Error: AST broken, '" + tok->previous()->str() + "' doesn't have two operands.", InternalError::AST); } } } std::string TokenList::getOrigFile(const Token *tok) const { return mOrigFiles.at(tok->fileIndex()); } const std::string& TokenList::file(const Token *tok) const { return mFiles.at(tok->fileIndex()); } std::string TokenList::fileLine(const Token *tok) const { return ErrorLogger::ErrorMessage::FileLocation(tok, this).stringify(); } bool TokenList::validateToken(const Token* tok) const { if (!tok) return true; for (const Token *t = mTokensFrontBack.front; t; t = t->next()) { if (tok==t) return true; } return false; } void TokenList::simplifyPlatformTypes() { const bool isCPP11 = mSettings->standards.cpp >= Standards::CPP11; enum { isLongLong, isLong, isInt } type; /** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */ if (mSettings->sizeof_size_t == mSettings->sizeof_long) type = isLong; else if (mSettings->sizeof_size_t == mSettings->sizeof_long_long) type = isLongLong; else if (mSettings->sizeof_size_t == mSettings->sizeof_int) type = isInt; else return; for (Token *tok = front(); tok; tok = tok->next()) { // pre-check to reduce unneeded match calls if (!Token::Match(tok, "std| ::| %type%")) continue; bool isUnsigned; if (Token::Match(tok, "std| ::| size_t|uintptr_t|uintmax_t")) { if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=") continue; isUnsigned = true; } else if (Token::Match(tok, "std| ::| ssize_t|ptrdiff_t|intptr_t|intmax_t")) { if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=") continue; isUnsigned = false; } else continue; bool inStd = false; if (tok->str() == "::") { tok->deleteThis(); } else if (tok->str() == "std") { if (tok->next()->str() != "::") continue; inStd = true; tok->deleteNext(); tok->deleteThis(); } if (inStd) tok->originalName("std::" + tok->str()); else tok->originalName(tok->str()); if (isUnsigned) tok->isUnsigned(true); switch (type) { case isLongLong: tok->isLong(true); tok->str("long"); break; case isLong: tok->str("long"); break; case isInt: tok->str("int"); break; } } const std::string platform_type(mSettings->platformString()); for (Token *tok = front(); tok; tok = tok->next()) { if (tok->tokType() != Token::eType && tok->tokType() != Token::eName) continue; const Library::PlatformType * const platformtype = mSettings->library.platform_type(tok->str(), platform_type); if (platformtype) { // check for namespace if (tok->strAt(-1) == "::") { const Token * tok1 = tok->tokAt(-2); // skip when non-global namespace defined if (tok1 && tok1->tokType() == Token::eName) continue; tok = tok->previous(); tok->deleteThis(); } Token *typeToken; if (platformtype->mConstPtr) { tok->str("const"); tok->insertToken("*"); tok->insertToken(platformtype->mType); typeToken = tok; } else if (platformtype->mPointer) { tok->str(platformtype->mType); typeToken = tok; tok->insertToken("*"); } else if (platformtype->mPtrPtr) { tok->str(platformtype->mType); typeToken = tok; tok->insertToken("*"); tok->insertToken("*"); } else { tok->originalName(tok->str()); tok->str(platformtype->mType); typeToken = tok; } if (platformtype->mSigned) typeToken->isSigned(true); if (platformtype->mUnsigned) typeToken->isUnsigned(true); if (platformtype->mLong) typeToken->isLong(true); } } } void TokenList::simplifyStdType() { for (Token *tok = front(); tok; tok = tok->next()) { if (Token::Match(tok, "char|short|int|long|unsigned|signed|double|float") || (mSettings->standards.c >= Standards::C99 && Token::Match(tok, "complex|_Complex"))) { bool isFloat= false; bool isSigned = false; bool isUnsigned = false; bool isComplex = false; int countLong = 0; Token* typeSpec = nullptr; Token* tok2 = tok; for (; tok2->next(); tok2 = tok2->next()) { if (tok2->str() == "long") { countLong++; if (!isFloat) typeSpec = tok2; } else if (tok2->str() == "short") { typeSpec = tok2; } else if (tok2->str() == "unsigned") isUnsigned = true; else if (tok2->str() == "signed") isSigned = true; else if (Token::Match(tok2, "float|double")) { isFloat = true; typeSpec = tok2; } else if (mSettings->standards.c >= Standards::C99 && Token::Match(tok2, "complex|_Complex")) isComplex = !isFloat || tok2->str() == "_Complex" || Token::Match(tok2->next(), "*|&|%name%"); // Ensure that "complex" is not the variables name else if (Token::Match(tok2, "char|int")) { if (!typeSpec) typeSpec = tok2; } else break; } if (!typeSpec) { // unsigned i; or similar declaration if (!isComplex) { // Ensure that "complex" is not the variables name tok->str("int"); tok->isSigned(isSigned); tok->isUnsigned(isUnsigned); } } else { typeSpec->isLong(typeSpec->isLong() || (isFloat && countLong == 1) || countLong > 1); typeSpec->isComplex(typeSpec->isComplex() || (isFloat && isComplex)); typeSpec->isSigned(typeSpec->isSigned() || isSigned); typeSpec->isUnsigned(typeSpec->isUnsigned() || isUnsigned); // Remove specifiers const Token* tok3 = tok->previous(); tok2 = tok2->previous(); while (tok3 != tok2) { if (tok2 != typeSpec && (isComplex || !Token::Match(tok2, "complex|_Complex"))) // Ensure that "complex" is not the variables name tok2->deleteThis(); tok2 = tok2->previous(); } } } } } cppcheck-1.90/lib/tokenlist.h000066400000000000000000000141441357737443600162050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef tokenlistH #define tokenlistH //--------------------------------------------------------------------------- #include "config.h" #include "token.h" #include #include class Settings; class Token; namespace simplecpp { class TokenList; } /// @addtogroup Core /// @{ class CPPCHECKLIB TokenList { public: explicit TokenList(const Settings* settings); ~TokenList(); void setSettings(const Settings *settings) { mSettings = settings; } const Settings *getSettings() const { return mSettings; } /** @return the source file path. e.g. "file.cpp" */ const std::string& getSourceFilePath() const; /** Is the code C. Used for bailouts */ bool isC() const { return mIsC; } /** Is the code CPP. Used for bailouts */ bool isCPP() const { return mIsCpp; } /** * Delete all tokens in given token list * @param tok token list to delete */ static void deleteTokens(Token *tok); void addtoken(std::string str, const nonneg int lineno, const nonneg int fileno, bool split = false); void addtoken(std::string str, const Token *locationTok); void addtoken(const Token *tok, const nonneg int lineno, const nonneg int fileno); void addtoken(const Token *tok, const Token *locationTok); void addtoken(const Token *tok); static void insertTokens(Token *dest, const Token *src, nonneg int n); /** * Copy tokens. * @param dest destination token where copied tokens will be inserted after * @param first first token to copy * @param last last token to copy * @param one_line true=>copy all tokens to the same line as dest. false=>copy all tokens to dest while keeping the 'line breaks' * @return new location of last token copied */ static Token *copyTokens(Token *dest, const Token *first, const Token *last, bool one_line = true); /** * Create tokens from code. * The code must be preprocessed first: * - multiline strings are not handled. * - UTF in the code are not handled. * - comments are not handled. * @param code input stream for code * @param file0 source file name */ bool createTokens(std::istream &code, const std::string& file0 = emptyString); void createTokens(const simplecpp::TokenList *tokenList); /** Deallocate list */ void deallocateTokens(); /** append file name if seen the first time; return its index in any case */ int appendFileIfNew(const std::string &fileName); /** get first token of list */ const Token *front() const { return mTokensFrontBack.front; } Token *front() { return mTokensFrontBack.front; } /** get last token of list */ const Token *back() const { return mTokensFrontBack.back; } Token *back() { return mTokensFrontBack.back; } /** * Get filenames (the sourcefile + the files it include). * The first filename is the filename for the sourcefile * @return vector with filenames */ const std::vector& getFiles() const { return mFiles; } std::string getOrigFile(const Token *tok) const; /** * get filename for given token * @param tok The given token * @return filename for the given token */ const std::string& file(const Token *tok) const; /** * Get file:line for a given token * @param tok given token * @return location for given token */ std::string fileLine(const Token *tok) const; /** * Calculates a 64-bit checksum of the token list used to compare * multiple token lists with each other as quickly as possible. */ unsigned long long calculateChecksum() const; /** * Create abstract syntax tree. */ void createAst(); /** * Check abstract syntax tree. * Throws InternalError on failure */ void validateAst() const; /** * Verify that the given token is an element of the tokenlist. * That method is implemented for debugging purposes. * @param[in] tok token to be checked * \return true if token was found in tokenlist, false else. In case of nullptr true is returned. */ bool validateToken(const Token* tok) const; /** * Convert platform dependent types to standard types. * 32 bits: size_t -> unsigned long * 64 bits: size_t -> unsigned long long */ void simplifyPlatformTypes(); /** * Collapse compound standard types into a single token. * unsigned long long int => long _isUnsigned=true,_isLong=true */ void simplifyStdType(); private: /** Disable copy constructor, no implementation */ TokenList(const TokenList &); /** Disable assignment operator, no implementation */ TokenList &operator=(const TokenList &); /** Token list */ TokensFrontBack mTokensFrontBack; /** filenames for the tokenized source code (source + included) */ std::vector mFiles; /** Original filenames for the tokenized source code (source + included) */ std::vector mOrigFiles; /** settings */ const Settings* mSettings; /** File is known to be C/C++ code */ bool mIsC, mIsCpp; }; /// @} //--------------------------------------------------------------------------- #endif // tokenlistH cppcheck-1.90/lib/utils.h000066400000000000000000000072211357737443600153270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef utilsH #define utilsH //--------------------------------------------------------------------------- #include #include #include #include inline bool endsWith(const std::string &str, char c) { return str[str.size()-1U] == c; } inline bool endsWith(const std::string &str, const char end[], std::size_t endlen) { return (str.size() >= endlen) && (str.compare(str.size()-endlen, endlen, end)==0); } inline static bool isPrefixStringCharLiteral(const std::string &str, char q, const std::string& p) { if (!endsWith(str, q)) return false; if ((str.length() + 1) > p.length() && (str.compare(0, p.size() + 1, p + q) == 0)) return true; return false; } inline static bool isStringCharLiteral(const std::string &str, char q) { for (const std::string & p: { "", "u8", "u", "U", "L" }) { if (isPrefixStringCharLiteral(str, q, p)) return true; } return false; } inline static bool isStringLiteral(const std::string &str) { return isStringCharLiteral(str, '"'); } inline static bool isCharLiteral(const std::string &str) { return isStringCharLiteral(str, '\''); } inline static std::string getStringCharLiteral(const std::string &str, char q) { const std::size_t quotePos = str.find(q); return str.substr(quotePos + 1U, str.size() - quotePos - 2U); } inline static std::string getStringLiteral(const std::string &str) { if (isStringLiteral(str)) return getStringCharLiteral(str, '"'); return ""; } inline static std::string getCharLiteral(const std::string &str) { if (isCharLiteral(str)) return getStringCharLiteral(str, '\''); return ""; } inline static const char *getOrdinalText(int i) { if (i == 1) return "st"; if (i == 2) return "nd"; if (i == 3) return "rd"; return "th"; } inline static int caseInsensitiveStringCompare(const std::string &lhs, const std::string &rhs) { if (lhs.size() != rhs.size()) return (lhs.size() < rhs.size()) ? -1 : 1; for (unsigned int i = 0; i < lhs.size(); ++i) { const int c1 = std::toupper(lhs[i]); const int c2 = std::toupper(rhs[i]); if (c1 != c2) return (c1 < c2) ? -1 : 1; } return 0; } #define UNUSED(x) (void)(x) // Use the nonneg macro when you want to assert that a variable/argument is not negative #ifdef __CPPCHECK__ #define nonneg __cppcheck_low__(0) #elif defined(NONNEG) // Enable non-negative values checking // TODO : investigate using annotations/contracts for stronger value checking #define nonneg unsigned #else // Disable non-negative values checking #define nonneg #endif #if defined(__has_feature) #if __has_feature(address_sanitizer) #define ASAN 1 #endif #endif #ifndef ASAN #ifdef __SANITIZE_ADDRESS__ #define ASAN 1 #else #define ASAN 0 #endif #endif #endif cppcheck-1.90/lib/valueflow.cpp000066400000000000000000010151031357737443600165250ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * @brief This is the ValueFlow component in Cppcheck. * * Each @sa Token in the token list has a list of values. These are * the "possible" values for the Token at runtime. * * In the --debug and --debug-normal output you can see the ValueFlow data. For example: * * int f() * { * int x = 10; * return 4 * x + 2; * } * * The --debug-normal output says: * * ##Value flow * Line 3 * 10 always 10 * Line 4 * 4 always 4 * * always 40 * x always 10 * + always 42 * 2 always 2 * * All value flow analysis is executed in the ValueFlow::setValues() function. The ValueFlow analysis is executed after * the tokenizer/ast/symboldatabase/etc.. The ValueFlow analysis is done in a series of valueFlow* function calls, where * each such function call can only use results from previous function calls. The function calls should be arranged so * that valueFlow* that do not require previous ValueFlow information should be first. * * Type of analysis * ================ * * This is "flow sensitive" value flow analysis. We _usually_ track the value for 1 variable at a time. * * How are calculations handled * ============================ * * Here is an example code: * * x = 3 + 4; * * The valueFlowNumber set the values for the "3" and "4" tokens by calling setTokenValue(). * The setTokenValue() handle the calculations automatically. When both "3" and "4" have values, the "+" can be * calculated. setTokenValue() recursively calls itself when parents in calculations can be calculated. * * Forward / Reverse flow analysis * =============================== * * In forward value flow analysis we know a value and see what happens when we are stepping the program forward. Like * normal execution. The valueFlowForwardVariable is used in this analysis. * * In reverse value flow analysis we know the value of a variable at line X. And try to "execute backwards" to determine * possible values before line X. The valueFlowReverse is used in this analysis. * * */ #include "valueflow.h" #include "astutils.h" #include "errorlogger.h" #include "library.h" #include "mathlib.h" #include "platform.h" #include "programmemory.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" #include "tokenlist.h" #include "utils.h" #include "path.h" #include #include #include #include #include #include #include #include #include #include static const int TIMEOUT = 10; // Do not repeat ValueFlow analysis more than 10 seconds static void bailoutInternal(TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what, const std::string &file, int line, const std::string &function) { std::list callstack(1, ErrorLogger::ErrorMessage::FileLocation(tok, tokenlist)); ErrorLogger::ErrorMessage errmsg(callstack, tokenlist->getSourceFilePath(), Severity::debug, Path::stripDirectoryPart(file) + ":" + MathLib::toString(line) + ":" + function + " bailout: " + what, "valueFlowBailout", false); errorLogger->reportErr(errmsg); } #if (defined __cplusplus) && __cplusplus >= 201103L #define bailout(tokenlist, errorLogger, tok, what) bailoutInternal(tokenlist, errorLogger, tok, what, __FILE__, __LINE__, __func__) #elif (defined __GNUC__) || (defined __clang__) || (defined _MSC_VER) #define bailout(tokenlist, errorLogger, tok, what) bailoutInternal(tokenlist, errorLogger, tok, what, __FILE__, __LINE__, __FUNCTION__) #else #define bailout(tokenlist, errorLogger, tok, what) bailoutInternal(tokenlist, errorLogger, tok, what, __FILE__, __LINE__, "(valueFlow)") #endif static void changeKnownToPossible(std::list &values, int indirect=-1) { for (ValueFlow::Value& v: values) { if (indirect >= 0 && v.indirect != indirect) continue; v.changeKnownToPossible(); } } static void removeImpossible(std::list& values, int indirect = -1) { values.remove_if([&](const ValueFlow::Value& v) { if (indirect >= 0 && v.indirect != indirect) return false; return v.isImpossible(); }); } static void lowerToPossible(std::list& values, int indirect = -1) { changeKnownToPossible(values, indirect); removeImpossible(values, indirect); } static void lowerToInconclusive(std::list& values, const Settings* settings, int indirect = -1) { if (settings->inconclusive) { removeImpossible(values, indirect); for (ValueFlow::Value& v : values) { if (indirect >= 0 && v.indirect != indirect) continue; v.setInconclusive(); } } else { // Remove all values if the inconclusive flags is not set values.remove_if([&](const ValueFlow::Value& v) { if (indirect >= 0 && v.indirect != indirect) return false; return true; }); } } static void changePossibleToKnown(std::list& values, int indirect = -1) { for (ValueFlow::Value& v : values) { if (indirect >= 0 && v.indirect != indirect) continue; if (!v.isPossible()) continue; if (v.bound != ValueFlow::Value::Bound::Point) continue; v.setKnown(); } } static void setValueUpperBound(ValueFlow::Value& value, bool upper) { if (upper) value.bound = ValueFlow::Value::Bound::Upper; else value.bound = ValueFlow::Value::Bound::Lower; } static void setValueBound(ValueFlow::Value& value, const Token* tok, bool invert) { if (Token::Match(tok, "<|<=")) { setValueUpperBound(value, !invert); } else if (Token::Match(tok, ">|>=")) { setValueUpperBound(value, invert); } } static void setConditionalValues(const Token *tok, bool invert, MathLib::bigint value, ValueFlow::Value &true_value, ValueFlow::Value &false_value) { if (Token::Match(tok, "==|!=|>=|<=")) { true_value = ValueFlow::Value{tok, value}; const char* greaterThan = ">="; const char* lessThan = "<="; if (invert) std::swap(greaterThan, lessThan); if (Token::simpleMatch(tok, greaterThan)) { false_value = ValueFlow::Value{tok, value - 1}; } else if (Token::simpleMatch(tok, lessThan)) { false_value = ValueFlow::Value{tok, value + 1}; } else { false_value = ValueFlow::Value{tok, value}; } } else { const char* greaterThan = ">"; const char* lessThan = "<"; if (invert) std::swap(greaterThan, lessThan); if (Token::simpleMatch(tok, greaterThan)) { true_value = ValueFlow::Value{tok, value + 1}; false_value = ValueFlow::Value{tok, value}; } else if (Token::simpleMatch(tok, lessThan)) { true_value = ValueFlow::Value{tok, value - 1}; false_value = ValueFlow::Value{tok, value}; } } setValueBound(true_value, tok, invert); setValueBound(false_value, tok, !invert); } static bool isSaturated(MathLib::bigint value) { return value == std::numeric_limits::max() || value == std::numeric_limits::min(); } const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value) { if (!tok->astOperand1() || !tok->astOperand2()) return nullptr; if (Token::Match(tok, "%comp%")) { if (tok->astOperand1()->hasKnownIntValue()) { MathLib::bigint value = tok->astOperand1()->values().front().intvalue; if (isSaturated(value)) return nullptr; setConditionalValues(tok, true, value, true_value, false_value); return tok->astOperand2(); } else if (tok->astOperand2()->hasKnownIntValue()) { MathLib::bigint value = tok->astOperand2()->values().front().intvalue; if (isSaturated(value)) return nullptr; setConditionalValues(tok, false, value, true_value, false_value); return tok->astOperand1(); } } return nullptr; } /** * Should value be skipped because it's hidden inside && || or ?: expression. * Example: ((x!=NULL) && (*x == 123)) * If 'valuetok' points at the x in '(*x == 123)'. Then the '&&' will be returned. * @param valuetok original variable token * @return NULL=>don't skip, non-NULL=>The operator token that cause the skip. For instance the '&&'. * */ static const Token * skipValueInConditionalExpression(const Token * const valuetok) { // Walk up the ast const Token *prev = valuetok; for (const Token *tok = valuetok->astParent(); tok; tok = tok->astParent()) { const bool prevIsLhs = (prev == tok->astOperand1()); prev = tok; if (prevIsLhs || !Token::Match(tok, "%oror%|&&|?|:")) continue; if (tok->hasKnownIntValue()) return tok; // Is variable protected in LHS.. bool bailout = false; visitAstNodes(tok->astOperand1(), [&](const Token *tok2) { if (tok2->str() == ".") return ChildrenToVisit::none; // A variable is seen.. if (tok2 != valuetok && tok2->variable() && (tok2->varId() == valuetok->varId() || (!tok2->variable()->isArgument() && !tok2->hasKnownIntValue()))) { // TODO: limit this bailout bailout = true; return ChildrenToVisit::done; } return ChildrenToVisit::op1_and_op2; }); if (bailout) return tok; } return nullptr; } static bool isEscapeScope(const Token* tok, TokenList * tokenlist, bool unknown = false) { if (!Token::simpleMatch(tok, "{")) return false; // TODO this search for termTok in all subscopes. It should check the end of the scope. const Token * termTok = Token::findmatch(tok, "return|continue|break|throw|goto", tok->link()); if (termTok && termTok->scope() == tok->scope()) return true; std::string unknownFunction; if (tokenlist && tokenlist->getSettings()->library.isScopeNoReturn(tok->link(), &unknownFunction)) return unknownFunction.empty() || unknown; return false; } static bool bailoutSelfAssignment(const Token * const tok) { const Token *parent = tok; while (parent) { const Token *op = parent; parent = parent->astParent(); // Assignment where lhs variable exists in rhs => return true if (parent != nullptr && parent->astOperand2() == op && parent->astOperand1() != nullptr && parent->str() == "=") { for (const Token *lhs = parent->astOperand1(); lhs; lhs = lhs->astOperand1()) { if (lhs->varId() == tok->varId()) return true; if (lhs->astOperand2() && lhs->astOperand2()->varId() == tok->varId()) return true; } } } return false; } static ValueFlow::Value castValue(ValueFlow::Value value, const ValueType::Sign sign, nonneg int bit) { if (value.isFloatValue()) { value.valueType = ValueFlow::Value::INT; if (value.floatValue >= std::numeric_limits::min() && value.floatValue <= std::numeric_limits::max()) { value.intvalue = value.floatValue; } else { // don't perform UB value.intvalue = 0; } } if (bit < MathLib::bigint_bits) { const MathLib::biguint one = 1; value.intvalue &= (one << bit) - 1; if (sign == ValueType::Sign::SIGNED && value.intvalue & (one << (bit - 1))) { value.intvalue |= ~((one << bit) - 1ULL); } } return value; } static void combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value *result) { if (value1.isKnown() && value2.isKnown()) result->setKnown(); else if (value1.isImpossible() || value2.isImpossible()) result->setImpossible(); else if (value1.isInconclusive() || value2.isInconclusive()) result->setInconclusive(); else result->setPossible(); result->condition = value1.condition ? value1.condition : value2.condition; result->varId = (value1.varId != 0U) ? value1.varId : value2.varId; result->varvalue = (result->varId == value1.varId) ? value1.varvalue : value2.varvalue; result->errorPath = (value1.errorPath.empty() ? value2 : value1).errorPath; result->safe = value1.safe || value2.safe; } static const Token *getCastTypeStartToken(const Token *parent) { // TODO: This might be a generic utility function? if (!parent || parent->str() != "(") return nullptr; if (!parent->astOperand2() && Token::Match(parent,"( %name%")) return parent->next(); if (parent->astOperand2() && Token::Match(parent->astOperand1(), "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) return parent->astOperand1()->tokAt(2); return nullptr; } /** Set token value for cast */ static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings); /** set ValueFlow value and perform calculations if possible */ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Settings *settings) { if (!tok->addValue(value)) return; Token *parent = tok->astParent(); if (!parent) return; if (value.isContainerSizeValue()) { // .empty, .size, +"abc", +'a' if (parent->str() == "+") { for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { ValueFlow::Value result; result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; if (value1.isContainerSizeValue() && value2.isContainerSizeValue()) result.intvalue = value1.intvalue + value2.intvalue; else if (value1.isContainerSizeValue() && value2.isTokValue() && value2.tokvalue->tokType() == Token::eString) result.intvalue = value1.intvalue + Token::getStrLength(value2.tokvalue); else if (value2.isContainerSizeValue() && value1.isTokValue() && value1.tokvalue->tokType() == Token::eString) result.intvalue = Token::getStrLength(value1.tokvalue) + value2.intvalue; else continue; combineValueProperties(value1, value2, &result); setTokenValue(parent, result, settings); } } } else if (Token::Match(parent, ". %name% (") && parent->astParent() == parent->tokAt(2) && parent->astOperand1() && parent->astOperand1()->valueType()) { const Library::Container *c = parent->astOperand1()->valueType()->container; const Library::Container::Yield yields = c ? c->getYield(parent->strAt(1)) : Library::Container::Yield::NO_YIELD; if (yields == Library::Container::Yield::SIZE) { ValueFlow::Value v(value); v.valueType = ValueFlow::Value::ValueType::INT; setTokenValue(parent->astParent(), v, settings); } else if (yields == Library::Container::Yield::EMPTY) { ValueFlow::Value v(value); v.intvalue = !v.intvalue; v.valueType = ValueFlow::Value::ValueType::INT; setTokenValue(parent->astParent(), v, settings); } } return; } if (value.isLifetimeValue()) { if (!isLifetimeBorrowed(parent, settings)) return; if (value.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator && astIsIterator(parent)) { setTokenValue(parent,value,settings); } else if (astIsPointer(tok) && astIsPointer(parent) && (parent->isArithmeticalOp() || Token::Match(parent, "( %type%"))) { setTokenValue(parent,value,settings); } return; } if (value.isUninitValue()) { ValueFlow::Value pvalue = value; if (parent->isUnaryOp("&")) { pvalue.indirect++; setTokenValue(parent, pvalue, settings); } else if (Token::Match(parent, ". %var%") && parent->astOperand1() == tok) { if (parent->originalName() == "->" && pvalue.indirect > 0) pvalue.indirect--; setTokenValue(parent->astOperand2(), pvalue, settings); } else if (Token::Match(parent->astParent(), ". %var%") && parent->astParent()->astOperand1() == parent) { if (parent->astParent()->originalName() == "->" && pvalue.indirect > 0) pvalue.indirect--; setTokenValue(parent->astParent()->astOperand2(), pvalue, settings); } else if (parent->isUnaryOp("*") && pvalue.indirect > 0) { pvalue.indirect--; setTokenValue(parent, pvalue, settings); } return; } // cast.. if (const Token *castType = getCastTypeStartToken(parent)) { if (astIsPointer(tok) && value.valueType == ValueFlow::Value::INT && Token::simpleMatch(parent->astOperand1(), "dynamic_cast")) return; const ValueType &valueType = ValueType::parseDecl(castType, settings); setTokenValueCast(parent, valueType, value, settings); } else if (parent->str() == ":") { setTokenValue(parent,value,settings); } else if (parent->str() == "?" && tok->str() == ":" && tok == parent->astOperand2() && parent->astOperand1()) { // is condition always true/false? if (parent->astOperand1()->hasKnownValue()) { const ValueFlow::Value &condvalue = parent->astOperand1()->values().front(); const bool cond(condvalue.isTokValue() || (condvalue.isIntValue() && condvalue.intvalue != 0)); if (cond && !tok->astOperand1()) { // true condition, no second operator setTokenValue(parent, condvalue, settings); } else { const Token *op = cond ? tok->astOperand1() : tok->astOperand2(); if (!op) // #7769 segmentation fault at setTokenValue() return; const std::list &values = op->values(); if (std::find(values.begin(), values.end(), value) != values.end()) setTokenValue(parent, value, settings); } } else { // is condition only depending on 1 variable? int varId = 0; bool ret = false; visitAstNodes(parent->astOperand1(), [&](const Token *t) { if (t->varId()) { if (varId > 0 || value.varId != 0U) ret = true; varId = t->varId(); } else if (t->str() == "(" && Token::Match(t->previous(), "%name%")) ret = true; // function call return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (ret) return; ValueFlow::Value v(value); v.conditional = true; v.changeKnownToPossible(); if (varId) v.varId = varId; setTokenValue(parent, v, settings); } } // Calculations.. else if ((parent->isArithmeticalOp() || parent->isComparisonOp() || (parent->tokType() == Token::eBitOp) || (parent->tokType() == Token::eLogicalOp)) && parent->astOperand1() && parent->astOperand2()) { const bool noninvertible = parent->isComparisonOp() || Token::Match(parent, "%|/|&|%or%"); // Skip operators with impossible values that are not invertible if (noninvertible && value.isImpossible()) return; // known result when a operand is 0. if (Token::Match(parent, "[&*]") && value.isKnown() && value.isIntValue() && value.intvalue==0) { setTokenValue(parent, value, settings); return; } // known result when a operand is true. if (Token::simpleMatch(parent, "&&") && value.isKnown() && value.isIntValue() && value.intvalue==0) { setTokenValue(parent, value, settings); return; } // known result when a operand is false. if (Token::simpleMatch(parent, "||") && value.isKnown() && value.isIntValue() && value.intvalue!=0) { setTokenValue(parent, value, settings); return; } for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { if (noninvertible && value1.isImpossible()) continue; if (!value1.isIntValue() && !value1.isFloatValue() && !value1.isTokValue()) continue; if (value1.isTokValue() && (!parent->isComparisonOp() || value1.tokvalue->tokType() != Token::eString)) continue; for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { if (noninvertible && value2.isImpossible()) continue; if (!value2.isIntValue() && !value2.isFloatValue() && !value2.isTokValue()) continue; if (value2.isTokValue() && (!parent->isComparisonOp() || value2.tokvalue->tokType() != Token::eString || value1.isTokValue())) continue; if (value1.isKnown() || value2.isKnown() || value1.varId == 0U || value2.varId == 0U || (value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue())) { ValueFlow::Value result(0); combineValueProperties(value1, value2, &result); const float floatValue1 = value1.isIntValue() ? value1.intvalue : value1.floatValue; const float floatValue2 = value2.isIntValue() ? value2.intvalue : value2.floatValue; switch (parent->str()[0]) { case '+': if (value1.isTokValue() || value2.isTokValue()) break; if (value1.isFloatValue() || value2.isFloatValue()) { result.valueType = ValueFlow::Value::FLOAT; result.floatValue = floatValue1 + floatValue2; } else { result.intvalue = value1.intvalue + value2.intvalue; } setTokenValue(parent, result, settings); break; case '-': if (value1.isTokValue() || value2.isTokValue()) break; if (value1.isFloatValue() || value2.isFloatValue()) { result.valueType = ValueFlow::Value::FLOAT; result.floatValue = floatValue1 - floatValue2; } else { result.intvalue = value1.intvalue - value2.intvalue; } setTokenValue(parent, result, settings); break; case '*': if (value1.isTokValue() || value2.isTokValue()) break; if (value1.isFloatValue() || value2.isFloatValue()) { result.valueType = ValueFlow::Value::FLOAT; result.floatValue = floatValue1 * floatValue2; } else { result.intvalue = value1.intvalue * value2.intvalue; } setTokenValue(parent, result, settings); break; case '/': if (value1.isTokValue() || value2.isTokValue() || value2.intvalue == 0) break; if (value1.isFloatValue() || value2.isFloatValue()) { result.valueType = ValueFlow::Value::FLOAT; result.floatValue = floatValue1 / floatValue2; } else { result.intvalue = value1.intvalue / value2.intvalue; } setTokenValue(parent, result, settings); break; case '%': if (!value1.isIntValue() || !value2.isIntValue()) break; if (value2.intvalue == 0) break; result.intvalue = value1.intvalue % value2.intvalue; setTokenValue(parent, result, settings); break; case '=': if (parent->str() == "==") { if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) { result.intvalue = 0; setTokenValue(parent, result, settings); } else if (value1.isIntValue() && value2.isIntValue()) { result.intvalue = value1.intvalue == value2.intvalue; setTokenValue(parent, result, settings); } } break; case '!': if (parent->str() == "!=") { if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) { result.intvalue = 1; setTokenValue(parent, result, settings); } else if (value1.isIntValue() && value2.isIntValue()) { result.intvalue = value1.intvalue != value2.intvalue; setTokenValue(parent, result, settings); } } break; case '>': { const bool f = value1.isFloatValue() || value2.isFloatValue(); if (!f && !value1.isIntValue() && !value2.isIntValue()) break; if (parent->str() == ">") result.intvalue = f ? (floatValue1 > floatValue2) : (value1.intvalue > value2.intvalue); else if (parent->str() == ">=") result.intvalue = f ? (floatValue1 >= floatValue2) : (value1.intvalue >= value2.intvalue); else if (!f && parent->str() == ">>" && value1.intvalue >= 0 && value2.intvalue >= 0 && value2.intvalue < MathLib::bigint_bits) result.intvalue = value1.intvalue >> value2.intvalue; else break; setTokenValue(parent, result, settings); break; } case '<': { const bool f = value1.isFloatValue() || value2.isFloatValue(); if (!f && !value1.isIntValue() && !value2.isIntValue()) break; if (parent->str() == "<") result.intvalue = f ? (floatValue1 < floatValue2) : (value1.intvalue < value2.intvalue); else if (parent->str() == "<=") result.intvalue = f ? (floatValue1 <= floatValue2) : (value1.intvalue <= value2.intvalue); else if (!f && parent->str() == "<<" && value1.intvalue >= 0 && value2.intvalue >= 0 && value2.intvalue < MathLib::bigint_bits) result.intvalue = value1.intvalue << value2.intvalue; else break; setTokenValue(parent, result, settings); break; } case '&': if (!value1.isIntValue() || !value2.isIntValue()) break; if (parent->str() == "&") result.intvalue = value1.intvalue & value2.intvalue; else result.intvalue = value1.intvalue && value2.intvalue; setTokenValue(parent, result, settings); break; case '|': if (!value1.isIntValue() || !value2.isIntValue()) break; if (parent->str() == "|") result.intvalue = value1.intvalue | value2.intvalue; else result.intvalue = value1.intvalue || value2.intvalue; setTokenValue(parent, result, settings); break; case '^': if (!value1.isIntValue() || !value2.isIntValue()) break; result.intvalue = value1.intvalue ^ value2.intvalue; setTokenValue(parent, result, settings); break; default: // unhandled operator, do nothing break; } } } } } // ! else if (parent->str() == "!") { for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue()) continue; ValueFlow::Value v(val); v.intvalue = !v.intvalue; setTokenValue(parent, v, settings); } } // ~ else if (parent->str() == "~") { for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue()) continue; ValueFlow::Value v(val); v.intvalue = ~v.intvalue; int bits = 0; if (settings && tok->valueType() && tok->valueType()->sign == ValueType::Sign::UNSIGNED && tok->valueType()->pointer == 0) { if (tok->valueType()->type == ValueType::Type::INT) bits = settings->int_bit; else if (tok->valueType()->type == ValueType::Type::LONG) bits = settings->long_bit; } if (bits > 0 && bits < MathLib::bigint_bits) v.intvalue &= (((MathLib::biguint)1)<isUnaryOp("-")) { for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue() && !val.isFloatValue()) continue; ValueFlow::Value v(val); if (v.isIntValue()) v.intvalue = -v.intvalue; else v.floatValue = -v.floatValue; setTokenValue(parent, v, settings); } } // Array element else if (parent->str() == "[" && parent->isBinaryOp()) { for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { if (!value1.isTokValue()) continue; for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { if (!value2.isIntValue()) continue; if (value1.varId == 0U || value2.varId == 0U || (value1.varId == value2.varId && value1.varvalue == value2.varvalue)) { ValueFlow::Value result(0); result.condition = value1.condition ? value1.condition : value2.condition; result.setInconclusive(value1.isInconclusive() | value2.isInconclusive()); result.varId = (value1.varId != 0U) ? value1.varId : value2.varId; result.varvalue = (result.varId == value1.varId) ? value1.intvalue : value2.intvalue; if (value1.valueKind == value2.valueKind) result.valueKind = value1.valueKind; if (value1.tokvalue->tokType() == Token::eString) { const std::string s = value1.tokvalue->strValue(); const MathLib::bigint index = value2.intvalue; if (index == s.size()) { result.intvalue = 0; setTokenValue(parent, result, settings); } else if (index >= 0 && index < s.size()) { result.intvalue = s[index]; setTokenValue(parent, result, settings); } } else if (value1.tokvalue->str() == "{") { MathLib::bigint index = value2.intvalue; const Token *element = value1.tokvalue->next(); while (index > 0 && element->str() != "}") { if (element->str() == ",") --index; if (Token::Match(element, "[{}()[]]")) break; element = element->next(); } if (Token::Match(element, "%num% [,}]")) { result.intvalue = MathLib::toLongNumber(element->str()); setTokenValue(parent, result, settings); } } } } } } else if (Token::Match(parent, ":: %name%") && parent->astOperand2() == tok) { setTokenValue(parent, value, settings); } } static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings *settings) { if (valueType.pointer) setTokenValue(parent,value,settings); else if (valueType.type == ValueType::Type::CHAR) setTokenValue(parent, castValue(value, valueType.sign, settings->char_bit), settings); else if (valueType.type == ValueType::Type::SHORT) setTokenValue(parent, castValue(value, valueType.sign, settings->short_bit), settings); else if (valueType.type == ValueType::Type::INT) setTokenValue(parent, castValue(value, valueType.sign, settings->int_bit), settings); else if (valueType.type == ValueType::Type::LONG) setTokenValue(parent, castValue(value, valueType.sign, settings->long_bit), settings); else if (valueType.type == ValueType::Type::LONGLONG) setTokenValue(parent, castValue(value, valueType.sign, settings->long_long_bit), settings); else if (value.isIntValue()) { const long long charMax = settings->signedCharMax(); const long long charMin = settings->signedCharMin(); if (charMin <= value.intvalue && value.intvalue <= charMax) { // unknown type, but value is small so there should be no truncation etc setTokenValue(parent,value,settings); } } } static nonneg int getSizeOfType(const Token *typeTok, const Settings *settings) { const std::string &typeStr = typeTok->str(); if (typeStr == "char") return 1; else if (typeStr == "short") return settings->sizeof_short; else if (typeStr == "int") return settings->sizeof_int; else if (typeStr == "long") return typeTok->isLong() ? settings->sizeof_long_long : settings->sizeof_long; else if (typeStr == "wchar_t") return settings->sizeof_wchar_t; else return 0; } size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings *settings) { if (vt.pointer) return settings->sizeof_pointer; else if (vt.type == ValueType::Type::CHAR) return 1; else if (vt.type == ValueType::Type::SHORT) return settings->sizeof_short; else if (vt.type == ValueType::Type::WCHAR_T) return settings->sizeof_wchar_t; else if (vt.type == ValueType::Type::INT) return settings->sizeof_int; else if (vt.type == ValueType::Type::LONG) return settings->sizeof_long; else if (vt.type == ValueType::Type::LONGLONG) return settings->sizeof_long_long; else if (vt.type == ValueType::Type::FLOAT) return settings->sizeof_float; else if (vt.type == ValueType::Type::DOUBLE) return settings->sizeof_double; return 0; } // Handle various constants.. static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, bool cpp) { if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) { ValueFlow::Value value(MathLib::toLongNumber(tok->str())); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, settings); } else if (tok->isNumber() && MathLib::isFloat(tok->str())) { ValueFlow::Value value; value.valueType = ValueFlow::Value::FLOAT; value.floatValue = MathLib::toDoubleNumber(tok->str()); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, settings); } else if (tok->enumerator() && tok->enumerator()->value_known) { ValueFlow::Value value(tok->enumerator()->value); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, settings); } else if (tok->str() == "NULL" || (cpp && tok->str() == "nullptr")) { ValueFlow::Value value(0); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, settings); } else if (Token::simpleMatch(tok, "sizeof (")) { const Token *tok2 = tok->tokAt(2); // skip over tokens to find variable or type while (Token::Match(tok2, "%name% ::|.|[")) { if (tok2->next()->str() == "[") tok2 = tok2->linkAt(1)->next(); else tok2 = tok2->tokAt(2); } if (Token::simpleMatch(tok, "sizeof ( *")) { const ValueType *vt = tok->tokAt(2)->valueType(); const size_t sz = vt ? ValueFlow::getSizeOf(*vt, settings) : 0; if (sz > 0) { ValueFlow::Value value(sz); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok->next(), value, settings); } } else if (tok2->enumerator() && tok2->enumerator()->scope) { long long size = settings->sizeof_int; const Token * type = tok2->enumerator()->scope->enumType; if (type) { size = getSizeOfType(type, settings); } ValueFlow::Value value(size); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok, value, settings); setTokenValue(tok->next(), value, settings); } else if (tok2->type() && tok2->type()->isEnumType()) { long long size = settings->sizeof_int; if (tok2->type()->classScope) { const Token * type = tok2->type()->classScope->enumType; if (type) { size = getSizeOfType(type, settings); } } ValueFlow::Value value(size); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok, value, settings); setTokenValue(tok->next(), value, settings); } else if (Token::Match(tok, "sizeof ( %var% ) / sizeof (") && tok->next()->astParent() == tok->tokAt(4)) { // Get number of elements in array const Token *sz1 = tok->tokAt(2); const Token *sz2 = tok->tokAt(7); const int varid1 = sz1->varId(); if (varid1 && sz1->variable() && sz1->variable()->isArray() && !sz1->variable()->dimensions().empty() && sz1->variable()->dimensionKnown(0) && (Token::Match(sz2, "* %varid% )", varid1) || Token::Match(sz2, "%varid% [ 0 ] )", varid1))) { ValueFlow::Value value(sz1->variable()->dimension(0)); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok->tokAt(4), value, settings); } } else if (Token::Match(tok2, "%var% )")) { const Variable *var = tok2->variable(); // only look for single token types (no pointers or references yet) if (var && var->typeStartToken() == var->typeEndToken()) { // find the size of the type size_t size = 0; if (var->isEnumType()) { size = settings->sizeof_int; if (var->type()->classScope && var->type()->classScope->enumType) size = getSizeOfType(var->type()->classScope->enumType, settings); } else if (var->valueType()) { size = ValueFlow::getSizeOf(*var->valueType(), settings); } else if (!var->type()) { size = getSizeOfType(var->typeStartToken(), settings); } // find the number of elements size_t count = 1; for (size_t i = 0; i < var->dimensions().size(); ++i) { if (var->dimensionKnown(i)) count *= var->dimension(i); else count = 0; } if (size && count > 0) { ValueFlow::Value value(count * size); if (settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok, value, settings); setTokenValue(tok->next(), value, settings); } } } else if (tok2->tokType() == Token::eString) { size_t sz = Token::getStrSize(tok2, settings); if (sz > 0) { ValueFlow::Value value(sz); value.setKnown(); setTokenValue(const_cast(tok->next()), value, settings); } } else if (tok2->tokType() == Token::eChar) { nonneg int sz = 0; if (cpp && settings->standards.cpp >= Standards::CPP20 && tok2->isUtf8()) sz = 1; else if (tok2->isUtf16()) sz = 2; else if (tok2->isUtf32()) sz = 4; else if (tok2->isLong()) sz = settings->sizeof_wchar_t; else if ((tok2->isCChar() && !cpp) || (tok2->isCMultiChar())) sz = settings->sizeof_int; else sz = 1; if (sz > 0) { ValueFlow::Value value(sz); value.setKnown(); setTokenValue(tok->next(), value, settings); } } else if (!tok2->type()) { const ValueType &vt = ValueType::parseDecl(tok2,settings); const size_t sz = ValueFlow::getSizeOf(vt, settings); if (sz > 0) { ValueFlow::Value value(sz); if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified) value.setKnown(); setTokenValue(tok->next(), value, settings); } } // skip over enum tok = tok->linkAt(1); } return tok->next(); } static void valueFlowNumber(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok;) { tok = valueFlowSetConstantValue(tok, tokenlist->getSettings(), tokenlist->isCPP()); } if (tokenlist->isCPP()) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->isName() && !tok->varId() && Token::Match(tok, "false|true")) { ValueFlow::Value value(tok->str() == "true"); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok, value, tokenlist->getSettings()); } else if (Token::Match(tok, "[(,] NULL [,)]")) { // NULL function parameters are not simplified in the // normal tokenlist ValueFlow::Value value(0); if (!tok->isTemplateArg()) value.setKnown(); setTokenValue(tok->next(), value, tokenlist->getSettings()); } } } } static void valueFlowString(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->tokType() == Token::eString) { ValueFlow::Value strvalue; strvalue.valueType = ValueFlow::Value::TOK; strvalue.tokvalue = tok; strvalue.setKnown(); setTokenValue(tok, strvalue, tokenlist->getSettings()); } } } static void valueFlowArray(TokenList *tokenlist) { std::map constantArrays; for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->varId() > 0U) { // array const std::map::const_iterator it = constantArrays.find(tok->varId()); if (it != constantArrays.end()) { ValueFlow::Value value; value.valueType = ValueFlow::Value::TOK; value.tokvalue = it->second; value.setKnown(); setTokenValue(tok, value, tokenlist->getSettings()); } // pointer = array else if (tok->variable() && tok->variable()->isArray() && Token::simpleMatch(tok->astParent(), "=") && tok == tok->astParent()->astOperand2() && tok->astParent()->astOperand1() && tok->astParent()->astOperand1()->variable() && tok->astParent()->astOperand1()->variable()->isPointer()) { ValueFlow::Value value; value.valueType = ValueFlow::Value::TOK; value.tokvalue = tok; value.setKnown(); setTokenValue(tok, value, tokenlist->getSettings()); } continue; } if (Token::Match(tok, "const %type% %var% [ %num%| ] = {")) { const Token *vartok = tok->tokAt(2); const Token *rhstok = vartok->next()->link()->tokAt(2); constantArrays[vartok->varId()] = rhstok; tok = rhstok->link(); continue; } else if (Token::Match(tok, "const char %var% [ %num%| ] = %str% ;")) { const Token *vartok = tok->tokAt(2); const Token *strtok = vartok->next()->link()->tokAt(2); constantArrays[vartok->varId()] = strtok; tok = strtok->next(); continue; } } } static bool isNonZero(const Token *tok) { return tok && (!tok->hasKnownIntValue() || tok->values().front().intvalue != 0); } static const Token *getOtherOperand(const Token *tok) { if (!tok) return nullptr; if (!tok->astParent()) return nullptr; if (tok->astParent()->astOperand1() != tok) return tok->astParent()->astOperand1(); if (tok->astParent()->astOperand2() != tok) return tok->astParent()->astOperand2(); return nullptr; } static void valueFlowArrayBool(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->hasKnownIntValue()) continue; const Variable *var = nullptr; bool known = false; std::list::const_iterator val = std::find_if(tok->values().begin(), tok->values().end(), std::mem_fn(&ValueFlow::Value::isTokValue)); if (val == tok->values().end()) { var = tok->variable(); known = true; } else { var = val->tokvalue->variable(); known = val->isKnown(); } if (!var) continue; if (!var->isArray() || var->isArgument() || var->isStlType()) continue; if (isNonZero(getOtherOperand(tok)) && Token::Match(tok->astParent(), "%comp%")) continue; // TODO: Check for function argument if ((astIsBool(tok->astParent()) && !Token::Match(tok->astParent(), "(|%name%")) || (tok->astParent() && Token::Match(tok->astParent()->previous(), "if|while|for ("))) { ValueFlow::Value value{1}; if (known) value.setKnown(); setTokenValue(tok, value, tokenlist->getSettings()); } } } static void valueFlowPointerAlias(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { // not address of if (!tok->isUnaryOp("&")) continue; // parent should be a '=' if (!Token::simpleMatch(tok->astParent(), "=")) continue; // child should be some buffer or variable const Token *vartok = tok->astOperand1(); while (vartok) { if (vartok->str() == "[") vartok = vartok->astOperand1(); else if (vartok->str() == "." || vartok->str() == "::") vartok = vartok->astOperand2(); else break; } if (!(vartok && vartok->variable() && !vartok->variable()->isPointer())) continue; ValueFlow::Value value; value.valueType = ValueFlow::Value::TOK; value.tokvalue = tok; setTokenValue(tok, value, tokenlist->getSettings()); } } static void valueFlowPointerAliasDeref(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->isUnaryOp("*")) continue; if (!astIsPointer(tok->astOperand1())) continue; const Token* lifeTok = nullptr; ErrorPath errorPath; for (const ValueFlow::Value& v:tok->astOperand1()->values()) { if (!v.isLocalLifetimeValue()) continue; lifeTok = v.tokvalue; errorPath = v.errorPath; } if (!lifeTok) continue; if (lifeTok->varId() == 0) continue; const Variable * var = lifeTok->variable(); if (!var) continue; if (!var->isConst() && isVariableChanged(lifeTok->next(), tok, lifeTok->varId(), !var->isLocal(), tokenlist->getSettings(), tokenlist->isCPP())) continue; for (const ValueFlow::Value& v:lifeTok->values()) { if (v.isLifetimeValue()) continue; ValueFlow::Value value = v; value.errorPath.insert(value.errorPath.begin(), errorPath.begin(), errorPath.end()); setTokenValue(tok, value, tokenlist->getSettings()); } } } static void valueFlowBitAnd(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->str() != "&") continue; if (tok->hasKnownValue()) continue; if (!tok->astOperand1() || !tok->astOperand2()) continue; MathLib::bigint number; if (MathLib::isInt(tok->astOperand1()->str())) number = MathLib::toLongNumber(tok->astOperand1()->str()); else if (MathLib::isInt(tok->astOperand2()->str())) number = MathLib::toLongNumber(tok->astOperand2()->str()); else continue; int bit = 0; while (bit <= (MathLib::bigint_bits - 2) && ((((MathLib::bigint)1) << bit) < number)) ++bit; if ((((MathLib::bigint)1) << bit) == number) { setTokenValue(tok, ValueFlow::Value(0), tokenlist->getSettings()); setTokenValue(tok, ValueFlow::Value(number), tokenlist->getSettings()); } } } static void valueFlowSameExpressions(TokenList *tokenlist) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (tok->hasKnownValue()) continue; if (!tok->astOperand1() || !tok->astOperand2()) continue; if (tok->astOperand1()->isLiteral() || tok->astOperand2()->isLiteral()) continue; if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false)) continue; ValueFlow::Value val; if (Token::Match(tok, "==|>=|<=|/")) { val = ValueFlow::Value(1); val.setKnown(); } if (Token::Match(tok, "!=|>|<|%|-")) { val = ValueFlow::Value(0); val.setKnown(); } if (!val.isKnown()) continue; if (isSameExpression(tokenlist->isCPP(), false, tok->astOperand1(), tok->astOperand2(), tokenlist->getSettings()->library, true, true, &val.errorPath)) { setTokenValue(tok, val, tokenlist->getSettings()); } } } static void valueFlowTerminatingCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { const bool cpp = symboldatabase->isCPP(); typedef std::pair Condition; for (const Scope * scope : symboldatabase->functionScopes) { bool skipFunction = false; std::vector conds; for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (tok->isIncompleteVar()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "Skipping function due to incomplete variable " + tok->str()); skipFunction = true; break; } if (!Token::simpleMatch(tok, "if (")) continue; // Skip known values if (tok->next()->hasKnownValue()) continue; const Token * condTok = tok->next(); if (!Token::simpleMatch(condTok->link(), ") {")) continue; const Token * blockTok = condTok->link()->tokAt(1); // Check if the block terminates early if (!isEscapeScope(blockTok, tokenlist)) continue; // Check if any variables are modified in scope bool bail = false; for (const Token * tok2=condTok->next(); tok2 != condTok->link(); tok2 = tok2->next()) { const Variable * var = tok2->variable(); if (!var) continue; if (!var->scope()) continue; const Token * endToken = var->scope()->bodyEnd; if (!var->isLocal() && !var->isConst() && !var->isArgument()) { bail = true; break; } if (var->isStatic() && !var->isConst()) { bail = true; break; } if (!var->isConst() && var->declEndToken() && isVariableChanged(var->declEndToken()->next(), endToken, tok2->varId(), false, settings, cpp)) { bail = true; break; } } if (bail) continue; // TODO: Handle multiple conditions if (Token::Match(condTok->astOperand2(), "%oror%|%or%|&|&&")) continue; const Scope * condScope = nullptr; for (const Scope * parent = condTok->scope(); parent; parent = parent->nestedIn) { if (parent->type == Scope::eIf || parent->type == Scope::eWhile || parent->type == Scope::eSwitch) { condScope = parent; break; } } conds.emplace_back(condTok->astOperand2(), condScope); } if (skipFunction) break; for (Condition cond:conds) { if (!cond.first) continue; Token *const startToken = cond.first->findExpressionStartEndTokens().second->next(); for (Token* tok = startToken; tok != scope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "%comp%")) continue; // Skip known values if (tok->hasKnownValue()) continue; if (cond.second) { bool bail = true; for (const Scope * parent = tok->scope()->nestedIn; parent; parent = parent->nestedIn) { if (parent == cond.second) { bail = false; break; } } if (bail) continue; } ErrorPath errorPath; if (isOppositeCond(true, cpp, tok, cond.first, settings->library, true, true, &errorPath)) { ValueFlow::Value val(1); val.setKnown(); val.condition = cond.first; val.errorPath = errorPath; val.errorPath.emplace_back(cond.first, "Assuming condition '" + cond.first->expressionString() + "' is false"); setTokenValue(tok, val, tokenlist->getSettings()); } else if (isSameExpression(cpp, true, tok, cond.first, settings->library, true, true, &errorPath)) { ValueFlow::Value val(0); val.setKnown(); val.condition = cond.first; val.errorPath = errorPath; val.errorPath.emplace_back(cond.first, "Assuming condition '" + cond.first->expressionString() + "' is false"); setTokenValue(tok, val, tokenlist->getSettings()); } } } } } static bool getExpressionRange(const Token *expr, MathLib::bigint *minvalue, MathLib::bigint *maxvalue) { if (expr->hasKnownIntValue()) { if (minvalue) *minvalue = expr->values().front().intvalue; if (maxvalue) *maxvalue = expr->values().front().intvalue; return true; } if (expr->str() == "&" && expr->astOperand1() && expr->astOperand2()) { MathLib::bigint vals[4]; bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]); bool rhsHasKnownRange = getExpressionRange(expr->astOperand2(), &vals[2], &vals[3]); if (!lhsHasKnownRange && !rhsHasKnownRange) return false; if (!lhsHasKnownRange || !rhsHasKnownRange) { if (minvalue) *minvalue = lhsHasKnownRange ? vals[0] : vals[2]; if (maxvalue) *maxvalue = lhsHasKnownRange ? vals[1] : vals[3]; } else { if (minvalue) *minvalue = vals[0] & vals[2]; if (maxvalue) *maxvalue = vals[1] & vals[3]; } return true; } if (expr->str() == "%" && expr->astOperand1() && expr->astOperand2()) { MathLib::bigint vals[4]; if (!getExpressionRange(expr->astOperand2(), &vals[2], &vals[3])) return false; if (vals[2] <= 0) return false; bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]); if (lhsHasKnownRange && vals[0] < 0) return false; // If lhs has unknown value, it must be unsigned if (!lhsHasKnownRange && (!expr->astOperand1()->valueType() || expr->astOperand1()->valueType()->sign != ValueType::Sign::UNSIGNED)) return false; if (minvalue) *minvalue = 0; if (maxvalue) *maxvalue = vals[3] - 1; return true; } return false; } static void valueFlowRightShift(TokenList *tokenList, const Settings* settings) { for (Token *tok = tokenList->front(); tok; tok = tok->next()) { if (tok->str() != ">>") continue; if (tok->hasKnownValue()) continue; if (!tok->astOperand1() || !tok->astOperand2()) continue; if (!tok->astOperand2()->hasKnownValue()) continue; const MathLib::bigint rhsvalue = tok->astOperand2()->values().front().intvalue; if (rhsvalue < 0) continue; if (!tok->astOperand1()->valueType() || !tok->astOperand1()->valueType()->isIntegral()) continue; if (!tok->astOperand2()->valueType() || !tok->astOperand2()->valueType()->isIntegral()) continue; MathLib::bigint lhsmax=0; if (!getExpressionRange(tok->astOperand1(), nullptr, &lhsmax)) continue; if (lhsmax < 0) continue; int lhsbits; if ((tok->astOperand1()->valueType()->type == ValueType::Type::CHAR) || (tok->astOperand1()->valueType()->type == ValueType::Type::SHORT) || (tok->astOperand1()->valueType()->type == ValueType::Type::WCHAR_T) || (tok->astOperand1()->valueType()->type == ValueType::Type::BOOL) || (tok->astOperand1()->valueType()->type == ValueType::Type::INT)) lhsbits = settings->int_bit; else if (tok->astOperand1()->valueType()->type == ValueType::Type::LONG) lhsbits = settings->long_bit; else if (tok->astOperand1()->valueType()->type == ValueType::Type::LONGLONG) lhsbits = settings->long_long_bit; else continue; if (rhsvalue >= lhsbits || rhsvalue >= MathLib::bigint_bits || (1ULL << rhsvalue) <= lhsmax) continue; ValueFlow::Value val(0); val.setKnown(); setTokenValue(tok, val, tokenList->getSettings()); } } static void valueFlowOppositeCondition(SymbolDatabase *symboldatabase, const Settings *settings) { for (const Scope &scope : symboldatabase->scopeList) { if (scope.type != Scope::eIf) continue; Token *tok = const_cast(scope.classDef); if (!Token::simpleMatch(tok, "if (")) continue; const Token *cond1 = tok->next()->astOperand2(); if (!cond1 || !cond1->isComparisonOp()) continue; const bool cpp = symboldatabase->isCPP(); Token *tok2 = tok->linkAt(1); while (Token::simpleMatch(tok2, ") {")) { tok2 = tok2->linkAt(1); if (!Token::simpleMatch(tok2, "} else { if (")) break; Token *ifOpenBraceTok = tok2->tokAt(4); Token *cond2 = ifOpenBraceTok->astOperand2(); if (!cond2 || !cond2->isComparisonOp()) continue; if (isOppositeCond(true, cpp, cond1, cond2, settings->library, true, true)) { ValueFlow::Value value(1); value.setKnown(); setTokenValue(cond2, value, settings); } tok2 = ifOpenBraceTok->link(); } } } static void valueFlowGlobalConstVar(TokenList* tokenList, const Settings *settings) { // Get variable values... std::map vars; for (const Token* tok = tokenList->front(); tok; tok = tok->next()) { if (!tok->variable()) continue; // Initialization... if (tok == tok->variable()->nameToken() && !tok->variable()->isVolatile() && !tok->variable()->isArgument() && tok->variable()->isConst() && tok->valueType() && tok->valueType()->isIntegral() && tok->valueType()->pointer == 0 && tok->valueType()->constness == 1 && Token::Match(tok, "%name% =") && tok->next()->astOperand2() && tok->next()->astOperand2()->hasKnownIntValue()) { vars[tok->variable()] = tok->next()->astOperand2()->values().front(); } } // Set values.. for (Token* tok = tokenList->front(); tok; tok = tok->next()) { if (!tok->variable()) continue; std::map::const_iterator var = vars.find(tok->variable()); if (var == vars.end()) continue; setTokenValue(tok, var->second, settings); } } static void valueFlowGlobalStaticVar(TokenList *tokenList, const Settings *settings) { // Get variable values... std::map vars; for (const Token *tok = tokenList->front(); tok; tok = tok->next()) { if (!tok->variable()) continue; // Initialization... if (tok == tok->variable()->nameToken() && tok->variable()->isStatic() && !tok->variable()->isConst() && tok->valueType() && tok->valueType()->isIntegral() && tok->valueType()->pointer == 0 && tok->valueType()->constness == 0 && Token::Match(tok, "%name% =") && tok->next()->astOperand2() && tok->next()->astOperand2()->hasKnownIntValue()) { vars[tok->variable()] = tok->next()->astOperand2()->values().front(); } else { // If variable is written anywhere in TU then remove it from vars if (!tok->astParent()) continue; if (Token::Match(tok->astParent(), "++|--|&") && !tok->astParent()->astOperand2()) vars.erase(tok->variable()); else if (tok->astParent()->isAssignmentOp()) { if (tok == tok->astParent()->astOperand1()) vars.erase(tok->variable()); else if (tokenList->isCPP() && Token::Match(tok->astParent()->tokAt(-2), "& %name% =")) vars.erase(tok->variable()); } else if (isLikelyStreamRead(tokenList->isCPP(), tok->astParent())) { vars.erase(tok->variable()); } else if (Token::Match(tok->astParent(), "[(,]")) vars.erase(tok->variable()); } } // Set values.. for (Token *tok = tokenList->front(); tok; tok = tok->next()) { if (!tok->variable()) continue; std::map::const_iterator var = vars.find(tok->variable()); if (var == vars.end()) continue; setTokenValue(tok, var->second, settings); } } static void valueFlowForward(Token* startToken, const Token* endToken, const Token* exprTok, std::list values, const bool constValue, const bool subFunction, TokenList* const tokenlist, ErrorLogger* const errorLogger, const Settings* settings); static void valueFlowReverse(TokenList *tokenlist, Token *tok, const Token * const varToken, ValueFlow::Value val, ValueFlow::Value val2, ErrorLogger *errorLogger, const Settings *settings) { const MathLib::bigint num = val.intvalue; const Variable * const var = varToken->variable(); if (!var) return; const int varid = varToken->varId(); const Token * const startToken = var->nameToken(); for (Token *tok2 = tok->previous(); ; tok2 = tok2->previous()) { if (!tok2 || tok2 == startToken || (tok2->str() == "{" && (tok2->scope()->type == Scope::ScopeType::eFunction || tok2->scope()->type == Scope::ScopeType::eLambda))) { break; } if (tok2->varId() == varid) { if (tok2->hasKnownValue()) break; // bailout: assignment if (Token::Match(tok2->previous(), "!!* %name% =")) { Token* assignTok = const_cast(tok2->next()->astOperand2()); if (!assignTok->hasKnownValue()) { setTokenValue(assignTok, val, settings); const std::string info = "Assignment from '" + assignTok->expressionString() + "'"; val.errorPath.emplace_back(assignTok, info); std::list values = {val}; if (val2.condition) { val2.errorPath.emplace_back(assignTok, info); setTokenValue(assignTok, val2, settings); values.push_back(val2); } const Token* startForwardToken = nextAfterAstRightmostLeaf(tok2->next()); const Token* endForwardToken = tok->scope() ? tok->scope()->bodyEnd : tok; valueFlowForward(const_cast(startForwardToken), endForwardToken, assignTok, values, false, false, tokenlist, errorLogger, settings); // Only reverse analysis supported with variables if (assignTok->varId() > 0) valueFlowReverse(tokenlist, tok2->previous(), assignTok, val, val2, errorLogger, settings); } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str()); break; } // increment/decrement int inc = 0; if (Token::Match(tok2->previous(), "[;{}] %name% ++|-- ;")) inc = (tok2->strAt(1)=="++") ? -1 : 1; else if (Token::Match(tok2->tokAt(-2), "[;{}] ++|-- %name% ;")) inc = (tok2->strAt(-1)=="++") ? -1 : 1; else if (Token::Match(tok2->previous(), "++|-- %name%") || Token::Match(tok2, "%name% ++|--")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "increment/decrement of " + tok2->str()); break; } if (inc != 0) { val.intvalue += inc; const std::string info(tok2->str() + " is " + std::string(inc==1 ? "decremented" : "incremented") + ", before this " + (inc==1?"decrement":"increment") + " the value is " + val.infoString()); val.errorPath.emplace_back(tok2, info); } // compound assignment if (Token::Match(tok2->previous(), "[;{}] %var% %assign%") && tok2->next()->str() != "=") { const Token * const assignToken = tok2->next(); const Token * const rhsToken = assignToken->astOperand2(); if (!rhsToken || !rhsToken->hasKnownIntValue()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "compound assignment, rhs value is not known"); break; } const MathLib::bigint rhsValue = rhsToken->values().front().intvalue; if (assignToken->str() == "+=") val.intvalue -= rhsValue; else if (assignToken->str() == "-=") val.intvalue += rhsValue; else if (assignToken->str() == "*=" && rhsValue != 0) val.intvalue /= rhsValue; else { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "compound assignment " + tok2->str()); break; } const std::string info("Compound assignment '" + assignToken->str() + "', before assignment value is " + val.infoString()); val.errorPath.emplace_back(tok2, info); } // bailout: variable is used in rhs in assignment to itself if (bailoutSelfAssignment(tok2)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + tok2->str() + " is used in rhs in assignment to itself"); break; } if (Token::Match(tok2->previous(), "sizeof|.")) { const Token *prev = tok2->previous(); while (Token::Match(prev,"%name%|.") && prev->str() != "sizeof") prev = prev->previous(); if (prev && prev->str() == "sizeof") continue; } // assigned by subfunction? bool inconclusive = false; if (isVariableChangedByFunctionCall(tok2, std::max(val.indirect, val2.indirect), settings, &inconclusive)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction"); break; } // Impossible values can't be inconclusive if (val.isImpossible() || val2.isImpossible()) break; val.setInconclusive(inconclusive); val2.setInconclusive(inconclusive); // skip if variable is conditionally used in ?: expression if (const Token *parent = skipValueInConditionalExpression(tok2)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "no simplification of " + tok2->str() + " within " + (Token::Match(parent,"[?:]") ? "?:" : parent->str()) + " expression"); continue; } // do-while condition, break in the loop body { const Token *parent = tok2->astParent(); while (parent && !Token::simpleMatch(parent->previous(), "while (")) parent = parent->astParent(); if (parent && Token::simpleMatch(parent->tokAt(-2), "} while (") && Token::simpleMatch(parent->linkAt(-2)->previous(), "do {")) { bool breakBailout = false; for (const Token *iftok = parent->linkAt(-2); iftok != parent; iftok = iftok->next()) { if (!Token::simpleMatch(iftok, "if (")) continue; if (!Token::simpleMatch(iftok->linkAt(1), ") { break")) continue; ProgramMemory programMemory; programMemory.setIntValue(varid, num); if (conditionIsTrue(iftok->next()->astOperand2(), programMemory)) { breakBailout = true; break; } } if (breakBailout) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "no simplification of " + tok2->str() + " in do-while condition since there is a break in the loop body"); break; } } } setTokenValue(tok2, val, settings); if (val2.condition) setTokenValue(tok2,val2, settings); if (tok2 == var->nameToken()) break; } // skip sizeof etc.. if (tok2->str() == ")" && Token::Match(tok2->link()->previous(), "sizeof|typeof|typeid (")) tok2 = tok2->link(); // goto label if (Token::Match(tok2, "[;{}] %name% :")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2->next(), "variable " + var->name() + " stopping on goto label"); break; } if (tok2->str() == "}") { const Token *vartok = Token::findmatch(tok2->link(), "%varid%", tok2, varid); while (Token::Match(vartok, "%name% = %num% ;") && !vartok->tokAt(2)->getValue(num)) vartok = Token::findmatch(vartok->next(), "%varid%", tok2, varid); if (vartok) { if (settings->debugwarnings) { std::string errmsg = "variable "; errmsg += var->name() + " "; errmsg += "stopping on }"; bailout(tokenlist, errorLogger, tok2, errmsg); } break; } else { tok2 = tok2->link(); } } else if (tok2->str() == "{") { // if variable is assigned in loop don't look before the loop if (tok2->previous() && (Token::simpleMatch(tok2->previous(), "do") || (tok2->strAt(-1) == ")" && Token::Match(tok2->linkAt(-1)->previous(), "for|while (")))) { const Token *start = tok2; const Token *end = start->link(); if (isVariableChanged(start,end,varid,var->isGlobal(),settings, tokenlist->isCPP())) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " is assigned in loop. so valueflow analysis bailout when start of loop is reached."); break; } } // Global variable : stop when leaving the function scope if (!var->isLocal()) { if (!Token::Match(tok2->previous(), ")|else|do {")) break; if ((tok2->previous()->str() == ")") && !Token::Match(tok2->linkAt(-1)->previous(), "if|for|while (")) break; } } else if (tok2->str() == ";") { const Token *parent = tok2->previous(); while (parent && !Token::Match(parent, "return|break|continue|goto")) parent = parent->astParent(); // reaching a break/continue/return if (parent) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " stopping on " + parent->str()); break; } } if (Token::Match(tok2, "%name% (") && !Token::simpleMatch(tok2->linkAt(1), ") {")) { // bailout: global non-const variables if (!(var->isLocal() || var->isArgument()) && !var->isConst()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "global variable " + var->name()); return; } } } } static bool isConditionKnown(const Token* tok, bool then) { const char* op = "||"; if (then) op = "&&"; const Token* parent = tok->astParent(); while (parent && parent->str() == op) parent = parent->astParent(); return (parent && parent->str() == "("); } static void valueFlowBeforeCondition(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope * scope : symboldatabase->functionScopes) { for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { MathLib::bigint num = 0; const Token *vartok = nullptr; if (tok->isComparisonOp() && tok->astOperand1() && tok->astOperand2()) { if (tok->astOperand1()->isName() && tok->astOperand2()->hasKnownIntValue()) { vartok = tok->astOperand1(); num = tok->astOperand2()->values().front().intvalue; } else if (tok->astOperand1()->hasKnownIntValue() && tok->astOperand2()->isName()) { vartok = tok->astOperand2(); num = tok->astOperand1()->values().front().intvalue; } else { continue; } } else if (Token::Match(tok->previous(), "if|while ( %name% %oror%|&&|)") || Token::Match(tok, "%oror%|&& %name% %oror%|&&|)")) { vartok = tok->next(); num = 0; } else if (Token::Match(tok, "[!?]") && Token::Match(tok->astOperand1(), "%name%")) { vartok = tok->astOperand1(); num = 0; } else { continue; } int varid = vartok->varId(); const Variable * const var = vartok->variable(); if (varid == 0U || !var) continue; if (tok->str() == "?" && tok->isExpandedMacro()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable " + var->name() + ", condition is defined in macro"); continue; } // bailout: for/while-condition, variable is changed in while loop for (const Token *tok2 = tok; tok2; tok2 = tok2->astParent()) { if (tok2->astParent() || tok2->str() != "(" || !Token::simpleMatch(tok2->link(), ") {")) continue; // Variable changed in 3rd for-expression if (Token::simpleMatch(tok2->previous(), "for (")) { if (tok2->astOperand2() && tok2->astOperand2()->astOperand2() && isVariableChanged(tok2->astOperand2()->astOperand2(), tok2->link(), varid, var->isGlobal(), settings, tokenlist->isCPP())) { varid = 0U; if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable " + var->name() + " used in loop"); } } // Variable changed in loop code if (Token::Match(tok2->previous(), "for|while (")) { const Token * const start = tok2->link()->next(); const Token * const end = start->link(); if (isVariableChanged(start,end,varid,var->isGlobal(),settings, tokenlist->isCPP())) { varid = 0U; if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable " + var->name() + " used in loop"); } } // if,macro => bailout else if (Token::simpleMatch(tok2->previous(), "if (") && tok2->previous()->isExpandedMacro()) { varid = 0U; if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "variable " + var->name() + ", condition is defined in macro"); } } if (varid == 0U) continue; // extra logic for unsigned variables 'i>=1' => possible value can also be 0 if (Token::Match(tok, "<|>")) { if (num != 0) continue; if (!var->typeStartToken()->isUnsigned()) continue; } ValueFlow::Value val(tok, num); val.varId = varid; ValueFlow::Value val2; if (num==1U && Token::Match(tok,"<=|>=")) { if (var->typeStartToken()->isUnsigned()) { val2 = ValueFlow::Value(tok,0); val2.varId = varid; } } Token* startTok = tok->astParent() ? tok->astParent() : tok->previous(); valueFlowReverse(tokenlist, startTok, vartok, val, val2, errorLogger, settings); } } } static void removeValues(std::list &values, const std::list &valuesToRemove) { for (std::list::iterator it = values.begin(); it != values.end();) { const bool found = std::any_of(valuesToRemove.cbegin(), valuesToRemove.cend(), [=](const ValueFlow::Value &v2) { return it->intvalue == v2.intvalue; }); if (found) values.erase(it++); else ++it; } } static void valueFlowAST(Token *tok, nonneg int varid, const ValueFlow::Value &value, const Settings *settings) { if (!tok) return; if (tok->varId() == varid) setTokenValue(tok, value, settings); valueFlowAST(tok->astOperand1(), varid, value, settings); if (tok->str() == "&&" && tok->astOperand1() && tok->astOperand1()->getValue(0)) { ProgramMemory pm; pm.setValue(varid,value); if (conditionIsFalse(tok->astOperand1(), pm)) return; } else if (tok->str() == "||" && tok->astOperand1()) { const std::list &values = tok->astOperand1()->values(); const bool nonzero = std::any_of(values.cbegin(), values.cend(), [=](const ValueFlow::Value &v) { return v.intvalue != 0; }); if (!nonzero) return; ProgramMemory pm; pm.setValue(varid,value); if (conditionIsTrue(tok->astOperand1(), pm)) return; } valueFlowAST(tok->astOperand2(), varid, value, settings); } /** if known variable is changed in loop body, change it to a possible value */ static bool handleKnownValuesInLoop(const Token *startToken, const Token *endToken, std::list *values, nonneg int varid, bool globalvar, const Settings *settings) { const bool isChanged = isVariableChanged(startToken, endToken, varid, globalvar, settings, true); if (!isChanged) return false; lowerToPossible(*values); return isChanged; } static bool evalAssignment(ValueFlow::Value &lhsValue, const std::string &assign, const ValueFlow::Value &rhsValue) { if (lhsValue.isIntValue()) { if (assign == "+=") lhsValue.intvalue += rhsValue.intvalue; else if (assign == "-=") lhsValue.intvalue -= rhsValue.intvalue; else if (assign == "*=") lhsValue.intvalue *= rhsValue.intvalue; else if (assign == "/=") { if (rhsValue.intvalue == 0) return false; else lhsValue.intvalue /= rhsValue.intvalue; } else if (assign == "%=") { if (rhsValue.intvalue == 0) return false; else lhsValue.intvalue %= rhsValue.intvalue; } else if (assign == "&=") lhsValue.intvalue &= rhsValue.intvalue; else if (assign == "|=") lhsValue.intvalue |= rhsValue.intvalue; else if (assign == "^=") lhsValue.intvalue ^= rhsValue.intvalue; else return false; } else if (lhsValue.isFloatValue()) { if (assign == "+=") lhsValue.floatValue += rhsValue.intvalue; else if (assign == "-=") lhsValue.floatValue -= rhsValue.intvalue; else if (assign == "*=") lhsValue.floatValue *= rhsValue.intvalue; else if (assign == "/=") lhsValue.floatValue /= rhsValue.intvalue; else return false; } else { return false; } return true; } // Check if its an alias of the variable or is being aliased to this variable static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const std::list& values) { if (tok->varId() == varid) return false; if (tok->varId() == 0) return false; if (isAliasOf(tok, varid)) return true; if (!var->isPointer()) return false; // Search through non value aliases for (const ValueFlow::Value &val : values) { if (!val.isNonValue()) continue; if (val.isInconclusive()) continue; if (val.isLifetimeValue() && !val.isLocalLifetimeValue()) continue; if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) continue; if (!Token::Match(val.tokvalue, ".|&|*|%var%")) continue; if (astHasVar(val.tokvalue, tok->varId())) return true; } return false; } static std::set getIndirections(const std::list& values) { std::set result; std::transform(values.begin(), values.end(), std::inserter(result, result.end()), [](const ValueFlow::Value& v) { return std::max(0, v.indirect); }); return result; } static void valueFlowForwardExpression(Token* startToken, const Token* endToken, const Token* exprTok, const std::list& values, const TokenList* const tokenlist, const Settings* settings) { FwdAnalysis fwdAnalysis(tokenlist->isCPP(), settings->library); for (const FwdAnalysis::KnownAndToken read : fwdAnalysis.valueFlow(exprTok, startToken, endToken)) { for (const ValueFlow::Value& value : values) { // Don't set inconclusive values if (value.isInconclusive()) continue; ValueFlow::Value v = value; if (v.isImpossible()) { if (read.known) continue; } else if (!read.known) { v.valueKind = ValueFlow::Value::ValueKind::Possible; } setTokenValue(const_cast(read.token), v, settings); } } } static bool valueFlowForwardVariable(Token* const startToken, const Token* const endToken, const Variable* const var, const nonneg int varid, std::list values, const bool constValue, const bool subFunction, TokenList* const tokenlist, ErrorLogger* const errorLogger, const Settings* const settings) { int indentlevel = 0; int number_of_if = 0; int varusagelevel = -1; bool returnStatement = false; // current statement is a return, stop analysis at the ";" bool read = false; // is variable value read? if (values.empty()) return true; for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) { if (values.empty()) return true; if (indentlevel >= 0 && tok2->str() == "{") ++indentlevel; else if (indentlevel >= 0 && tok2->str() == "}") { --indentlevel; if (indentlevel <= 0 && isReturnScope(tok2, &settings->library) && Token::Match(tok2->link()->previous(), "else|) {")) { const Token *condition = tok2->link(); const bool iselse = Token::simpleMatch(condition->tokAt(-2), "} else {"); if (iselse) condition = condition->linkAt(-2); if (condition && Token::simpleMatch(condition->previous(), ") {")) condition = condition->linkAt(-1)->astOperand2(); else condition = nullptr; if (!condition) { if (settings->debugwarnings) bailout( tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForwardVariable, bailing out since it's unknown if conditional return is executed"); return false; } bool bailoutflag = false; const Token * const start1 = iselse ? tok2->link()->linkAt(-2) : nullptr; for (std::list::iterator it = values.begin(); it != values.end();) { if (!iselse && conditionIsTrue(condition, getProgramMemory(condition->astParent(), varid, *it))) { bailoutflag = true; break; } if (iselse && conditionIsFalse(condition, getProgramMemory(condition->astParent(), varid, *it))) { bailoutflag = true; break; } if (iselse && it->isPossible() && isVariableChanged(start1, start1->link(), varid, var->isGlobal(), settings, tokenlist->isCPP())) values.erase(it++); else ++it; } if (bailoutflag) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForwardVariable, conditional return is assumed to be executed"); return false; } if (values.empty()) return true; } else if (indentlevel <= 0 && Token::simpleMatch(tok2->link()->previous(), "else {") && !isReturnScope(tok2->link()->tokAt(-2), &settings->library) && isVariableChanged(tok2->link(), tok2, varid, var->isGlobal(), settings, tokenlist->isCPP())) { lowerToPossible(values); } } // skip lambda functions // TODO: handle lambda functions if (tok2->str() == "[" && findLambdaEndToken(tok2)) { Token *lambdaEndToken = const_cast(findLambdaEndToken(tok2)); if (isVariableChanged(lambdaEndToken->link(), lambdaEndToken, varid, var->isGlobal(), settings, tokenlist->isCPP())) return false; // Don't skip lambdas for lifetime values if (!std::all_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) { tok2 = lambdaEndToken; continue; } } if (Token::Match(tok2, "[;{}] %name% :") || tok2->str() == "case") { lowerToPossible(values); tok2 = tok2->tokAt(2); continue; } else if ((var->isGlobal() || tok2->str() == "asm") && Token::Match(tok2, "%name% (") && Token::Match(tok2->linkAt(1), ") !!{")) { return false; } // Skip sizeof etc else if (Token::Match(tok2, "sizeof|typeof|typeid (")) tok2 = tok2->linkAt(1); else if (Token::simpleMatch(tok2, "else {")) { // Should scope be skipped because variable value is checked? const Token *condition = tok2->linkAt(-1); condition = condition ? condition->linkAt(-1) : nullptr; condition = condition ? condition->astOperand2() : nullptr; const bool skipelse = std::any_of(values.cbegin(), values.cend(), [=](const ValueFlow::Value &v) { return conditionIsTrue(condition, getProgramMemory(tok2, varid, v)); }); if (skipelse) { tok2 = tok2->linkAt(1); continue; } } else if (Token::simpleMatch(tok2, "do {")) { const Token *start = tok2->next(); const Token *end = start->link(); if (Token::simpleMatch(end, "} while (")) end = end->linkAt(2); if (isVariableChanged(start, end, varid, var->isGlobal(), settings, tokenlist->isCPP())) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForwardVariable, assignment in do-while"); return false; } handleKnownValuesInLoop(start, end, &values, varid, var->isGlobal(), settings); } // conditional block of code that assigns variable.. else if (!tok2->varId() && Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") {")) { // is variable changed in condition? for (int i:getIndirections(values)) { Token* tokChanged = findVariableChanged(tok2->next(), tok2->next()->link(), i, varid, var->isGlobal(), settings, tokenlist->isCPP()); if (tokChanged != nullptr) { // Set the value before bailing if (tokChanged->varId() == varid) { for (const ValueFlow::Value &v : values) { if (!v.isNonValue()) continue; setTokenValue(tokChanged, v, settings); } } values.remove_if([&](const ValueFlow::Value& v) { return v.indirect == i; }); } } if (values.empty()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForwardVariable, assignment in condition"); return false; } // if known variable is changed in loop body, change it to a possible value.. if (Token::Match(tok2, "for|while")) { if (handleKnownValuesInLoop(tok2, tok2->linkAt(1)->linkAt(1), &values, varid, var->isGlobal(), settings)) number_of_if++; } // Set values in condition for (Token* tok3 = tok2->tokAt(2); tok3 != tok2->next()->link(); tok3 = tok3->next()) { if (tok3->varId() == varid) { for (const ValueFlow::Value &v : values) setTokenValue(tok3, v, settings); } else if (Token::Match(tok3, "%oror%|&&|?|;")) { break; } } const Token * const condTok = tok2->next()->astOperand2(); const bool condAlwaysTrue = (condTok && condTok->hasKnownIntValue() && condTok->values().front().intvalue != 0); const bool condAlwaysFalse = (condTok && condTok->hasKnownIntValue() && condTok->values().front().intvalue == 0); // Should scope be skipped because variable value is checked? std::list truevalues; std::list falsevalues; for (const ValueFlow::Value &v : values) { if (condAlwaysTrue) { truevalues.push_back(v); continue; } if (condAlwaysFalse) { falsevalues.push_back(v); continue; } // TODO: Compute program from tokvalue first ProgramMemory programMemory = getProgramMemory(tok2, varid, v); const bool isTrue = conditionIsTrue(condTok, programMemory); const bool isFalse = conditionIsFalse(condTok, programMemory); if (isTrue) truevalues.push_back(v); if (isFalse) falsevalues.push_back(v); } if (!truevalues.empty() || !falsevalues.empty()) { // '{' const Token * const startToken1 = tok2->linkAt(1)->next(); bool vfresult = valueFlowForwardVariable(startToken1->next(), startToken1->link(), var, varid, truevalues, constValue, subFunction, tokenlist, errorLogger, settings); if (!condAlwaysFalse && isVariableChanged(startToken1, startToken1->link(), varid, var->isGlobal(), settings, tokenlist->isCPP())) { removeValues(values, truevalues); lowerToPossible(values); } // goto '}' tok2 = startToken1->link(); if (isEscapeScope(startToken1, tokenlist, true) || !vfresult) { if (condAlwaysTrue) return false; removeValues(values, truevalues); } if (Token::simpleMatch(tok2, "} else {")) { const Token * const startTokenElse = tok2->tokAt(2); vfresult = valueFlowForwardVariable(startTokenElse->next(), startTokenElse->link(), var, varid, falsevalues, constValue, subFunction, tokenlist, errorLogger, settings); if (!condAlwaysTrue && isVariableChanged(startTokenElse, startTokenElse->link(), varid, var->isGlobal(), settings, tokenlist->isCPP())) { removeValues(values, falsevalues); lowerToPossible(values); } // goto '}' tok2 = startTokenElse->link(); if (isEscapeScope(startTokenElse, tokenlist, true) || !vfresult) { if (condAlwaysFalse) return false; removeValues(values, falsevalues); } } if (values.empty()) return false; continue; } Token * const start = tok2->linkAt(1)->next(); Token * const end = start->link(); const bool varusage = (indentlevel >= 0 && constValue && number_of_if == 0U) ? isVariableChanged(start,end,varid,var->isGlobal(),settings, tokenlist->isCPP()) : (nullptr != Token::findmatch(start, "%varid%", end, varid)); if (!read) { read = bool(nullptr != Token::findmatch(tok2, "%varid% !!=", end, varid)); } if (varusage) { varusagelevel = indentlevel; if (indentlevel < 0 && tok2->str() == "switch") return false; // TODO: don't check noreturn scopes if (read && (number_of_if > 0U || Token::findmatch(tok2, "%varid%", start, varid))) { // Set values in condition const Token * const condend = tok2->linkAt(1); for (Token *condtok = tok2; condtok != condend; condtok = condtok->next()) { if (condtok->varId() == varid) { for (const ValueFlow::Value &v : values) setTokenValue(condtok, v, settings); } if (Token::Match(condtok, "%oror%|&&|?|;")) break; } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " is assigned in conditional code"); return false; } if (var->isStatic()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " bailout when conditional code that contains var is seen"); return false; } // Forward known values in the else branch if (Token::simpleMatch(end, "} else {")) { std::list knownValues; std::copy_if(values.begin(), values.end(), std::back_inserter(knownValues), std::mem_fn(&ValueFlow::Value::isKnown)); valueFlowForwardVariable(end->tokAt(2), end->linkAt(2), var, varid, knownValues, constValue, subFunction, tokenlist, errorLogger, settings); } // Remove conditional values std::list::iterator it; for (it = values.begin(); it != values.end();) { if (it->condition || it->conditional || it->isImpossible()) values.erase(it++); else { it->changeKnownToPossible(); ++it; } } } // stop after conditional return scopes that are executed if (isReturnScope(end, &settings->library)) { std::list::iterator it; for (it = values.begin(); it != values.end();) { if (conditionIsTrue(tok2->next()->astOperand2(), getProgramMemory(tok2, varid, *it))) values.erase(it++); else ++it; } if (values.empty()) return false; } // noreturn scopes.. if ((number_of_if > 0 || Token::findmatch(tok2, "%varid%", start, varid)) && (isEscapeScope(start, tokenlist) || (Token::simpleMatch(end,"} else {") && isEscapeScope(end->tokAt(2), tokenlist)))) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + ". noreturn conditional scope."); return false; } if (isVariableChanged(start, end, varid, var->isGlobal(), settings, tokenlist->isCPP())) { if ((!read || number_of_if == 0) && Token::simpleMatch(tok2, "if (") && !(Token::simpleMatch(end, "} else {") && isEscapeScope(end->tokAt(2), tokenlist))) { ++number_of_if; tok2 = end; } else { // loop that conditionally set variable and then break => either loop condition is // redundant or the variable can be unchanged after the loop. bool loopCondition = false; if (Token::simpleMatch(tok2, "while (") && Token::Match(tok2->next()->astOperand2(), "%op%")) loopCondition = true; else if (Token::simpleMatch(tok2, "for (") && Token::simpleMatch(tok2->next()->astOperand2(), ";") && Token::simpleMatch(tok2->next()->astOperand2()->astOperand2(), ";") && Token::Match(tok2->next()->astOperand2()->astOperand2()->astOperand1(), "%op%")) loopCondition = true; bool bail = true; if (loopCondition) { const Token *tok3 = Token::findmatch(start, "%varid%", end, varid); if (Token::Match(tok3, "%varid% =", varid) && tok3->scope()->bodyEnd && Token::Match(tok3->scope()->bodyEnd->tokAt(-3), "[;}] break ;") && !Token::findmatch(tok3->next(), "%varid%", end, varid)) { bail = false; tok2 = end; } } if (bail) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " is assigned in conditional code"); return false; } } } } else if (Token::Match(tok2, "assert|ASSERT (") && Token::simpleMatch(tok2->linkAt(1), ") ;")) { const Token * const arg = tok2->next()->astOperand2(); if (arg != nullptr && arg->str() != ",") { // Should scope be skipped because variable value is checked? for (std::list::iterator it = values.begin(); it != values.end();) { if (conditionIsFalse(arg, getProgramMemory(tok2, varid, *it))) values.erase(it++); else ++it; } } } // TODO: Check for eFunction else if (tok2->str() == "}" && indentlevel <= 0 && tok2->scope() && tok2->scope()->type == Scope::eLambda) { return true; } else if (tok2->str() == "}" && indentlevel == varusagelevel) { ++number_of_if; // Set "conditional" flag for all values removeImpossible(values); std::list::iterator it; for (it = values.begin(); it != values.end(); ++it) { it->conditional = true; it->changeKnownToPossible(); } if (Token::simpleMatch(tok2,"} else {")) tok2 = tok2->linkAt(2); } else if (Token::Match(tok2, "break|continue|goto")) { const Scope *scope = tok2->scope(); if (indentlevel > 0) { const Token *tok3 = tok2->tokAt(2); int indentlevel2 = indentlevel; while (indentlevel2 > 0 && tok3->str() == "}" && Token::Match(tok3->link()->previous(), "!!)")) { indentlevel2--; tok3 = tok3->next(); if (tok3 && tok3->str() == ";") tok3 = tok3->next(); } if (indentlevel2 > 0) continue; scope = tok3->scope(); indentlevel = 0; } if (tok2->str() == "break") { if (scope && scope->type == Scope::eSwitch) { tok2 = const_cast(scope->bodyEnd); if (tok2 == endToken) break; --indentlevel; lowerToPossible(values); continue; } } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + ". noreturn conditional scope."); return false; } else if (indentlevel <= 0 && Token::Match(tok2, "return|throw|setjmp|longjmp")) returnStatement = true; else if (returnStatement && tok2->str() == ";") return false; // If a ? is seen and it's known that the condition is true/false.. else if (tok2->str() == "?") { if (subFunction && (astIsPointer(tok2->astOperand1()) || astIsIntegral(tok2->astOperand1(), false))) { tok2 = const_cast(nextAfterAstRightmostLeaf(tok2)); if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForwardVariable, skip ternary in subfunctions"); continue; } const Token *condition = tok2->astOperand1(); Token *op2 = tok2->astOperand2(); if (!condition || !op2) // Ticket #6713 continue; if (condition->hasKnownIntValue()) { const ValueFlow::Value &condValue = condition->values().front(); Token *expr = (condValue.intvalue != 0) ? op2->astOperand1() : op2->astOperand2(); for (const ValueFlow::Value &v : values) valueFlowAST(expr, varid, v, settings); if (isVariableChangedByFunctionCall(expr, 0, varid, settings, nullptr)) lowerToPossible(values, 0); if (isVariableChangedByFunctionCall(expr, 1, varid, settings, nullptr)) lowerToPossible(values, 1); } else { if (number_of_if >= 1) { // is variable used in conditional code? the value is not known if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForwardVariable, number_of_if"); return false; } for (const ValueFlow::Value &v : values) { const ProgramMemory programMemory(getProgramMemory(tok2, varid, v)); if (conditionIsTrue(condition, programMemory)) valueFlowAST(op2->astOperand1(), varid, v, settings); else if (conditionIsFalse(condition, programMemory)) valueFlowAST(op2->astOperand2(), varid, v, settings); else valueFlowAST(op2, varid, v, settings); } const Token * const expr0 = op2->astOperand1() ? op2->astOperand1() : tok2->astOperand1(); const Token * const expr1 = op2->astOperand2(); const std::pair startEnd0 = expr0->findExpressionStartEndTokens(); const std::pair startEnd1 = expr1->findExpressionStartEndTokens(); const bool changed0 = isVariableChanged(startEnd0.first, startEnd0.second->next(), varid, var->isGlobal(), settings, tokenlist->isCPP()); const bool changed1 = isVariableChanged(startEnd1.first, startEnd1.second->next(), varid, var->isGlobal(), settings, tokenlist->isCPP()); if (changed0 && changed1) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForwardVariable, changed in both : expressions"); return false; } if (changed0 || changed1) lowerToPossible(values); } // Skip conditional expressions.. const Token * const questionToken = tok2; while (tok2->astOperand1() || tok2->astOperand2()) { if (tok2->astOperand2()) tok2 = tok2->astOperand2(); else if (tok2->isUnaryPreOp()) tok2 = tok2->astOperand1(); else break; } tok2 = tok2->next(); if (isVariableChanged(questionToken, questionToken->astOperand2(), varid, false, settings, tokenlist->isCPP()) && isVariableChanged(questionToken->astOperand2(), tok2, varid, false, settings, tokenlist->isCPP())) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " valueFlowForwardVariable, assignment in condition"); return false; } } else if (tok2->varId() == varid) { // compound assignment, known value in rhs if (Token::Match(tok2->previous(), "!!* %name% %assign%") && tok2->next()->str() != "=" && tok2->next()->astOperand2() && tok2->next()->astOperand2()->hasKnownIntValue()) { const ValueFlow::Value &rhsValue = tok2->next()->astOperand2()->values().front(); const std::string &assign = tok2->next()->str(); std::list::iterator it; // Erase values that are not int values.. for (it = values.begin(); it != values.end();) { if (!evalAssignment(*it, assign, rhsValue)) { it = values.erase(it); } else { const std::string info("Compound assignment '" + assign + "', assigned value is " + it->infoString()); it->errorPath.emplace_back(tok2, info); ++it; } } if (values.empty()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "compound assignment of " + tok2->str()); return false; } } // bailout: assignment else if (Token::Match(tok2->previous(), "!!* %name% %assign%")) { // simplify rhs std::stack rhs; rhs.push(tok2->next()->astOperand2()); while (!rhs.empty()) { Token *rtok = rhs.top(); rhs.pop(); if (!rtok) continue; if (rtok->str() == "(" && Token::Match(rtok->astOperand1(), "sizeof|typeof|typeid")) continue; if (Token::Match(rtok, "++|--|?|:|;|,")) continue; if (rtok->varId() == varid) { for (const ValueFlow::Value &v : values) setTokenValue(rtok, v, settings); } rhs.push(rtok->astOperand1()); rhs.push(rtok->astOperand2()); } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str()); return false; } // bailout: possible assignment using >> if (isLikelyStreamRead(tokenlist->isCPP(), tok2->previous())) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "Possible assignment of " + tok2->str() + " using " + tok2->strAt(-1)); return false; } // skip if variable is conditionally used in ?: expression if (const Token *parent = skipValueInConditionalExpression(tok2)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "no simplification of " + tok2->str() + " within " + (Token::Match(parent,"[?:]") ? "?:" : parent->str()) + " expression"); const Token *astTop = parent->astTop(); if (Token::simpleMatch(astTop->astOperand1(), "for (")) tok2 = astTop->link(); // bailout if address of var is taken.. if (tok2->astParent() && tok2->astParent()->isUnaryOp("&")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "Taking address of " + tok2->str()); return false; } continue; } { // Is variable usage protected by && || ?: const Token *tok3 = tok2; const Token *parent = tok3->astParent(); while (parent && !Token::Match(parent, "%oror%|&&|:")) { tok3 = parent; parent = parent->astParent(); } const bool conditional = parent && (parent->str() == ":" || parent->astOperand2() == tok3); for (const ValueFlow::Value &v : values) { if (!conditional || !v.conditional) setTokenValue(tok2, v, settings); } } // increment/decrement if (Token::Match(tok2->previous(), "++|-- %name%") || Token::Match(tok2, "%name% ++|--")) { std::list::iterator it; // Erase values that are not int values.. for (it = values.begin(); it != values.end();) { if (!it->isIntValue()) it = values.erase(it); else ++it; } if (values.empty()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "increment/decrement of " + tok2->str()); return false; } const bool pre = Token::Match(tok2->previous(), "++|--"); Token * const op = pre ? tok2->previous() : tok2->next(); const bool inc = (op->str() == "++"); // Perform increment/decrement.. for (it = values.begin(); it != values.end(); ++it) { if (!pre) setTokenValue(op, *it, settings); it->intvalue += (inc ? 1 : -1); if (pre) setTokenValue(op, *it, settings); const std::string info(tok2->str() + " is " + std::string(inc ? "incremented" : "decremented") + "', new value is " + it->infoString()); it->errorPath.emplace_back(tok2, info); } } // bailout if address of var is taken.. if (tok2->astParent() && tok2->astParent()->isUnaryOp("&")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "Taking address of " + tok2->str()); return false; } // bailout if reference is created.. if (tok2->astParent() && Token::Match(tok2->astParent()->tokAt(-2), "& %name% =")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "Reference of " + tok2->str()); return false; } // bailout if its stream.. if (isLikelyStream(tokenlist->isCPP(), tok2)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "Stream used: " + tok2->str()); return false; } // assigned by subfunction? for (int i:getIndirections(values)) { bool inconclusive = false; if (isVariableChangedByFunctionCall(tok2, i, settings, &inconclusive)) { values.remove_if([&](const ValueFlow::Value& v) { return v.indirect <= i; }); } if (inconclusive) lowerToInconclusive(values, settings, i); } if (values.empty()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction"); return false; } if (tok2->strAt(1) == "." && tok2->next()->originalName() != "->") { lowerToInconclusive(values, settings); if (!settings->inconclusive) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by member function"); return false; } } // Variable changed for (int i:getIndirections(values)) { // Remove uninitialized values if modified if (isVariableChanged(tok2, i, settings, tokenlist->isCPP())) values.remove_if([&](const ValueFlow::Value& v) { return v.isUninitValue() && v.indirect <= i; }); } } else if (isAliasOf(var, tok2, varid, values) && isVariableChanged(tok2, 0, settings, tokenlist->isCPP())) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "Alias variable was modified."); // Bail at the end of the statement if its in an assignment const Token * top = tok2->astTop(); if (Token::Match(top, "%assign%") && astHasToken(top->astOperand1(), tok2)) returnStatement = true; else return false; } // Lambda function if (Token::simpleMatch(tok2, "= [") && Token::simpleMatch(tok2->linkAt(1), "] (") && Token::simpleMatch(tok2->linkAt(1)->linkAt(1), ") {")) { const Token *bodyStart = tok2->linkAt(1)->linkAt(1)->next(); if (isVariableChanged(bodyStart, bodyStart->link(), varid, var->isGlobal(), settings, tokenlist->isCPP())) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "valueFlowForwardVariable, " + var->name() + " is changed in lambda function"); return false; } } } return true; } static const Token* parseBinaryIntOp(const Token* expr, MathLib::bigint& known) { if (!expr) return nullptr; if (!expr->astOperand1() || !expr->astOperand2()) return nullptr; const Token* knownTok = nullptr; const Token* varTok = nullptr; if (expr->astOperand1()->hasKnownIntValue() && !expr->astOperand2()->hasKnownIntValue()) { varTok = expr->astOperand2(); knownTok = expr->astOperand1(); } else if (expr->astOperand2()->hasKnownIntValue() && !expr->astOperand1()->hasKnownIntValue()) { varTok = expr->astOperand1(); knownTok = expr->astOperand2(); } if (knownTok) known = knownTok->values().front().intvalue; return varTok; } template void transformIntValues(std::list& values, F f) { std::transform(values.begin(), values.end(), values.begin(), [&](ValueFlow::Value x) { if (x.isIntValue()) x.intvalue = f(x.intvalue); return x; }); } static const Token* solveExprValues(const Token* expr, std::list& values) { MathLib::bigint intval; const Token* binaryTok = parseBinaryIntOp(expr, intval); if (binaryTok && expr->str().size() == 1) { switch (expr->str()[0]) { case '+': { transformIntValues(values, [&](MathLib::bigint x) { return x - intval; }); return solveExprValues(binaryTok, values); } case '*': { if (intval == 0) break; transformIntValues(values, [&](MathLib::bigint x) { return x / intval; }); return solveExprValues(binaryTok, values); } case '^': { transformIntValues(values, [&](MathLib::bigint x) { return x ^ intval; }); return solveExprValues(binaryTok, values); } } } return expr; } static void valueFlowForward(Token* startToken, const Token* endToken, const Token* exprTok, std::list values, const bool constValue, const bool subFunction, TokenList* const tokenlist, ErrorLogger* const errorLogger, const Settings* settings) { const Token* expr = solveExprValues(exprTok, values); if (Token::Match(expr, "%var%") && expr->variable()) { valueFlowForwardVariable(startToken, endToken, expr->variable(), expr->varId(), values, constValue, subFunction, tokenlist, errorLogger, settings); } else { valueFlowForwardExpression(startToken, endToken, expr, values, tokenlist, settings); } } static std::vector findReturns(const Function* f) { std::vector result; const Scope* scope = f->functionScope; if (!scope) return result; for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { if (tok->str() == "{" && tok->scope() && (tok->scope()->type == Scope::eLambda || tok->scope()->type == Scope::eClass)) { tok = tok->link(); continue; } if (Token::simpleMatch(tok->astParent(), "return")) { result.push_back(tok); } // Skip lambda functions since the scope may not be set correctly const Token* lambdaEndToken = findLambdaEndToken(tok); if (lambdaEndToken) { tok = lambdaEndToken; } } return result; } static int getArgumentPos(const Variable *var, const Function *f) { auto arg_it = std::find_if(f->argumentList.begin(), f->argumentList.end(), [&](const Variable &v) { return v.nameToken() == var->nameToken(); }); if (arg_it == f->argumentList.end()) return -1; return std::distance(f->argumentList.begin(), arg_it); } std::string lifetimeType(const Token *tok, const ValueFlow::Value *val) { std::string result; if (!val) return "object"; switch (val->lifetimeKind) { case ValueFlow::Value::LifetimeKind::Lambda: result = "lambda"; break; case ValueFlow::Value::LifetimeKind::Iterator: result = "iterator"; break; case ValueFlow::Value::LifetimeKind::Object: case ValueFlow::Value::LifetimeKind::Address: if (astIsPointer(tok)) result = "pointer"; else result = "object"; break; } return result; } std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ErrorPath &errorPath) { const Token *tokvalue = val ? val->tokvalue : nullptr; const Variable *tokvar = tokvalue ? tokvalue->variable() : nullptr; const Token *vartok = tokvar ? tokvar->nameToken() : nullptr; std::string type = lifetimeType(tok, val); std::string msg = type; if (vartok) { errorPath.emplace_back(vartok, "Variable created here."); const Variable * var = vartok->variable(); if (var) { switch (val->lifetimeKind) { case ValueFlow::Value::LifetimeKind::Object: case ValueFlow::Value::LifetimeKind::Address: if (type == "pointer") msg += " to local variable"; else msg += " that points to local variable"; break; case ValueFlow::Value::LifetimeKind::Lambda: msg += " that captures local variable"; break; case ValueFlow::Value::LifetimeKind::Iterator: msg += " to local container"; break; } msg += " '" + var->name() + "'"; } } return msg; } ValueFlow::Value getLifetimeObjValue(const Token *tok) { ValueFlow::Value result; auto pred = [](const ValueFlow::Value &v) { if (!v.isLocalLifetimeValue()) return false; if (v.isInconclusive()) return false; if (!v.tokvalue->variable()) return false; return true; }; auto it = std::find_if(tok->values().begin(), tok->values().end(), pred); if (it == tok->values().end()) return result; result = *it; // There should only be one lifetime if (std::find_if(std::next(it), tok->values().end(), pred) != tok->values().end()) return result; return result; } std::vector getLifetimeTokens(const Token* tok, ValueFlow::Value::ErrorPath errorPath, int depth) { if (!tok) return std::vector {}; const Variable *var = tok->variable(); if (depth < 0) return {{tok, std::move(errorPath)}}; if (var && var->declarationId() == tok->varId()) { if (var->isReference() || var->isRValueReference()) { if (!var->declEndToken()) return {{tok, true, std::move(errorPath)}}; if (var->isArgument()) { errorPath.emplace_back(var->declEndToken(), "Passed to reference."); return {{tok, true, std::move(errorPath)}}; } else if (Token::simpleMatch(var->declEndToken(), "=")) { errorPath.emplace_back(var->declEndToken(), "Assigned to reference."); const Token *vartok = var->declEndToken()->astOperand2(); if (vartok == tok || (var->isConst() && isTemporary(true, vartok, nullptr))) return {{tok, true, std::move(errorPath)}}; if (vartok) return getLifetimeTokens(vartok, std::move(errorPath), depth - 1); } else { return std::vector {}; } } } else if (Token::Match(tok->previous(), "%name% (")) { const Function *f = tok->previous()->function(); if (f) { if (!Function::returnsReference(f)) return {{tok, std::move(errorPath)}}; std::vector result; std::vector returns = findReturns(f); for (const Token* returnTok : returns) { if (returnTok == tok) continue; for (LifetimeToken& lt : getLifetimeTokens(returnTok, std::move(errorPath), depth - 1)) { const Token* argvarTok = lt.token; const Variable* argvar = argvarTok->variable(); if (!argvar) continue; if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) { int n = getArgumentPos(argvar, f); if (n < 0) return std::vector {}; const Token* argTok = getArguments(tok->previous()).at(n); lt.errorPath.emplace_back(returnTok, "Return reference."); lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->str() + "'."); std::vector arglts = LifetimeToken::setInconclusive( getLifetimeTokens(argTok, std::move(lt.errorPath), depth - 1), returns.size() > 1); result.insert(result.end(), arglts.begin(), arglts.end()); } } } return result; } else if (Token::Match(tok->tokAt(-2), ". %name% (") && astIsContainer(tok->tokAt(-2)->astOperand1())) { const Library::Container* library = getLibraryContainer(tok->tokAt(-2)->astOperand1()); Library::Container::Yield y = library->getYield(tok->previous()->str()); if (y == Library::Container::Yield::AT_INDEX || y == Library::Container::Yield::ITEM) { errorPath.emplace_back(tok->previous(), "Accessing container."); return LifetimeToken::setAddressOf( getLifetimeTokens(tok->tokAt(-2)->astOperand1(), std::move(errorPath), depth - 1), false); } } } else if (Token::Match(tok, ".|::|[")) { const Token *vartok = tok; while (vartok) { if (vartok->str() == "[" || vartok->originalName() == "->") vartok = vartok->astOperand1(); else if (vartok->str() == "." || vartok->str() == "::") vartok = vartok->astOperand2(); else break; } if (!vartok) return {{tok, std::move(errorPath)}}; const Variable *tokvar = vartok->variable(); if (!astIsContainer(vartok) && !(tokvar && tokvar->isArray() && !tokvar->isArgument()) && (Token::Match(vartok->astParent(), "[|*") || vartok->astParent()->originalName() == "->")) { for (const ValueFlow::Value &v : vartok->values()) { if (!v.isLocalLifetimeValue()) continue; errorPath.insert(errorPath.end(), v.errorPath.begin(), v.errorPath.end()); return getLifetimeTokens(v.tokvalue, std::move(errorPath)); } } else { return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, std::move(errorPath)), !(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "["))); } } return {{tok, std::move(errorPath)}}; } static const Token* getLifetimeToken(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr) { std::vector lts = getLifetimeTokens(tok); if (lts.size() != 1) return nullptr; if (lts.front().inconclusive) return nullptr; if (addressOf) *addressOf = lts.front().addressOf; errorPath.insert(errorPath.end(), lts.front().errorPath.begin(), lts.front().errorPath.end()); return lts.front().token; } const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf) { const Token* tok2 = getLifetimeToken(tok, errorPath, addressOf); if (tok2 && tok2->variable()) return tok2->variable(); return nullptr; } static bool isNotLifetimeValue(const ValueFlow::Value& val) { return !val.isLifetimeValue(); } static bool isLifetimeOwned(const ValueType *vt, const ValueType *vtParent) { if (!vtParent) return false; if (!vt) { if (vtParent->type == ValueType::CONTAINER) return true; return false; } if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE) { if (vt->pointer != vtParent->pointer) return true; if (vt->type != vtParent->type) { if (vtParent->type == ValueType::RECORD) return true; if (vtParent->type == ValueType::CONTAINER) return true; } } return false; } static bool isLifetimeBorrowed(const ValueType *vt, const ValueType *vtParent) { if (!vtParent) return false; if (!vt) return false; if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE) { if (vtParent->pointer > vt->pointer) return true; if (vtParent->pointer < vt->pointer && vtParent->isIntegral()) return true; } return false; } bool isLifetimeBorrowed(const Token *tok, const Settings *settings) { if (!tok) return true; if (Token::simpleMatch(tok, ",")) return true; if (!tok->astParent()) return true; if (!Token::Match(tok->astParent()->previous(), "%name% (") && !Token::simpleMatch(tok->astParent(), ",")) { if (!Token::simpleMatch(tok, "{")) { const ValueType *vt = tok->valueType(); const ValueType *vtParent = tok->astParent()->valueType(); if (isLifetimeBorrowed(vt, vtParent)) return true; if (isLifetimeOwned(vt, vtParent)) return false; } const Type *t = Token::typeOf(tok); const Type *parentT = Token::typeOf(tok->astParent()); if (t && parentT && t->classDef && parentT->classDef && t->classDef != parentT->classDef) { return false; } } else if (Token::Match(tok->astParent()->tokAt(-3), "%var% . push_back|push_front|insert|push (") && astIsContainer(tok->astParent()->tokAt(-3))) { const ValueType *vt = tok->valueType(); const ValueType *vtCont = tok->astParent()->tokAt(-3)->valueType(); if (!vtCont->containerTypeToken) return true; ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, settings); if (isLifetimeBorrowed(vt, &vtParent)) return true; if (isLifetimeOwned(vt, &vtParent)) return false; } return true; } static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings); static void valueFlowLifetimeConstructor(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings); static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { Token *parent = tok->astParent(); while (parent && (parent->isArithmeticalOp() || parent->str() == ",")) parent = parent->astParent(); if (!parent) return; // Assignment if (parent->str() == "=" && (!parent->astParent() || Token::simpleMatch(parent->astParent(), ";"))) { const Variable *var = getLHSVariable(parent); if (!var) return; const Token * endOfVarScope = nullptr; if (!var->isLocal()) endOfVarScope = tok->scope()->bodyEnd; else endOfVarScope = var->typeStartToken()->scope()->bodyEnd; // Rhs values.. if (!parent->astOperand2() || parent->astOperand2()->values().empty()) return; if (!isLifetimeBorrowed(parent->astOperand2(), settings)) return; std::list values = parent->astOperand2()->values(); // Static variable initialisation? if (var->isStatic() && var->nameToken() == parent->astOperand1()) lowerToPossible(values); // Skip RHS const Token *nextExpression = nextAfterAstRightmostLeaf(parent); // Only forward lifetime values values.remove_if(&isNotLifetimeValue); valueFlowForwardVariable(const_cast(nextExpression), endOfVarScope, var, var->declarationId(), values, false, false, tokenlist, errorLogger, settings); if (tok->astTop() && Token::simpleMatch(tok->astTop()->previous(), "for (") && Token::simpleMatch(tok->astTop()->link(), ") {")) { Token *start = tok->astTop()->link()->next(); valueFlowForwardVariable( start, start->link(), var, var->declarationId(), values, false, false, tokenlist, errorLogger, settings); } // Constructor } else if (Token::simpleMatch(parent, "{") && !isScopeBracket(parent)) { valueFlowLifetimeConstructor(parent, tokenlist, errorLogger, settings); valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings); // Function call } else if (Token::Match(parent->previous(), "%name% (")) { valueFlowLifetimeFunction(parent->previous(), tokenlist, errorLogger, settings); valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings); // Variable } else if (tok->variable()) { const Variable *var = tok->variable(); if (!var->typeStartToken() || !var->typeStartToken()->scope()) return; const Token *endOfVarScope = var->typeStartToken()->scope()->bodyEnd; std::list values = tok->values(); const Token *nextExpression = nextAfterAstRightmostLeaf(parent); // Only forward lifetime values values.remove_if(&isNotLifetimeValue); valueFlowForwardVariable(const_cast(nextExpression), endOfVarScope, var, var->declarationId(), values, false, false, tokenlist, errorLogger, settings); } } struct LifetimeStore { const Token *argtok; std::string message; ValueFlow::Value::LifetimeKind type; ErrorPath errorPath; bool inconclusive; LifetimeStore() : argtok(nullptr), message(), type(), errorPath(), inconclusive(false) {} LifetimeStore(const Token *argtok, const std::string &message, ValueFlow::Value::LifetimeKind type = ValueFlow::Value::LifetimeKind::Object, bool inconclusive = false) : argtok(argtok), message(message), type(type), errorPath(), inconclusive(inconclusive) {} static LifetimeStore fromFunctionArg(const Function * f, Token *tok, const Variable *var, TokenList *tokenlist, ErrorLogger *errorLogger) { if (!var) return LifetimeStore{}; if (!var->isArgument()) return LifetimeStore{}; int n = getArgumentPos(var, f); if (n < 0) return LifetimeStore{}; std::vector args = getArguments(tok); if (n >= args.size()) { if (tokenlist->getSettings()->debugwarnings) bailout(tokenlist, errorLogger, tok, "Argument mismatch: Function '" + tok->str() + "' returning lifetime from argument index " + std::to_string(n) + " but only " + std::to_string(args.size()) + " arguments are available."); return LifetimeStore{}; } const Token *argtok2 = args[n]; return LifetimeStore{argtok2, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}; } template void byRef(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { if (!settings->inconclusive && inconclusive) return; if (!argtok) return; for (const LifetimeToken& lt : getLifetimeTokens(argtok)) { if (!settings->inconclusive && lt.inconclusive) continue; ErrorPath er = errorPath; er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end()); if (!lt.token) return; if (!pred(lt.token)) return; er.emplace_back(argtok, message); ValueFlow::Value value; value.valueType = ValueFlow::Value::LIFETIME; value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; value.tokvalue = lt.token; value.errorPath = std::move(er); value.lifetimeKind = type; value.setInconclusive(lt.inconclusive || inconclusive); // Don't add the value a second time if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) return; setTokenValue(tok, value, tokenlist->getSettings()); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } } void byRef(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { byRef(tok, tokenlist, errorLogger, settings, [](const Token *) { return true; }); } template void byVal(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { if (!settings->inconclusive && inconclusive) return; if (!argtok) return; if (argtok->values().empty()) { ErrorPath er; er.emplace_back(argtok, message); const Variable *var = getLifetimeVariable(argtok, er); if (var && var->isArgument()) { ValueFlow::Value value; value.valueType = ValueFlow::Value::LIFETIME; value.lifetimeScope = ValueFlow::Value::LifetimeScope::Argument; value.tokvalue = var->nameToken(); value.errorPath = er; value.lifetimeKind = type; value.setInconclusive(inconclusive); // Don't add the value a second time if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) return; setTokenValue(tok, value, tokenlist->getSettings()); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } } for (const ValueFlow::Value &v : argtok->values()) { if (!v.isLifetimeValue()) continue; const Token *tok3 = v.tokvalue; for (const LifetimeToken& lt : getLifetimeTokens(tok3)) { if (!settings->inconclusive && lt.inconclusive) continue; ErrorPath er = v.errorPath; er.insert(er.end(), lt.errorPath.begin(), lt.errorPath.end()); if (!lt.token) return; if (!pred(lt.token)) return; er.emplace_back(argtok, message); er.insert(er.end(), errorPath.begin(), errorPath.end()); ValueFlow::Value value; value.valueType = ValueFlow::Value::LIFETIME; value.lifetimeScope = v.lifetimeScope; value.tokvalue = lt.token; value.errorPath = std::move(er); value.lifetimeKind = type; value.setInconclusive(lt.inconclusive || v.isInconclusive() || inconclusive); // Don't add the value a second time if (std::find(tok->values().begin(), tok->values().end(), value) != tok->values().end()) continue; setTokenValue(tok, value, tokenlist->getSettings()); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } } } void byVal(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { byVal(tok, tokenlist, errorLogger, settings, [](const Token *) { return true; }); } template void byDerefCopy(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings, Predicate pred) const { if (!settings->inconclusive && inconclusive) return; if (!argtok) return; for (const ValueFlow::Value &v : argtok->values()) { if (!v.isLifetimeValue()) continue; const Token *tok2 = v.tokvalue; ErrorPath er = v.errorPath; const Variable *var = getLifetimeVariable(tok2, er); er.insert(er.end(), errorPath.begin(), errorPath.end()); if (!var) continue; for (const Token *tok3 = tok; tok3 && tok3 != var->declEndToken(); tok3 = tok3->previous()) { if (tok3->varId() == var->declarationId()) { LifetimeStore{tok3, message, type, inconclusive} .byVal(tok, tokenlist, errorLogger, settings, pred); break; } } } } void byDerefCopy(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) const { byDerefCopy(tok, tokenlist, errorLogger, settings, [](const Token *) { return true; }); } }; static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { if (!Token::Match(tok, "%name% (")) return; if (Token::Match(tok->tokAt(-2), "std :: ref|cref|tie|front_inserter|back_inserter")) { for (const Token *argtok : getArguments(tok)) { LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byRef( tok->next(), tokenlist, errorLogger, settings); } } else if (Token::Match(tok->tokAt(-2), "std :: make_tuple|tuple_cat|make_pair|make_reverse_iterator|next|prev|move")) { for (const Token *argtok : getArguments(tok)) { LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byVal( tok->next(), tokenlist, errorLogger, settings); } } else if (Token::Match(tok->tokAt(-2), "%var% . push_back|push_front|insert|push|assign") && astIsContainer(tok->tokAt(-2))) { Token *vartok = tok->tokAt(-2); std::vector args = getArguments(tok); std::size_t n = args.size(); if (n > 1 && Token::typeStr(args[n - 2]) == Token::typeStr(args[n - 1]) && (((astIsIterator(args[n - 2]) && astIsIterator(args[n - 1])) || (astIsPointer(args[n - 2]) && astIsPointer(args[n - 1]))))) { LifetimeStore{args.back(), "Added to container '" + vartok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byDerefCopy( vartok, tokenlist, errorLogger, settings); } else if (!args.empty() && isLifetimeBorrowed(args.back(), settings)) { LifetimeStore{args.back(), "Added to container '" + vartok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} .byVal( vartok, tokenlist, errorLogger, settings); } } else if (tok->function()) { const Function *f = tok->function(); if (Function::returnsReference(f)) return; std::vector returns = findReturns(f); const bool inconclusive = returns.size() > 1; for (const Token* returnTok : returns) { if (returnTok == tok) continue; const Variable *returnVar = returnTok->variable(); if (returnVar && returnVar->isArgument() && (returnVar->isConst() || !isVariableChanged(returnVar, settings, tokenlist->isCPP()))) { LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, returnVar, tokenlist, errorLogger); ls.inconclusive = inconclusive; ls.byVal(tok->next(), tokenlist, errorLogger, settings); } for (const ValueFlow::Value &v : returnTok->values()) { if (!v.isLifetimeValue()) continue; if (!v.tokvalue) continue; const Variable *var = v.tokvalue->variable(); LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, var, tokenlist, errorLogger); if (!ls.argtok) continue; ls.inconclusive = inconclusive; ls.errorPath = v.errorPath; ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + "."); if (var->isReference() || var->isRValueReference()) { ls.byRef(tok->next(), tokenlist, errorLogger, settings); } else if (v.isArgumentLifetimeValue()) { ls.byVal(tok->next(), tokenlist, errorLogger, settings); } } } } } static void valueFlowLifetimeConstructor(Token* tok, const Type* t, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) { if (!t) return; if (!Token::Match(tok, "(|{")) return; const Scope* scope = t->classScope; if (!scope) return; // Only support aggregate constructors for now if (scope->numConstructors == 0 && t->derivedFrom.empty() && (t->isClassType() || t->isStructType())) { std::vector args = getArguments(tok); std::size_t i = 0; for (const Variable& var : scope->varlist) { if (i >= args.size()) break; const Token* argtok = args[i]; LifetimeStore ls{ argtok, "Passed to constructor of '" + t->name() + "'.", ValueFlow::Value::LifetimeKind::Object}; if (var.isReference() || var.isRValueReference()) { ls.byRef(tok, tokenlist, errorLogger, settings); } else { ls.byVal(tok, tokenlist, errorLogger, settings); } i++; } } } static bool hasInitList(const Token* tok) { if (astIsPointer(tok)) return true; if (astIsContainer(tok)) { const Library::Container * library = getLibraryContainer(tok); if (!library) return false; return library->hasInitializerListConstructor; } return false; } static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) { if (!Token::Match(tok, "(|{")) return; Token* parent = tok->astParent(); while (Token::simpleMatch(parent, ",")) parent = parent->astParent(); if (Token::simpleMatch(parent, "{") && hasInitList(parent->astParent())) { valueFlowLifetimeConstructor(tok, Token::typeOf(parent->previous()), tokenlist, errorLogger, settings); } else if (Token::simpleMatch(tok, "{") && hasInitList(parent)) { std::vector args = getArguments(tok); for (const Token *argtok : args) { LifetimeStore ls{argtok, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object}; ls.byVal(tok, tokenlist, errorLogger, settings); } } else if (const Type* t = Token::typeOf(tok->previous())) { valueFlowLifetimeConstructor(tok, t, tokenlist, errorLogger, settings); } } struct Lambda { explicit Lambda(const Token * tok) : capture(nullptr), arguments(nullptr), returnTok(nullptr), bodyTok(nullptr) { if (!Token::simpleMatch(tok, "[") || !tok->link()) return; capture = tok; if (Token::simpleMatch(capture->link(), "] (")) { arguments = capture->link()->next(); } const Token * afterArguments = arguments ? arguments->link()->next() : capture->link()->next(); if (afterArguments && afterArguments->originalName() == "->") { returnTok = afterArguments->next(); bodyTok = Token::findsimplematch(returnTok, "{"); } else if (Token::simpleMatch(afterArguments, "{")) { bodyTok = afterArguments; } } const Token * capture; const Token * arguments; const Token * returnTok; const Token * bodyTok; bool isLambda() const { return capture && bodyTok; } }; static bool isDecayedPointer(const Token *tok) { if (!tok) return false; if (astIsPointer(tok->astParent()) && !Token::simpleMatch(tok->astParent(), "return")) return true; if (Token::Match(tok->astParent(), "%cop%")) return true; if (!Token::simpleMatch(tok->astParent(), "return")) return false; return astIsPointer(tok->astParent()); } static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->scope()) continue; if (tok->scope()->type == Scope::eGlobal) continue; Lambda lam(tok); // Lamdas if (lam.isLambda()) { const Scope * bodyScope = lam.bodyTok->scope(); std::set scopes; auto isCapturingVariable = [&](const Token *varTok) { const Variable *var = varTok->variable(); if (!var) return false; if (!var->isLocal() && !var->isArgument()) return false; const Scope *scope = var->scope(); if (!scope) return false; if (scopes.count(scope) > 0) return false; if (scope->isNestedIn(bodyScope)) return false; scopes.insert(scope); return true; }; // TODO: Handle explicit capture bool captureByRef = Token::Match(lam.capture, "[ & ]"); bool captureByValue = Token::Match(lam.capture, "[ = ]"); 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); } } } // address of else if (tok->isUnaryOp("&")) { for (const LifetimeToken& lt : getLifetimeTokens(tok->astOperand1())) { if (!settings->inconclusive && lt.inconclusive) continue; ErrorPath errorPath = lt.errorPath; errorPath.emplace_back(tok, "Address of variable taken here."); ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; value.tokvalue = lt.token; value.errorPath = std::move(errorPath); if (astIsPointer(lt.token) || !Token::Match(lt.token->astParent(), ".|[")) value.lifetimeKind = ValueFlow::Value::LifetimeKind::Address; value.setInconclusive(lt.inconclusive); setTokenValue(tok, value, tokenlist->getSettings()); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } } // container lifetimes else if (astIsContainer(tok)) { Token * parent = astParentSkipParens(tok); if (!Token::Match(parent, ". %name% (")) continue; LifetimeStore ls; if (astIsIterator(parent->tokAt(2))) ls = LifetimeStore{tok, "Iterator to container is created here.", ValueFlow::Value::LifetimeKind::Iterator}; else if (astIsPointer(parent->tokAt(2)) || Token::Match(parent->next(), "data|c_str")) ls = LifetimeStore{tok, "Pointer to container is created here.", ValueFlow::Value::LifetimeKind::Object}; else continue; // Dereferencing if (tok->isUnaryOp("*") || parent->originalName() == "->") ls.byDerefCopy(parent->tokAt(2), tokenlist, errorLogger, settings); else ls.byRef(parent->tokAt(2), tokenlist, errorLogger, settings); } // Check constructors else if (Token::Match(tok, "=|return|%type%|%var% {")) { valueFlowLifetimeConstructor(tok->next(), tokenlist, errorLogger, settings); } // Check function calls else if (Token::Match(tok, "%name% (")) { valueFlowLifetimeFunction(tok, tokenlist, errorLogger, settings); } // Check variables else if (tok->variable()) { ErrorPath errorPath; const Variable * var = getLifetimeVariable(tok, errorPath); if (!var) continue; if (var->nameToken() == tok) continue; if (var->isArray() && !var->isStlType() && !var->isArgument() && isDecayedPointer(tok)) { errorPath.emplace_back(tok, "Array decayed to pointer here."); ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::LIFETIME; value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; value.tokvalue = var->nameToken(); value.errorPath = errorPath; setTokenValue(tok, value, tokenlist->getSettings()); valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); } } } } static bool isStdMoveOrStdForwarded(Token * tok, ValueFlow::Value::MoveKind * moveKind, Token ** varTok = nullptr) { if (tok->str() != "std") return false; ValueFlow::Value::MoveKind kind = ValueFlow::Value::MoveKind::NonMovedVariable; Token * variableToken = nullptr; if (Token::Match(tok, "std :: move ( %var% )")) { variableToken = tok->tokAt(4); kind = ValueFlow::Value::MoveKind::MovedVariable; } else if (Token::simpleMatch(tok, "std :: forward <")) { const Token * const leftAngle = tok->tokAt(3); Token * rightAngle = leftAngle->link(); if (Token::Match(rightAngle, "> ( %var% )")) { variableToken = rightAngle->tokAt(2); kind = ValueFlow::Value::MoveKind::ForwardedVariable; } } if (!variableToken) return false; if (variableToken->strAt(2) == ".") // Only partially moved return false; if (variableToken->valueType() && variableToken->valueType()->type >= ValueType::Type::VOID) return false; if (moveKind != nullptr) *moveKind = kind; if (varTok != nullptr) *varTok = variableToken; return true; } static bool isOpenParenthesisMemberFunctionCallOfVarId(const Token * openParenthesisToken, nonneg int varId) { const Token * varTok = openParenthesisToken->tokAt(-3); return Token::Match(varTok, "%varid% . %name% (", varId) && varTok->next()->originalName() == emptyString; } static const Token * findOpenParentesisOfMove(const Token * moveVarTok) { const Token * tok = moveVarTok; while (tok && tok->str() != "(") tok = tok->previous(); return tok; } static const Token * findEndOfFunctionCallForParameter(const Token * parameterToken) { if (!parameterToken) return nullptr; const Token * parent = parameterToken->astParent(); while (parent && !parent->isOp() && parent->str() != "(") parent = parent->astParent(); if (!parent) return nullptr; return nextAfterAstRightmostLeaf(parent); } static void valueFlowAfterMove(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { if (!tokenlist->isCPP() || settings->standards.cpp < Standards::CPP11) return; for (const Scope * scope : symboldatabase->functionScopes) { if (!scope) continue; const Token * start = scope->bodyStart; if (scope->function) { const Token * memberInitializationTok = scope->function->constructorMemberInitialization(); if (memberInitializationTok) start = memberInitializationTok; } for (Token* tok = const_cast(start); tok != scope->bodyEnd; tok = tok->next()) { Token * varTok; if (Token::Match(tok, "%var% . reset|clear (") && tok->next()->originalName() == emptyString) { varTok = tok; ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::MOVED; value.moveKind = ValueFlow::Value::MoveKind::NonMovedVariable; value.errorPath.emplace_back(tok, "Calling " + tok->next()->expressionString() + " makes " + tok->str() + " 'non-moved'"); value.setKnown(); std::list values; values.push_back(value); const Variable *var = varTok->variable(); if (!var || (!var->isLocal() && !var->isArgument())) continue; const int varId = varTok->varId(); const Token * const endOfVarScope = var->typeStartToken()->scope()->bodyEnd; setTokenValue(varTok, value, settings); valueFlowForwardVariable( varTok->next(), endOfVarScope, var, varId, values, false, false, tokenlist, errorLogger, settings); continue; } ValueFlow::Value::MoveKind moveKind; if (!isStdMoveOrStdForwarded(tok, &moveKind, &varTok)) continue; const int varId = varTok->varId(); // x is not MOVED after assignment if code is: x = ... std::move(x) .. ; const Token *parent = tok->astParent(); while (parent && parent->str() != "=" && parent->str() != "return" && !(parent->str() == "(" && isOpenParenthesisMemberFunctionCallOfVarId(parent, varId))) parent = parent->astParent(); if (parent && (parent->str() == "return" || // MOVED in return statement parent->str() == "(")) // MOVED in self assignment, isOpenParenthesisMemberFunctionCallOfVarId == true continue; if (parent && parent->astOperand1() && parent->astOperand1()->varId() == varId) continue; const Variable *var = varTok->variable(); if (!var) continue; const Token * const endOfVarScope = var->typeStartToken()->scope()->bodyEnd; ValueFlow::Value value; value.valueType = ValueFlow::Value::ValueType::MOVED; value.moveKind = moveKind; if (moveKind == ValueFlow::Value::MoveKind::MovedVariable) value.errorPath.emplace_back(tok, "Calling std::move(" + varTok->str() + ")"); else // if (moveKind == ValueFlow::Value::ForwardedVariable) value.errorPath.emplace_back(tok, "Calling std::forward(" + varTok->str() + ")"); value.setKnown(); std::list values; values.push_back(value); const Token * openParentesisOfMove = findOpenParentesisOfMove(varTok); const Token * endOfFunctionCall = findEndOfFunctionCallForParameter(openParentesisOfMove); if (endOfFunctionCall) valueFlowForwardVariable(const_cast(endOfFunctionCall), endOfVarScope, var, varId, values, false, false, tokenlist, errorLogger, settings); } } } static void valueFlowForwardAssign(Token * const tok, const Variable * const var, std::list values, const bool constValue, const bool init, TokenList * const tokenlist, ErrorLogger * const errorLogger, const Settings * const settings) { const Token * const endOfVarScope = var->typeStartToken()->scope()->bodyEnd; if (std::any_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) { valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); values.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue)); } if (!var->isPointer() && !var->isSmartPointer()) values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue)); if (tok->astParent()) { for (std::list::iterator it = values.begin(); it != values.end(); ++it) { const std::string info = "Assignment '" + tok->astParent()->expressionString() + "', assigned value is " + it->infoString(); it->errorPath.emplace_back(tok, info); } } if (tokenlist->isCPP() && Token::Match(var->typeStartToken(), "bool|_Bool")) { std::list::iterator it; for (it = values.begin(); it != values.end(); ++it) { if (it->isIntValue()) it->intvalue = (it->intvalue != 0); if (it->isTokValue()) it ->intvalue = (it->tokvalue != nullptr); } } // Static variable initialisation? if (var->isStatic() && init) lowerToPossible(values); // Skip RHS const Token * nextExpression = tok->astParent() ? nextAfterAstRightmostLeaf(tok->astParent()) : tok->next(); if (std::any_of(values.begin(), values.end(), std::mem_fn(&ValueFlow::Value::isTokValue))) { std::list tokvalues; std::copy_if(values.begin(), values.end(), std::back_inserter(tokvalues), std::mem_fn(&ValueFlow::Value::isTokValue)); valueFlowForwardVariable(const_cast(nextExpression), endOfVarScope, var, var->declarationId(), tokvalues, constValue, false, tokenlist, errorLogger, settings); values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue)); } for (ValueFlow::Value& value:values) value.tokvalue = tok; valueFlowForwardVariable(const_cast(nextExpression), endOfVarScope, var, var->declarationId(), values, constValue, false, tokenlist, errorLogger, settings); } static std::list truncateValues(std::list values, const ValueType *valueType, const Settings *settings) { if (!valueType || !valueType->isIntegral()) return values; const size_t sz = ValueFlow::getSizeOf(*valueType, settings); for (ValueFlow::Value &value : values) { if (value.isFloatValue()) { value.intvalue = value.floatValue; value.valueType = ValueFlow::Value::INT; } if (value.isIntValue() && sz > 0 && sz < 8) { const MathLib::biguint unsignedMaxValue = (1ULL << (sz * 8)) - 1ULL; const MathLib::biguint signBit = 1ULL << (sz * 8 - 1); value.intvalue &= unsignedMaxValue; if (valueType->sign == ValueType::Sign::SIGNED && (value.intvalue & signBit)) value.intvalue |= ~unsignedMaxValue; } } return values; } static bool isLiteralNumber(const Token *tok, bool cpp) { return tok->isNumber() || tok->str() == "NULL" || (cpp && Token::Match(tok, "false|true|nullptr")); } static void valueFlowAfterAssign(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope * scope : symboldatabase->functionScopes) { std::set aliased; for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { // Alias if (tok->isUnaryOp("&")) { aliased.insert(tok->astOperand1()->varId()); continue; } // Assignment if ((tok->str() != "=") || (tok->astParent())) continue; // Lhs should be a variable if (!tok->astOperand1() || !tok->astOperand1()->varId() || tok->astOperand1()->hasKnownValue()) continue; const int varid = tok->astOperand1()->varId(); if (aliased.find(varid) != aliased.end()) continue; const Variable *var = tok->astOperand1()->variable(); if (!var || (!var->isLocal() && !var->isGlobal() && !var->isArgument())) continue; // Rhs values.. if (!tok->astOperand2() || tok->astOperand2()->values().empty()) continue; std::list values = truncateValues(tok->astOperand2()->values(), tok->astOperand1()->valueType(), settings); const bool constValue = isLiteralNumber(tok->astOperand2(), tokenlist->isCPP()); const bool init = var->nameToken() == tok->astOperand1(); valueFlowForwardAssign(tok->astOperand2(), var, values, constValue, init, tokenlist, errorLogger, settings); } } } static void valueFlowSetConditionToKnown(const Token* tok, std::list& values, bool then) { if (values.empty()) return; if (then && !Token::Match(tok, "==|!|(")) return; if (!then && !Token::Match(tok, "!=|%var%|(")) return; if (isConditionKnown(tok, then)) changePossibleToKnown(values); } static bool isBreakScope(const Token* const endToken) { if (!Token::simpleMatch(endToken, "}")) return false; if (!Token::simpleMatch(endToken->link(), "{")) return false; return Token::findmatch(endToken->link(), "break|goto", endToken); } static ValueFlow::Value asImpossible(ValueFlow::Value v) { v.invertRange(); v.setImpossible(); return v; } static void insertImpossible(std::list& values, const std::list& input) { std::transform(input.begin(), input.end(), std::back_inserter(values), &asImpossible); } static std::vector getExprVariables(const Token* expr, const TokenList* tokenlist, const SymbolDatabase* symboldatabase, const Settings* settings) { std::vector result; FwdAnalysis fwdAnalysis(tokenlist->isCPP(), settings->library); std::set varids = fwdAnalysis.getExprVarIds(expr); std::transform(varids.begin(), varids.end(), std::back_inserter(result), [&](int id) { return symboldatabase->getVariableFromVarId(id); }); return result; } struct ValueFlowConditionHandler { struct Condition { const Token *vartok; std::list true_values; std::list false_values; Condition() : vartok(nullptr), true_values(), false_values() {} }; std::function& values, bool constValue)> forward; std::function parse; void afterCondition(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings) const { for (const Scope *scope : symboldatabase->functionScopes) { std::set aliased; for (Token *tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "if|while|for (")) continue; if (Token::Match(tok, "= & %var% ;")) aliased.insert(tok->tokAt(2)->varId()); const Token* top = tok->astTop(); if (!top) continue; if (!Token::Match(top->previous(), "if|while|for (") && !Token::Match(tok->astParent(), "&&|%oror%")) continue; Condition cond = parse(tok); if (!cond.vartok) continue; if (cond.true_values.empty() || cond.false_values.empty()) continue; if (exprDependsOnThis(cond.vartok)) continue; std::vector vars = getExprVariables(cond.vartok, tokenlist, symboldatabase, settings); if (std::any_of(vars.begin(), vars.end(), [](const Variable* var) { return !var; })) continue; if (!vars.empty() && (vars.front())) if (std::any_of(vars.begin(), vars.end(), [&](const Variable* var) { return var && aliased.find(var->declarationId()) != aliased.end(); })) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, cond.vartok, "variable is aliased so we just skip all valueflow after condition"); continue; } if (Token::Match(tok->astParent(), "%oror%|&&")) { Token *parent = tok->astParent(); const std::string &op(parent->str()); if (parent->astOperand1() == tok && ((op == "&&" && Token::Match(tok, "==|>=|<=|!")) || (op == "||" && Token::Match(tok, "%name%|!=")))) { for (; parent && parent->str() == op; parent = parent->astParent()) { std::stack tokens; tokens.push(parent->astOperand2()); bool assign = false; while (!tokens.empty()) { Token *rhstok = tokens.top(); tokens.pop(); if (!rhstok) continue; tokens.push(rhstok->astOperand1()); tokens.push(rhstok->astOperand2()); if (isSameExpression( tokenlist->isCPP(), false, cond.vartok, rhstok, settings->library, true, false)) setTokenValue(rhstok, cond.true_values.front(), settings); else if (Token::Match(rhstok, "++|--|=") && isSameExpression(tokenlist->isCPP(), false, cond.vartok, rhstok->astOperand1(), settings->library, true, false)) { assign = true; break; } } if (assign) break; while (parent->astParent() && parent == parent->astParent()->astOperand2()) parent = parent->astParent(); } } } if (top && Token::Match(top->previous(), "if|while (") && !top->previous()->isExpandedMacro()) { // does condition reassign variable? if (tok != top->astOperand2() && Token::Match(top->astOperand2(), "%oror%|&&") && isVariablesChanged(top, top->link(), 0, vars, settings, tokenlist->isCPP())) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok, "assignment in condition"); continue; } std::list thenValues; std::list elseValues; if (!Token::Match(tok, "!=|=") && tok != cond.vartok) { thenValues.insert(thenValues.end(), cond.true_values.begin(), cond.true_values.end()); if (isConditionKnown(tok, false)) insertImpossible(elseValues, cond.false_values); } if (!Token::Match(tok, "==|!")) { elseValues.insert(elseValues.end(), cond.false_values.begin(), cond.false_values.end()); if (isConditionKnown(tok, true)) insertImpossible(thenValues, cond.true_values); } // start token of conditional code Token* startTokens[] = {nullptr, nullptr}; // if astParent is "!" we need to invert codeblock { const Token *tok2 = tok; while (tok2->astParent()) { const Token *parent = tok2->astParent(); while (parent && parent->str() == "&&") parent = parent->astParent(); if (parent && (parent->str() == "!" || Token::simpleMatch(parent, "== false"))) { std::swap(thenValues, elseValues); } tok2 = parent; } } // determine startToken(s) if (Token::simpleMatch(top->link(), ") {")) startTokens[0] = top->link()->next(); if (Token::simpleMatch(top->link()->linkAt(1), "} else {")) startTokens[1] = top->link()->linkAt(1)->tokAt(2); int changeBlock = -1; for (int i = 0; i < 2; i++) { const Token *const startToken = startTokens[i]; if (!startToken) continue; std::list& values = (i == 0 ? thenValues : elseValues); valueFlowSetConditionToKnown(tok, values, i == 0); // TODO: The endToken should not be startTokens[i]->link() in the valueFlowForwardVariable call if (forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, true)) changeBlock = i; changeKnownToPossible(values); } // TODO: Values changed in noreturn blocks should not bail if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, startTokens[changeBlock]->link(), "valueFlowAfterCondition: " + cond.vartok->expressionString() + " is changed in conditional block"); continue; } // After conditional code.. if (Token::simpleMatch(top->link(), ") {")) { Token *after = top->link()->linkAt(1); std::string unknownFunction; if (settings->library.isScopeNoReturn(after, &unknownFunction)) { if (settings->debugwarnings && !unknownFunction.empty()) bailout(tokenlist, errorLogger, after, "possible noreturn scope"); continue; } bool dead_if = isReturnScope(after, &settings->library) || (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "while (") && !isBreakScope(after)); bool dead_else = false; if (Token::simpleMatch(after, "} else {")) { after = after->linkAt(2); if (Token::simpleMatch(after->tokAt(-2), ") ; }")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, after, "possible noreturn scope"); continue; } dead_else = isReturnScope(after, &settings->library); } if (dead_if && dead_else) continue; std::list values; if (dead_if) { values = elseValues; } else if (dead_else) { values = thenValues; } else { std::copy_if(thenValues.begin(), thenValues.end(), std::back_inserter(values), std::mem_fn(&ValueFlow::Value::isPossible)); std::copy_if(elseValues.begin(), elseValues.end(), std::back_inserter(values), std::mem_fn(&ValueFlow::Value::isPossible)); } if (!values.empty()) { if ((dead_if || dead_else) && !Token::Match(tok->astParent(), "&&|&")) { valueFlowSetConditionToKnown(tok, values, true); valueFlowSetConditionToKnown(tok, values, false); } // TODO: constValue could be true if there are no assignments in the conditional blocks and // perhaps if there are no && and no || in the condition bool constValue = false; forward(after, top->scope()->bodyEnd, cond.vartok, values, constValue); } } } } } } }; static void valueFlowAfterCondition(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { ValueFlowConditionHandler handler; handler.forward = [&](Token* start, const Token* stop, const Token* vartok, const std::list& values, bool constValue) { valueFlowForward(start->next(), stop, vartok, values, constValue, false, tokenlist, errorLogger, settings); std::vector vars = getExprVariables(vartok, tokenlist, symboldatabase, settings); return isVariablesChanged(start, stop, 0, vars, settings, tokenlist->isCPP()); }; handler.parse = [&](const Token *tok) { ValueFlowConditionHandler::Condition cond; ValueFlow::Value true_value; ValueFlow::Value false_value; const Token *vartok = parseCompareInt(tok, true_value, false_value); if (vartok) { if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2()) vartok = vartok->astOperand1(); cond.true_values.push_back(true_value); cond.false_values.push_back(false_value); cond.vartok = vartok; return cond; } if (tok->str() == "!") { vartok = tok->astOperand1(); } else if (tok->astParent() && (Token::Match(tok->astParent(), "%oror%|&&") || Token::Match(tok->astParent()->previous(), "if|while ("))) { if (Token::simpleMatch(tok, "=")) vartok = tok->astOperand1(); else if (!Token::Match(tok, "%comp%|%assign%")) vartok = tok; } if (!vartok) return cond; cond.true_values.emplace_back(tok, 0LL); cond.false_values.emplace_back(tok, 0LL); cond.vartok = vartok; return cond; }; handler.afterCondition(tokenlist, symboldatabase, errorLogger, settings); } static bool isInBounds(const ValueFlow::Value& value, MathLib::bigint x) { if (value.intvalue == x) return true; if (value.bound == ValueFlow::Value::Bound::Lower && value.intvalue > x) return false; if (value.bound == ValueFlow::Value::Bound::Upper && value.intvalue < x) return false; // Checking for equality is not necessary since we already know the value is not equal if (value.bound == ValueFlow::Value::Bound::Point) return false; return true; } static const ValueFlow::Value* proveNotEqual(const std::list& values, MathLib::bigint x) { const ValueFlow::Value* result = nullptr; for (const ValueFlow::Value& value : values) { if (value.valueType != ValueFlow::Value::INT) continue; if (result && !isInBounds(value, result->intvalue)) continue; if (value.isImpossible()) { if (value.intvalue == x) return &value; if (!isInBounds(value, x)) continue; result = &value; } else { if (value.intvalue == x) return nullptr; if (!isInBounds(value, x)) continue; result = nullptr; } } return result; } static void valueFlowInferCondition(TokenList* tokenlist, const Settings* settings) { for (Token* tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->astParent()) continue; if (tok->hasKnownValue()) continue; if (Token::Match(tok, "%var%") && (Token::Match(tok->astParent(), "?|&&|!|%oror%") || Token::Match(tok->astParent()->previous(), "if|while ("))) { const ValueFlow::Value* result = proveNotEqual(tok->values(), 0); if (!result) continue; ValueFlow::Value value = *result; value.intvalue = 1; value.setKnown(); setTokenValue(tok, value, settings); } else if (Token::Match(tok, "==|!=")) { MathLib::bigint val = 0; const Token* varTok = nullptr; if (tok->astOperand1()->hasKnownIntValue()) { val = tok->astOperand1()->values().front().intvalue; varTok = tok->astOperand2(); } else if (tok->astOperand2()->hasKnownIntValue()) { val = tok->astOperand2()->values().front().intvalue; varTok = tok->astOperand1(); } if (!varTok) continue; if (varTok->hasKnownIntValue()) continue; const ValueFlow::Value* result = proveNotEqual(varTok->values(), val); if (!result) continue; ValueFlow::Value value = *result; value.intvalue = tok->str() == "!="; value.setKnown(); setTokenValue(tok, value, settings); } } } static bool valueFlowForLoop1(const Token *tok, int * const varid, MathLib::bigint * const num1, MathLib::bigint * const num2, MathLib::bigint * const numAfter) { tok = tok->tokAt(2); if (!Token::Match(tok, "%type%| %var% =")) return false; const Token * const vartok = Token::Match(tok, "%var% =") ? tok : tok->next(); *varid = vartok->varId(); tok = vartok->tokAt(2); const Token * const num1tok = Token::Match(tok, "%num% ;") ? tok : nullptr; if (num1tok) *num1 = MathLib::toLongNumber(num1tok->str()); while (Token::Match(tok, "%name%|%num%|%or%|+|-|*|/|&|[|]|(")) tok = (tok->str() == "(") ? tok->link()->next() : tok->next(); if (!tok || tok->str() != ";") return false; tok = tok->next(); const Token *num2tok = nullptr; if (Token::Match(tok, "%varid% <|<=|!=", vartok->varId())) { tok = tok->next(); num2tok = tok->astOperand2(); if (num2tok && num2tok->str() == "(" && !num2tok->astOperand2()) num2tok = num2tok->astOperand1(); if (!Token::Match(num2tok, "%num% ;|%oror%")) // TODO: || enlarges the scope of the condition, so it should not cause FP, but it should no lnger be part of this pattern as soon as valueFlowForLoop2 can handle an unknown RHS of || better num2tok = nullptr; } if (!num2tok) return false; *num2 = MathLib::toLongNumber(num2tok->str()) - ((tok->str()=="<=") ? 0 : 1); *numAfter = *num2 + 1; if (!num1tok) *num1 = *num2; while (tok && tok->str() != ";") tok = tok->next(); if (!Token::Match(tok, "; %varid% ++ ) {", vartok->varId()) && !Token::Match(tok, "; ++ %varid% ) {", vartok->varId())) return false; return true; } static bool valueFlowForLoop2(const Token *tok, ProgramMemory *memory1, ProgramMemory *memory2, ProgramMemory *memoryAfter) { // for ( firstExpression ; secondExpression ; thirdExpression ) const Token *firstExpression = tok->next()->astOperand2()->astOperand1(); const Token *secondExpression = tok->next()->astOperand2()->astOperand2()->astOperand1(); const Token *thirdExpression = tok->next()->astOperand2()->astOperand2()->astOperand2(); ProgramMemory programMemory; MathLib::bigint result(0); bool error = false; execute(firstExpression, &programMemory, &result, &error); if (error) return false; execute(secondExpression, &programMemory, &result, &error); if (result == 0) // 2nd expression is false => no looping return false; if (error) { // If a variable is reassigned in second expression, return false bool reassign = false; visitAstNodes(secondExpression, [&](const Token *t) { if (t->str() == "=" && t->astOperand1() && programMemory.hasValue(t->astOperand1()->varId())) // TODO: investigate what variable is assigned. reassign = true; return reassign ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (reassign) return false; } ProgramMemory startMemory(programMemory); ProgramMemory endMemory; int maxcount = 10000; while (result != 0 && !error && --maxcount > 0) { endMemory = programMemory; execute(thirdExpression, &programMemory, &result, &error); if (!error) execute(secondExpression, &programMemory, &result, &error); } memory1->swap(startMemory); if (!error) { memory2->swap(endMemory); memoryAfter->swap(programMemory); } return true; } static void valueFlowForLoopSimplify(Token * const bodyStart, const nonneg int varid, bool globalvar, const MathLib::bigint value, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { const Token * const bodyEnd = bodyStart->link(); // Is variable modified inside for loop if (isVariableChanged(bodyStart, bodyEnd, varid, globalvar, settings, tokenlist->isCPP())) return; for (Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) { if (tok2->varId() == varid) { const Token * parent = tok2->astParent(); while (parent) { const Token * const p = parent; parent = parent->astParent(); if (!parent || parent->str() == ":") break; if (parent->str() == "?") { if (parent->astOperand2() != p) parent = nullptr; break; } } if (parent) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop variable " + tok2->str() + " stopping on ?"); continue; } ValueFlow::Value value1(value); value1.varId = tok2->varId(); setTokenValue(tok2, value1, settings); } if (Token::Match(tok2, "%oror%|&&")) { const ProgramMemory programMemory(getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value))); if ((tok2->str() == "&&" && !conditionIsTrue(tok2->astOperand1(), programMemory)) || (tok2->str() == "||" && !conditionIsFalse(tok2->astOperand1(), programMemory))) { // Skip second expression.. const Token *parent = tok2; while (parent && parent->str() == tok2->str()) parent = parent->astParent(); // Jump to end of condition if (parent && parent->str() == "(") { tok2 = parent->link(); // cast if (Token::simpleMatch(tok2, ") (")) tok2 = tok2->linkAt(1); } } } if ((tok2->str() == "&&" && conditionIsFalse(tok2->astOperand1(), getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value)))) || (tok2->str() == "||" && conditionIsTrue(tok2->astOperand1(), getProgramMemory(tok2->astTop(), varid, ValueFlow::Value(value))))) break; else if (Token::simpleMatch(tok2, ") {") && Token::findmatch(tok2->link(), "%varid%", tok2, varid)) { if (Token::findmatch(tok2, "continue|break|return", tok2->linkAt(1), varid)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop variable bailout on conditional continue|break|return"); break; } if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop variable skipping conditional scope"); tok2 = tok2->next()->link(); if (Token::simpleMatch(tok2, "} else {")) { if (Token::findmatch(tok2, "continue|break|return", tok2->linkAt(2), varid)) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop variable bailout on conditional continue|break|return"); break; } tok2 = tok2->linkAt(2); } } else if (Token::simpleMatch(tok2, ") {")) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, tok2, "For loop skipping {} code"); tok2 = tok2->linkAt(1); if (Token::simpleMatch(tok2, "} else {")) tok2 = tok2->linkAt(2); } } } static void valueFlowForLoopSimplifyAfter(Token *fortok, nonneg int varid, const MathLib::bigint num, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings) { const Token *vartok = nullptr; for (const Token *tok = fortok; tok; tok = tok->next()) { if (tok->varId() == varid) { vartok = tok; break; } } if (!vartok || !vartok->variable()) return; const Variable *var = vartok->variable(); const Token *endToken = nullptr; if (var->isLocal()) endToken = var->typeStartToken()->scope()->bodyEnd; else endToken = fortok->scope()->bodyEnd; std::list values; values.emplace_back(num); values.back().errorPath.emplace_back(fortok,"After for loop, " + var->name() + " has value " + values.back().infoString()); valueFlowForwardVariable( fortok->linkAt(1)->linkAt(1)->next(), endToken, var, varid, values, false, false, tokenlist, errorLogger, settings); } static void valueFlowForLoop(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope &scope : symboldatabase->scopeList) { if (scope.type != Scope::eFor) continue; Token* tok = const_cast(scope.classDef); Token* const bodyStart = const_cast(scope.bodyStart); if (!Token::simpleMatch(tok->next()->astOperand2(), ";") || !Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) continue; int varid(0); MathLib::bigint num1(0), num2(0), numAfter(0); if (valueFlowForLoop1(tok, &varid, &num1, &num2, &numAfter)) { if (num1 <= num2) { valueFlowForLoopSimplify(bodyStart, varid, false, num1, tokenlist, errorLogger, settings); valueFlowForLoopSimplify(bodyStart, varid, false, num2, tokenlist, errorLogger, settings); valueFlowForLoopSimplifyAfter(tok, varid, numAfter, tokenlist, errorLogger, settings); } else valueFlowForLoopSimplifyAfter(tok, varid, num1, tokenlist, errorLogger, settings); } else { ProgramMemory mem1, mem2, memAfter; if (valueFlowForLoop2(tok, &mem1, &mem2, &memAfter)) { std::map::const_iterator it; for (it = mem1.values.begin(); it != mem1.values.end(); ++it) { if (!it->second.isIntValue()) continue; valueFlowForLoopSimplify(bodyStart, it->first, false, it->second.intvalue, tokenlist, errorLogger, settings); } for (it = mem2.values.begin(); it != mem2.values.end(); ++it) { if (!it->second.isIntValue()) continue; valueFlowForLoopSimplify(bodyStart, it->first, false, it->second.intvalue, tokenlist, errorLogger, settings); } for (it = memAfter.values.begin(); it != memAfter.values.end(); ++it) { if (!it->second.isIntValue()) continue; valueFlowForLoopSimplifyAfter(tok, it->first, it->second.intvalue, tokenlist, errorLogger, settings); } } } } } static void valueFlowInjectParameter(TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings, const Variable* arg, const Scope* functionScope, const std::list& argvalues) { // Is argument passed by value or const reference, and is it a known non-class type? if (arg->isReference() && !arg->isConst() && !arg->isClass()) return; // Set value in function scope.. const int varid2 = arg->declarationId(); if (!varid2) return; valueFlowForwardVariable(const_cast(functionScope->bodyStart->next()), functionScope->bodyEnd, arg, varid2, argvalues, false, true, tokenlist, errorLogger, settings); } static void valueFlowSwitchVariable(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope &scope : symboldatabase->scopeList) { if (scope.type != Scope::ScopeType::eSwitch) continue; if (!Token::Match(scope.classDef, "switch ( %var% ) {")) continue; const Token *vartok = scope.classDef->tokAt(2); const Variable *var = vartok->variable(); if (!var) continue; // bailout: global non-const variables if (!(var->isLocal() || var->isArgument()) && !var->isConst()) { if (settings->debugwarnings) bailout(tokenlist, errorLogger, vartok, "switch variable " + var->name() + " is global"); continue; } for (Token *tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) { if (tok->str() == "{") { tok = tok->link(); continue; } if (Token::Match(tok, "case %num% :")) { std::list values; values.emplace_back(MathLib::toLongNumber(tok->next()->str())); values.back().condition = tok; const std::string info("case " + tok->next()->str() + ": " + vartok->str() + " is " + tok->next()->str() + " here."); values.back().errorPath.emplace_back(tok, info); bool known = false; if ((Token::simpleMatch(tok->previous(), "{") || Token::simpleMatch(tok->tokAt(-2), "break ;")) && !Token::Match(tok->tokAt(3), ";| case")) known = true; while (Token::Match(tok->tokAt(3), ";| case %num% :")) { known = false; tok = tok->tokAt(3); if (!tok->isName()) tok = tok->next(); values.emplace_back(MathLib::toLongNumber(tok->next()->str())); values.back().condition = tok; const std::string info2("case " + tok->next()->str() + ": " + vartok->str() + " is " + tok->next()->str() + " here."); values.back().errorPath.emplace_back(tok, info2); } for (std::list::const_iterator val = values.begin(); val != values.end(); ++val) { valueFlowReverse(tokenlist, const_cast(scope.classDef), vartok, *val, ValueFlow::Value(), errorLogger, settings); } if (vartok->variable()->scope()) { if (known) values.back().setKnown(); // FIXME We must check if there is a return. See #9276 /* valueFlowForwardVariable(tok->tokAt(3), vartok->variable()->scope()->bodyEnd, vartok->variable(), vartok->varId(), values, values.back().isKnown(), false, tokenlist, errorLogger, settings); */ } } } } } static void setTokenValues(Token *tok, const std::list &values, const Settings *settings) { for (const ValueFlow::Value &value : values) { if (value.isIntValue()) setTokenValue(tok, value, settings); } } static bool evaluate(const Token *expr, const std::vector> &values, std::list *result) { if (!expr) return false; // strlen(arg).. if (expr->str() == "(" && Token::Match(expr->previous(), "strlen ( %name% )")) { const Token *arg = expr->next(); if (arg->str().compare(0,3,"arg") != 0 || arg->str().size() != 4) return false; const char n = arg->str()[3]; if (n < '1' || n - '1' >= values.size()) return false; for (const ValueFlow::Value &argvalue : values[n - '1']) { if (argvalue.isTokValue() && argvalue.tokvalue->tokType() == Token::eString) { ValueFlow::Value res(argvalue); // copy all "inconclusive", "condition", etc attributes // set return value.. res.valueType = ValueFlow::Value::INT; res.tokvalue = nullptr; res.intvalue = Token::getStrLength(argvalue.tokvalue); result->emplace_back(std::move(res)); } } return !result->empty(); } // unary operands if (expr->astOperand1() && !expr->astOperand2()) { std::list opvalues; if (!evaluate(expr->astOperand1(), values, &opvalues)) return false; if (expr->str() == "+") { result->swap(opvalues); return true; } if (expr->str() == "-") { for (ValueFlow::Value v: opvalues) { if (v.isIntValue()) { v.intvalue = -v.intvalue; result->emplace_back(std::move(v)); } } return true; } return false; } // binary/ternary operands if (expr->astOperand1() && expr->astOperand2()) { std::list lhsValues, rhsValues; if (!evaluate(expr->astOperand1(), values, &lhsValues)) return false; if (expr->str() != "?" && !evaluate(expr->astOperand2(), values, &rhsValues)) return false; for (const ValueFlow::Value &val1 : lhsValues) { if (!val1.isIntValue()) continue; if (expr->str() == "?") { rhsValues.clear(); const Token *expr2 = val1.intvalue ? expr->astOperand2()->astOperand1() : expr->astOperand2()->astOperand2(); if (!evaluate(expr2, values, &rhsValues)) continue; result->insert(result->end(), rhsValues.begin(), rhsValues.end()); continue; } for (const ValueFlow::Value &val2 : rhsValues) { if (!val2.isIntValue()) continue; if (val1.varId != 0 && val2.varId != 0) { if (val1.varId != val2.varId || val1.varvalue != val2.varvalue) continue; } if (expr->str() == "+") result->emplace_back(ValueFlow::Value(val1.intvalue + val2.intvalue)); else if (expr->str() == "-") result->emplace_back(ValueFlow::Value(val1.intvalue - val2.intvalue)); else if (expr->str() == "*") result->emplace_back(ValueFlow::Value(val1.intvalue * val2.intvalue)); else if (expr->str() == "/" && val2.intvalue != 0) result->emplace_back(ValueFlow::Value(val1.intvalue / val2.intvalue)); else if (expr->str() == "%" && val2.intvalue != 0) result->emplace_back(ValueFlow::Value(val1.intvalue % val2.intvalue)); else if (expr->str() == "&") result->emplace_back(ValueFlow::Value(val1.intvalue & val2.intvalue)); else if (expr->str() == "|") result->emplace_back(ValueFlow::Value(val1.intvalue | val2.intvalue)); else if (expr->str() == "^") result->emplace_back(ValueFlow::Value(val1.intvalue ^ val2.intvalue)); else if (expr->str() == "==") result->emplace_back(ValueFlow::Value(val1.intvalue == val2.intvalue)); else if (expr->str() == "!=") result->emplace_back(ValueFlow::Value(val1.intvalue != val2.intvalue)); else if (expr->str() == "<") result->emplace_back(ValueFlow::Value(val1.intvalue < val2.intvalue)); else if (expr->str() == ">") result->emplace_back(ValueFlow::Value(val1.intvalue > val2.intvalue)); else if (expr->str() == ">=") result->emplace_back(ValueFlow::Value(val1.intvalue >= val2.intvalue)); else if (expr->str() == "<=") result->emplace_back(ValueFlow::Value(val1.intvalue <= val2.intvalue)); else if (expr->str() == "&&") result->emplace_back(ValueFlow::Value(val1.intvalue && val2.intvalue)); else if (expr->str() == "||") result->emplace_back(ValueFlow::Value(val1.intvalue || val2.intvalue)); else if (expr->str() == "<<") result->emplace_back(ValueFlow::Value(val1.intvalue << val2.intvalue)); else if (expr->str() == ">>") result->emplace_back(ValueFlow::Value(val1.intvalue >> val2.intvalue)); else return false; combineValueProperties(val1, val2, &result->back()); } } return !result->empty(); } if (expr->str().compare(0,3,"arg")==0) { *result = values[expr->str()[3] - '1']; return true; } if (expr->isNumber()) { result->emplace_back(ValueFlow::Value(MathLib::toLongNumber(expr->str()))); result->back().setKnown(); return true; } else if (expr->tokType() == Token::eChar) { result->emplace_back(ValueFlow::Value(MathLib::toLongNumber(expr->str()))); result->back().setKnown(); return true; } return false; } static std::list getFunctionArgumentValues(const Token *argtok) { std::list argvalues(argtok->values()); removeImpossible(argvalues); if (argvalues.empty() && Token::Match(argtok, "%comp%|%oror%|&&|!")) { argvalues.emplace_back(0); argvalues.emplace_back(1); } return argvalues; } static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue, const Settings *settings) { std::vector> argValues; for (const Token *argtok : getArguments(tok->previous())) { argValues.emplace_back(getFunctionArgumentValues(argtok)); if (argValues.back().empty()) return; } if (returnValue.find("arg") != std::string::npos && argValues.empty()) return; TokenList tokenList(settings); { const std::string code = "return " + returnValue + ";"; std::istringstream istr(code); if (!tokenList.createTokens(istr)) return; } // combine operators, set links, etc.. std::stack lpar; for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) { if (Token::Match(tok2, "[!<>=] =")) { tok2->str(tok2->str() + "="); tok2->deleteNext(); } else if (tok2->str() == "(") lpar.push(tok2); else if (tok2->str() == ")") { if (lpar.empty()) return; Token::createMutualLinks(lpar.top(), tok2); lpar.pop(); } } if (!lpar.empty()) return; // Evaluate expression tokenList.createAst(); std::list results; if (evaluate(tokenList.front()->astOperand1(), argValues, &results)) setTokenValues(tok, results, settings); } static void valueFlowSubFunction(TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!Token::Match(tok, "%name% (")) continue; const Function * const calledFunction = tok->function(); if (!calledFunction) { // library function? const std::string& returnValue(settings->library.returnValue(tok)); if (!returnValue.empty()) valueFlowLibraryFunction(tok->next(), returnValue, settings); continue; } const Scope * const calledFunctionScope = calledFunction->functionScope; if (!calledFunctionScope) continue; // TODO: Rewrite this. It does not work well to inject 1 argument at a time. const std::vector &callArguments = getArguments(tok); for (int argnr = 0U; argnr < callArguments.size(); ++argnr) { const Token *argtok = callArguments[argnr]; // Get function argument const Variable * const argvar = calledFunction->getArgumentVar(argnr); if (!argvar) break; // passing value(s) to function std::list argvalues(getFunctionArgumentValues(argtok)); // Don't forward lifetime values argvalues.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue)); if (argvalues.empty()) continue; // Error path.. for (ValueFlow::Value &v : argvalues) { const std::string nr = MathLib::toString(argnr + 1) + getOrdinalText(argnr + 1); v.errorPath.emplace_back(argtok, "Calling function '" + calledFunction->name() + "', " + nr + " argument '" + argtok->expressionString() + "' value is " + v.infoString()); } // passed values are not "known".. lowerToPossible(argvalues); valueFlowInjectParameter(tokenlist, errorLogger, settings, argvar, calledFunctionScope, argvalues); // FIXME: We need to rewrite the valueflow analysis to better handle multiple arguments if (!argvalues.empty()) break; } } } static void valueFlowFunctionDefaultParameter(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { if (!tokenlist->isCPP()) return; for (const Scope* scope : symboldatabase->functionScopes) { const Function* function = scope->function; if (!function) continue; for (std::size_t arg = function->minArgCount(); arg < function->argCount(); arg++) { const Variable* var = function->getArgumentVar(arg); if (var && var->hasDefault() && Token::Match(var->nameToken(), "%var% = %num%|%str% [,)]")) { const std::list &values = var->nameToken()->tokAt(2)->values(); std::list argvalues; for (const ValueFlow::Value &value : values) { ValueFlow::Value v(value); v.defaultArg = true; v.changeKnownToPossible(); if (v.isPossible()) argvalues.push_back(v); } if (!argvalues.empty()) valueFlowInjectParameter(tokenlist, errorLogger, settings, var, scope, argvalues); } } } } static bool isKnown(const Token * tok) { return tok && tok->hasKnownIntValue(); } static void valueFlowFunctionReturn(TokenList *tokenlist, ErrorLogger *errorLogger) { for (Token *tok = tokenlist->back(); tok; tok = tok->previous()) { if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand1()->function()) continue; if (tok->hasKnownValue()) continue; // Arguments.. std::vector parvalues; if (tok->astOperand2()) { const Token *partok = tok->astOperand2(); while (partok && partok->str() == "," && isKnown(partok->astOperand2())) partok = partok->astOperand1(); if (!isKnown(partok)) continue; parvalues.push_back(partok->values().front().intvalue); partok = partok->astParent(); while (partok && partok->str() == ",") { parvalues.push_back(partok->astOperand2()->values().front().intvalue); partok = partok->astParent(); } if (partok != tok) continue; } // Get scope and args of function const Function * const function = tok->astOperand1()->function(); const Scope * const functionScope = function->functionScope; if (!functionScope || !Token::simpleMatch(functionScope->bodyStart, "{ return")) { if (functionScope && tokenlist->getSettings()->debugwarnings && Token::findsimplematch(functionScope->bodyStart, "return", functionScope->bodyEnd)) bailout(tokenlist, errorLogger, tok, "function return; nontrivial function body"); continue; } ProgramMemory programMemory; for (std::size_t i = 0; i < parvalues.size(); ++i) { const Variable * const arg = function->getArgumentVar(i); if (!arg || !Token::Match(arg->typeStartToken(), "%type% %name% ,|)")) { if (tokenlist->getSettings()->debugwarnings) bailout(tokenlist, errorLogger, tok, "function return; unhandled argument type"); programMemory.clear(); break; } programMemory.setIntValue(arg->declarationId(), parvalues[i]); } if (programMemory.empty() && !parvalues.empty()) continue; // Determine return value of subfunction.. MathLib::bigint result = 0; bool error = false; execute(functionScope->bodyStart->next()->astOperand1(), &programMemory, &result, &error); if (!error) { ValueFlow::Value v(result); if (function->hasVirtualSpecifier()) v.setPossible(); else v.setKnown(); setTokenValue(tok, v, tokenlist->getSettings()); } } } static void valueFlowUninit(TokenList *tokenlist, SymbolDatabase * /*symbolDatabase*/, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!Token::Match(tok,"[;{}] %type%")) continue; if (!tok->scope()->isExecutable()) continue; const Token *vardecl = tok->next(); bool stdtype = false; bool pointer = false; while (Token::Match(vardecl, "%name%|::|*") && vardecl->varId() == 0) { stdtype |= vardecl->isStandardType(); pointer |= vardecl->str() == "*"; vardecl = vardecl->next(); } // if (!stdtype && !pointer) // continue; if (!Token::Match(vardecl, "%var% ;")) continue; if (Token::Match(vardecl, "%varid% ; %varid% =", vardecl->varId())) continue; const Variable *var = vardecl->variable(); if (!var || var->nameToken() != vardecl) continue; if ((!var->isPointer() && var->type() && var->type()->needInitialization != Type::NeedInitialization::True) || !var->isLocal() || var->isStatic() || var->isExtern() || var->isReference() || var->isThrow()) continue; if (!var->type() && !stdtype && !pointer) continue; ValueFlow::Value uninitValue; uninitValue.setKnown(); uninitValue.valueType = ValueFlow::Value::UNINIT; uninitValue.tokvalue = vardecl; std::list values; values.push_back(uninitValue); const bool constValue = true; const bool subFunction = false; valueFlowForwardVariable(vardecl->next(), vardecl->scope()->bodyEnd, var, vardecl->varId(), values, constValue, subFunction, tokenlist, errorLogger, settings); } } static bool hasContainerSizeGuard(const Token *tok, nonneg int containerId) { for (; tok && tok->astParent(); tok = tok->astParent()) { const Token *parent = tok->astParent(); if (tok != parent->astOperand2()) continue; if (!Token::Match(parent, "%oror%|&&|?")) continue; // is container found in lhs? bool found = false; visitAstNodes(parent->astOperand1(), [&](const Token *t) { if (t->varId() == containerId) found = true; return found ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; }); if (found) return true; } return false; } static bool isContainerSize(const Token* tok) { if (!Token::Match(tok, "%var% . %name% (")) return false; if (!astIsContainer(tok)) return false; if (tok->valueType()->container && tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::SIZE) return true; if (Token::Match(tok->tokAt(2), "size|length ( )")) return true; return false; } static bool isContainerEmpty(const Token* tok) { if (!Token::Match(tok, "%var% . %name% (")) return false; if (!astIsContainer(tok)) return false; if (tok->valueType()->container && tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::EMPTY) return true; if (Token::simpleMatch(tok->tokAt(2), "empty ( )")) return true; return false; } static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth = 20); static bool isContainerSizeChangedByFunction(const Token *tok, int depth = 20) { if (!tok->valueType() || !tok->valueType()->container) return false; // If we are accessing an element then we are not changing the container size if (Token::Match(tok, "%name% . %name% (")) { Library::Container::Yield yield = tok->valueType()->container->getYield(tok->strAt(2)); if (yield != Library::Container::Yield::NO_YIELD) return false; } if (Token::simpleMatch(tok->astParent(), "[")) return false; // address of variable const bool addressOf = tok->valueType()->pointer || (tok->astParent() && tok->astParent()->isUnaryOp("&")); int narg; const Token * ftok = getTokenArgumentFunction(tok, narg); if (!ftok) return false; // not a function => variable not changed const Function * fun = ftok->function(); if (fun) { const Variable *arg = fun->getArgumentVar(narg); if (arg) { if (!arg->isReference() && !addressOf) return false; if (!addressOf && arg->isConst()) return false; if (arg->valueType() && arg->valueType()->constness == 1) return false; const Scope * scope = fun->functionScope; if (scope) { // Argument not used if (!arg->nameToken()) return false; if (depth > 0) return isContainerSizeChanged(arg->declarationId(), scope->bodyStart, scope->bodyEnd, depth - 1); } // Don't know => Safe guess return true; } } bool inconclusive = false; const bool isChanged = isVariableChangedByFunctionCall(tok, 0, nullptr, &inconclusive); return (isChanged || inconclusive); } static void valueFlowContainerReverse(Token *tok, nonneg int containerId, const ValueFlow::Value &value, const Settings *settings) { while (nullptr != (tok = tok->previous())) { if (Token::Match(tok, "[{}]")) break; if (Token::Match(tok, "return|break|continue")) break; if (tok->varId() != containerId) continue; if (Token::Match(tok, "%name% =")) break; if (isContainerSizeChangedByFunction(tok)) break; if (!tok->valueType() || !tok->valueType()->container) break; if (Token::Match(tok, "%name% . %name% (") && tok->valueType()->container->getAction(tok->strAt(2)) != Library::Container::Action::NO_ACTION) break; if (!hasContainerSizeGuard(tok, containerId)) setTokenValue(tok, value, settings); } } static void valueFlowContainerForward(Token *tok, nonneg int containerId, ValueFlow::Value value, const Settings *settings, bool cpp) { while (nullptr != (tok = tok->next())) { if (Token::Match(tok, "[{}]")) break; if (Token::Match(tok, "while|for (")) { const Token *start = tok->linkAt(1)->next(); if (!Token::simpleMatch(start->link(), "{")) break; if (isContainerSizeChanged(containerId, start, start->link())) break; } if (Token::simpleMatch(tok, ") {") && Token::Match(tok->link()->previous(), "while|for|if (")) { const Token *start = tok->next(); if (isContainerSizeChanged(containerId, start, start->link()) || isEscapeScope(start, nullptr)) break; tok = start->link(); if (Token::simpleMatch(tok, "} else {")) { start = tok->tokAt(2); if (isContainerSizeChanged(containerId, start, start->link())) break; tok = start->link(); } } if (tok->varId() != containerId) continue; if (Token::Match(tok, "%name% =")) break; if (Token::Match(tok, "%name% +=")) { if (!tok->valueType() || !tok->valueType()->container || !tok->valueType()->container->stdStringLike) break; const Token *rhs = tok->next()->astOperand2(); if (rhs->tokType() == Token::eString) value.intvalue += Token::getStrLength(rhs); else if (rhs->valueType() && rhs->valueType()->container && rhs->valueType()->container->stdStringLike) { bool found = false; for (const ValueFlow::Value &rhsval : rhs->values()) { if (rhsval.isKnown() && rhsval.isContainerSizeValue()) { value.intvalue += rhsval.intvalue; found = true; } } if (!found) break; } else break; } if (isLikelyStreamRead(cpp, tok->astParent())) break; if (isContainerSizeChangedByFunction(tok)) break; if (!tok->valueType() || !tok->valueType()->container) break; if (Token::Match(tok, "%name% . %name% (") && tok->valueType()->container->getAction(tok->strAt(2)) != Library::Container::Action::NO_ACTION) break; if (!hasContainerSizeGuard(tok, containerId)) setTokenValue(tok, value, settings); } } static bool isContainerSizeChanged(nonneg int varId, const Token *start, const Token *end, int depth) { for (const Token *tok = start; tok != end; tok = tok->next()) { if (tok->varId() != varId) continue; if (!tok->valueType() || !tok->valueType()->container) return true; if (Token::Match(tok, "%name% %assign%|<<")) return true; if (Token::Match(tok, "%name% . %name% (")) { Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2)); switch (action) { case Library::Container::Action::RESIZE: case Library::Container::Action::CLEAR: case Library::Container::Action::PUSH: case Library::Container::Action::POP: case Library::Container::Action::CHANGE: case Library::Container::Action::INSERT: case Library::Container::Action::ERASE: case Library::Container::Action::CHANGE_INTERNAL: return true; case Library::Container::Action::NO_ACTION: // might be unknown action return true; case Library::Container::Action::FIND: case Library::Container::Action::CHANGE_CONTENT: break; }; } if (isContainerSizeChangedByFunction(tok, depth)) return true; } return false; } static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->scope()) continue; if (!tok->scope()->isExecutable()) continue; if (!tok->variable()) continue; const Variable * var = tok->variable(); if (!var->isSmartPointer()) continue; if (var->nameToken() == tok) { if (Token::Match(tok, "%var% (|{") && tok->next()->astOperand2() && tok->next()->astOperand2()->str() != ",") { Token * inTok = tok->next()->astOperand2(); std::list values = inTok->values(); const bool constValue = inTok->isNumber(); valueFlowForwardAssign(inTok, var, values, constValue, true, tokenlist, errorLogger, settings); } else if (Token::Match(tok, "%var% ;")) { std::list values; ValueFlow::Value v(0); v.setKnown(); values.push_back(v); valueFlowForwardAssign(tok, var, values, false, true, tokenlist, errorLogger, settings); } } else if (Token::Match(tok, "%var% . reset (") && tok->next()->originalName() != "->") { if (Token::simpleMatch(tok->tokAt(3), "( )")) { std::list values; ValueFlow::Value v(0); v.setKnown(); values.push_back(v); valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings); } else { tok->removeValues(std::mem_fn(&ValueFlow::Value::isIntValue)); Token * inTok = tok->tokAt(3)->astOperand2(); if (!inTok) continue; std::list values = inTok->values(); const bool constValue = inTok->isNumber(); valueFlowForwardAssign(inTok, var, values, constValue, false, tokenlist, errorLogger, settings); } } else if (Token::Match(tok, "%var% . release ( )") && tok->next()->originalName() != "->") { std::list values; ValueFlow::Value v(0); v.setKnown(); values.push_back(v); valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings); } } } static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger * /*errorLogger*/, const Settings *settings) { // declaration for (const Variable *var : symboldatabase->variableList()) { if (!var || !var->isLocal() || var->isPointer() || var->isReference()) continue; if (!var->valueType() || !var->valueType()->container) continue; if (!Token::Match(var->nameToken(), "%name% ;")) continue; if (var->nameToken()->hasKnownValue()) continue; ValueFlow::Value value(0); if (var->valueType()->container->size_templateArgNo >= 0) { if (var->dimensions().size() == 1 && var->dimensions().front().tok && var->dimensions().front().tok->hasKnownIntValue()) value.intvalue = var->dimensions().front().tok->getKnownIntValue(); else continue; } value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; value.setKnown(); valueFlowContainerForward(var->nameToken()->next(), var->declarationId(), value, settings, tokenlist->isCPP()); } // after assignment for (const Scope *functionScope : symboldatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%name%|;|{|} %var% = %str% ;")) { const Token *containerTok = tok->next(); if (containerTok && containerTok->valueType() && containerTok->valueType()->container && containerTok->valueType()->container->stdStringLike) { ValueFlow::Value value(Token::getStrLength(containerTok->tokAt(2))); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; value.setKnown(); valueFlowContainerForward(containerTok->next(), containerTok->varId(), value, settings, tokenlist->isCPP()); } } } } // conditional conditionSize for (const Scope &scope : symboldatabase->scopeList) { if (scope.type != Scope::ScopeType::eIf) // TODO: while continue; for (const Token *tok = scope.classDef; tok && tok->str() != "{"; tok = tok->next()) { if (!tok->isName() || !tok->valueType() || tok->valueType()->type != ValueType::CONTAINER || !tok->valueType()->container) continue; const Token *conditionToken; MathLib::bigint intval; if (Token::Match(tok, "%name% . %name% (")) { if (tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::SIZE) { const Token *parent = tok->tokAt(3)->astParent(); if (!parent || !parent->isComparisonOp() || !parent->astOperand2()) continue; if (parent->astOperand1()->hasKnownIntValue()) intval = parent->astOperand1()->values().front().intvalue; else if (parent->astOperand2()->hasKnownIntValue()) intval = parent->astOperand2()->values().front().intvalue; else continue; conditionToken = parent; } else if (tok->valueType()->container->getYield(tok->strAt(2)) == Library::Container::Yield::EMPTY) { conditionToken = tok->tokAt(3); intval = 0; } else { continue; } } else if (tok->valueType()->container->stdStringLike && Token::Match(tok, "%name% ==|!= %str%") && tok->next()->astOperand2() == tok->tokAt(2)) { intval = Token::getStrLength(tok->tokAt(2)); conditionToken = tok->next(); } else { continue; } ValueFlow::Value value(conditionToken, intval); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; // possible value before condition valueFlowContainerReverse(const_cast(scope.classDef), tok->varId(), value, settings); } } } static void valueFlowContainerAfterCondition(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { ValueFlowConditionHandler handler; handler.forward = [&](Token* start, const Token* stop, const Token* vartok, const std::list& values, bool) { // TODO: Forward multiple values if (values.empty()) return false; const Variable* var = vartok->variable(); if (!var) return false; valueFlowContainerForward(start, var->declarationId(), values.front(), settings, tokenlist->isCPP()); return isContainerSizeChanged(var->declarationId(), start, stop); }; handler.parse = [&](const Token *tok) { ValueFlowConditionHandler::Condition cond; ValueFlow::Value true_value; ValueFlow::Value false_value; const Token *vartok = parseCompareInt(tok, true_value, false_value); if (vartok) { vartok = vartok->tokAt(-3); if (!isContainerSize(vartok)) return cond; true_value.valueType = ValueFlow::Value::CONTAINER_SIZE; false_value.valueType = ValueFlow::Value::CONTAINER_SIZE; cond.true_values.push_back(true_value); cond.false_values.push_back(false_value); cond.vartok = vartok; return cond; } // Empty check if (tok->str() == "(") { vartok = tok->tokAt(-3); // TODO: Handle .size() if (!isContainerEmpty(vartok)) return cond; const Token *parent = tok->astParent(); while (parent) { if (Token::Match(parent, "%comp%|!")) return cond; parent = parent->astParent(); } ValueFlow::Value value(tok, 0LL); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; cond.true_values.emplace_back(value); cond.false_values.emplace_back(std::move(value)); cond.vartok = vartok; return cond; } // String compare if (Token::Match(tok, "==|!=")) { const Token *strtok = nullptr; if (Token::Match(tok->astOperand1(), "%str%")) { strtok = tok->astOperand1(); vartok = tok->astOperand2(); } else if (Token::Match(tok->astOperand2(), "%str%")) { strtok = tok->astOperand2(); vartok = tok->astOperand1(); } if (!strtok) return cond; if (!astIsContainer(vartok)) return cond; ValueFlow::Value value(tok, Token::getStrLength(strtok)); value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; cond.false_values.emplace_back(value); cond.true_values.emplace_back(std::move(value)); cond.vartok = vartok; return cond; } return cond; }; handler.afterCondition(tokenlist, symboldatabase, errorLogger, settings); } static void valueFlowFwdAnalysis(const TokenList *tokenlist, const Settings *settings) { for (const Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "for (")) tok = tok->linkAt(1); if (tok->str() != "=" || !tok->astOperand1() || !tok->astOperand2()) continue; if (!tok->scope()->isExecutable()) continue; if (!tok->astOperand2()->hasKnownIntValue()) continue; ValueFlow::Value v(tok->astOperand2()->values().front()); v.errorPath.emplace_back(tok, tok->astOperand1()->expressionString() + " is assigned value " + MathLib::toString(v.intvalue)); const Token *startToken = tok->findExpressionStartEndTokens().second->next(); const Scope *functionScope = tok->scope(); while (functionScope->nestedIn && functionScope->nestedIn->isExecutable()) functionScope = functionScope->nestedIn; const Token *endToken = functionScope->bodyEnd; valueFlowForwardExpression(const_cast(startToken), endToken, tok->astOperand1(), {v}, tokenlist, settings); } } static void valueFlowDynamicBufferSize(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope *functionScope : symboldatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "[;{}] %var% =")) continue; if (!tok->next()->variable()) continue; const Token *rhs = tok->tokAt(2)->astOperand2(); while (rhs && rhs->isCast()) rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1(); if (!rhs) continue; if (!Token::Match(rhs->previous(), "%name% (")) continue; const Library::AllocFunc *allocFunc = settings->library.getAllocFuncInfo(rhs->previous()); if (!allocFunc) allocFunc = settings->library.getReallocFuncInfo(rhs->previous()); if (!allocFunc || allocFunc->bufferSize == Library::AllocFunc::BufferSize::none) continue; const std::vector args = getArguments(rhs->previous()); const Token * const arg1 = (args.size() >= allocFunc->bufferSizeArg1) ? args[allocFunc->bufferSizeArg1 - 1] : nullptr; const Token * const arg2 = (args.size() >= allocFunc->bufferSizeArg2) ? args[allocFunc->bufferSizeArg2 - 1] : nullptr; MathLib::bigint sizeValue = -1; switch (allocFunc->bufferSize) { case Library::AllocFunc::BufferSize::none: break; case Library::AllocFunc::BufferSize::malloc: if (arg1 && arg1->hasKnownIntValue()) sizeValue = arg1->getKnownIntValue(); break; case Library::AllocFunc::BufferSize::calloc: if (arg1 && arg2 && arg1->hasKnownIntValue() && arg2->hasKnownIntValue()) sizeValue = arg1->getKnownIntValue() * arg2->getKnownIntValue(); break; case Library::AllocFunc::BufferSize::strdup: if (arg1 && arg1->hasKnownValue()) { const ValueFlow::Value &value = arg1->values().back(); if (value.isTokValue() && value.tokvalue->tokType() == Token::eString) sizeValue = Token::getStrLength(value.tokvalue) + 1; // Add one for the null terminator } break; }; if (sizeValue < 0) continue; ValueFlow::Value value(sizeValue); value.errorPath.emplace_back(tok->tokAt(2), "Assign " + tok->strAt(1) + ", buffer with size " + MathLib::toString(sizeValue)); value.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; value.setKnown(); const std::list values{value}; valueFlowForwardVariable(const_cast(rhs), functionScope->bodyEnd, tok->next()->variable(), tok->next()->varId(), values, true, false, tokenlist, errorLogger, settings); } } } static bool getMinMaxValues(const ValueType *vt, const cppcheck::Platform &platform, MathLib::bigint *minValue, MathLib::bigint *maxValue) { if (!vt || !vt->isIntegral() || vt->pointer) return false; int bits; switch (vt->type) { case ValueType::Type::BOOL: bits = 1; break; case ValueType::Type::CHAR: bits = platform.char_bit; break; case ValueType::Type::SHORT: bits = platform.short_bit; break; case ValueType::Type::INT: bits = platform.int_bit; break; case ValueType::Type::LONG: bits = platform.long_bit; break; case ValueType::Type::LONGLONG: bits = platform.long_long_bit; break; default: return false; }; if (bits == 1) { *minValue = 0; *maxValue = 1; } else if (bits < 62) { if (vt->sign == ValueType::Sign::UNSIGNED) { *minValue = 0; *maxValue = (1LL << bits) - 1; } else { *minValue = -(1LL << (bits - 1)); *maxValue = (1LL << (bits - 1)) - 1; } } else if (bits == 64) { if (vt->sign == ValueType::Sign::UNSIGNED) { *minValue = 0; *maxValue = LLONG_MAX; // todo max unsigned value } else { *minValue = LLONG_MIN; *maxValue = LLONG_MAX; } } else { return false; } return true; } static bool getMinMaxValues(const std::string &typestr, const Settings *settings, MathLib::bigint *minvalue, MathLib::bigint *maxvalue) { TokenList typeTokens(settings); std::istringstream istr(typestr+";"); if (!typeTokens.createTokens(istr)) return false; typeTokens.simplifyPlatformTypes(); typeTokens.simplifyStdType(); const ValueType &vt = ValueType::parseDecl(typeTokens.front(), settings); return getMinMaxValues(&vt, *settings, minvalue, maxvalue); } static void valueFlowSafeFunctions(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (const Scope *functionScope : symboldatabase->functionScopes) { if (!functionScope->bodyStart) continue; const Function *function = functionScope->function; if (!function) continue; const bool safe = function->isSafe(settings); const bool all = safe && settings->platformType != cppcheck::Platform::PlatformType::Unspecified; for (const Variable &arg : function->argumentList) { if (!arg.nameToken() || !arg.valueType()) continue; if (arg.valueType()->type == ValueType::Type::CONTAINER) { if (!safe) continue; std::list argValues; argValues.emplace_back(0); argValues.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " is empty"); argValues.back().safe = true; argValues.emplace_back(1000000); argValues.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " size is 1000000"); argValues.back().safe = true; for (const ValueFlow::Value &value : argValues) valueFlowContainerForward(const_cast(functionScope->bodyStart), arg.declarationId(), value, settings, tokenlist->isCPP()); continue; } MathLib::bigint low, high; bool isLow = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, &low); bool isHigh = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, &high); if (!isLow && !isHigh && !all) continue; const bool safeLow = !isLow; const bool safeHigh = !isHigh; if ((!isLow || !isHigh) && all) { MathLib::bigint minValue, maxValue; if (getMinMaxValues(arg.valueType(), *settings, &minValue, &maxValue)) { if (!isLow) low = minValue; if (!isHigh) high = maxValue; isLow = isHigh = true; } else if (arg.valueType()->type == ValueType::Type::FLOAT || arg.valueType()->type == ValueType::Type::DOUBLE || arg.valueType()->type == ValueType::Type::LONGDOUBLE) { std::list argValues; argValues.emplace_back(0); argValues.back().valueType = ValueFlow::Value::ValueType::FLOAT; argValues.back().floatValue = isLow ? low : -1E25f; argValues.back().errorPath.emplace_back(arg.nameToken(), "Safe checks: Assuming argument has value " + MathLib::toString(argValues.back().floatValue)); argValues.back().safe = true; argValues.emplace_back(0); argValues.back().valueType = ValueFlow::Value::ValueType::FLOAT; argValues.back().floatValue = isHigh ? high : 1E25f; argValues.back().errorPath.emplace_back(arg.nameToken(), "Safe checks: Assuming argument has value " + MathLib::toString(argValues.back().floatValue)); argValues.back().safe = true; valueFlowForwardVariable(const_cast(functionScope->bodyStart->next()), functionScope->bodyEnd, &arg, arg.declarationId(), argValues, false, false, tokenlist, errorLogger, settings); continue; } } std::list argValues; if (isLow) { argValues.emplace_back(low); argValues.back().errorPath.emplace_back(arg.nameToken(), std::string(safeLow ? "Safe checks: " : "") + "Assuming argument has value " + MathLib::toString(low)); argValues.back().safe = safeLow; } if (isHigh) { argValues.emplace_back(high); argValues.back().errorPath.emplace_back(arg.nameToken(), std::string(safeHigh ? "Safe checks: " : "") + "Assuming argument has value " + MathLib::toString(high)); argValues.back().safe = safeHigh; } if (!argValues.empty()) valueFlowForwardVariable(const_cast(functionScope->bodyStart->next()), functionScope->bodyEnd, &arg, arg.declarationId(), argValues, false, false, tokenlist, errorLogger, settings); } } } static void valueFlowUnknownFunctionReturn(TokenList *tokenlist, const Settings *settings) { if (settings->checkUnknownFunctionReturn.empty()) return; for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { if (!tok->astParent() || tok->str() != "(") continue; if (!Token::Match(tok->previous(), "%name%")) continue; if (settings->checkUnknownFunctionReturn.find(tok->previous()->str()) == settings->checkUnknownFunctionReturn.end()) continue; std::vector unknownValues = settings->library.unknownReturnValues(tok->astOperand1()); if (unknownValues.empty()) continue; // Get min/max values for return type const std::string &typestr = settings->library.returnValueType(tok->previous()); MathLib::bigint minvalue, maxvalue; if (!getMinMaxValues(typestr, settings, &minvalue, &maxvalue)) continue; for (MathLib::bigint value : unknownValues) { if (value < minvalue) value = minvalue; else if (value > maxvalue) value = maxvalue; setTokenValue(const_cast(tok), ValueFlow::Value(value), settings); } } } ValueFlow::Value::Value(const Token* c, long long val) : valueType(INT), bound(Bound::Point), intvalue(val), tokvalue(nullptr), floatValue(0.0), moveKind(MoveKind::NonMovedVariable), varvalue(val), condition(c), varId(0U), safe(false), conditional(false), defaultArg(false), indirect(0), lifetimeKind(LifetimeKind::Object), lifetimeScope(LifetimeScope::Local), valueKind(ValueKind::Possible) { errorPath.emplace_back(c, "Assuming that condition '" + c->expressionString() + "' is not redundant"); } std::string ValueFlow::Value::infoString() const { switch (valueType) { case INT: return MathLib::toString(intvalue); case TOK: return tokvalue->str(); case FLOAT: return MathLib::toString(floatValue); case MOVED: return ""; case UNINIT: return ""; case BUFFER_SIZE: case CONTAINER_SIZE: return "size=" + MathLib::toString(intvalue); case LIFETIME: return "lifetime=" + tokvalue->str(); }; throw InternalError(nullptr, "Invalid ValueFlow Value type"); } const ValueFlow::Value *ValueFlow::valueFlowConstantFoldAST(Token *expr, const Settings *settings) { if (expr && expr->values().empty()) { valueFlowConstantFoldAST(expr->astOperand1(), settings); valueFlowConstantFoldAST(expr->astOperand2(), settings); valueFlowSetConstantValue(expr, settings, true /* TODO: this is a guess */); } return expr && expr->hasKnownValue() ? &expr->values().front() : nullptr; } static std::size_t getTotalValues(TokenList *tokenlist) { std::size_t n = 1; for (Token *tok = tokenlist->front(); tok; tok = tok->next()) n += tok->values().size(); return n; } void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { for (Token *tok = tokenlist->front(); tok; tok = tok->next()) tok->clearValueFlow(); valueFlowNumber(tokenlist); valueFlowString(tokenlist); valueFlowArray(tokenlist); valueFlowUnknownFunctionReturn(tokenlist, settings); valueFlowGlobalConstVar(tokenlist, settings); valueFlowGlobalStaticVar(tokenlist, settings); valueFlowPointerAlias(tokenlist); valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); valueFlowFunctionReturn(tokenlist, errorLogger); valueFlowBitAnd(tokenlist); valueFlowSameExpressions(tokenlist); valueFlowFwdAnalysis(tokenlist, settings); std::size_t values = 0; std::size_t n = 4; while (n > 0 && values < getTotalValues(tokenlist)) { values = getTotalValues(tokenlist); valueFlowPointerAliasDeref(tokenlist); valueFlowArrayBool(tokenlist); valueFlowRightShift(tokenlist, settings); valueFlowOppositeCondition(symboldatabase, settings); valueFlowTerminatingCondition(tokenlist, symboldatabase, errorLogger, settings); valueFlowBeforeCondition(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterMove(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterCondition(tokenlist, symboldatabase, errorLogger, settings); valueFlowInferCondition(tokenlist, settings); valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings); valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings); valueFlowSubFunction(tokenlist, errorLogger, settings); valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings); valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings); if (tokenlist->isCPP()) { valueFlowSmartPointer(tokenlist, errorLogger, settings); valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings); valueFlowContainerAfterCondition(tokenlist, symboldatabase, errorLogger, settings); } valueFlowSafeFunctions(tokenlist, symboldatabase, errorLogger, settings); n--; } valueFlowDynamicBufferSize(tokenlist, symboldatabase, errorLogger, settings); } std::string ValueFlow::eitherTheConditionIsRedundant(const Token *condition) { if (!condition) return "Either the condition is redundant"; if (condition->str() == "case") { std::string expr; for (const Token *tok = condition; tok && tok->str() != ":"; tok = tok->next()) { expr += tok->str(); if (Token::Match(tok, "%name%|%num% %name%|%num%")) expr += ' '; } return "Either the switch case '" + expr + "' is redundant"; } return "Either the condition '" + condition->expressionString() + "' is redundant"; } cppcheck-1.90/lib/valueflow.h000066400000000000000000000270161357737443600161770ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //--------------------------------------------------------------------------- #ifndef valueflowH #define valueflowH //--------------------------------------------------------------------------- #include "config.h" #include "utils.h" #include #include #include #include class ErrorLogger; class Settings; class SymbolDatabase; class Token; class TokenList; class ValueType; class Variable; namespace ValueFlow { struct increment { template void operator()(T& x) const { x++; } }; struct decrement { template void operator()(T& x) const { x--; } }; class CPPCHECKLIB Value { public: typedef std::pair ErrorPathItem; typedef std::list ErrorPath; explicit Value(long long val = 0) : valueType(ValueType::INT), bound(Bound::Point), intvalue(val), tokvalue(nullptr), floatValue(0.0), moveKind(MoveKind::NonMovedVariable), varvalue(val), condition(nullptr), varId(0U), safe(false), conditional(false), defaultArg(false), indirect(0), lifetimeKind(LifetimeKind::Object), lifetimeScope(LifetimeScope::Local), valueKind(ValueKind::Possible) {} Value(const Token *c, long long val); bool equalValue(const ValueFlow::Value& rhs) const { if (valueType != rhs.valueType) return false; switch (valueType) { case ValueType::INT: if (intvalue != rhs.intvalue) return false; break; case ValueType::TOK: if (tokvalue != rhs.tokvalue) return false; break; case ValueType::FLOAT: // TODO: Write some better comparison if (floatValue > rhs.floatValue || floatValue < rhs.floatValue) return false; break; case ValueType::MOVED: if (moveKind != rhs.moveKind) return false; break; case ValueType::UNINIT: break; case ValueType::BUFFER_SIZE: if (intvalue != rhs.intvalue) return false; break; case ValueType::CONTAINER_SIZE: if (intvalue != rhs.intvalue) return false; break; case ValueType::LIFETIME: if (tokvalue != rhs.tokvalue) return false; } return true; } template void visitValue(F f) { switch (valueType) { case ValueType::INT: case ValueType::BUFFER_SIZE: case ValueType::CONTAINER_SIZE: { f(intvalue); break; } case ValueType::FLOAT: { f(floatValue); break; } case ValueType::UNINIT: case ValueType::TOK: case ValueType::LIFETIME: case ValueType::MOVED: break; } } bool operator==(const Value &rhs) const { if (!equalValue(rhs)) return false; return varvalue == rhs.varvalue && condition == rhs.condition && varId == rhs.varId && conditional == rhs.conditional && defaultArg == rhs.defaultArg && indirect == rhs.indirect && valueKind == rhs.valueKind; } bool operator!=(const Value &rhs) const { return !(*this == rhs); } void decreaseRange() { if (bound == Bound::Lower) visitValue(increment{}); else if (bound == Bound::Upper) visitValue(decrement{}); } void invertRange() { if (bound == Bound::Lower) bound = Bound::Upper; else if (bound == Bound::Upper) bound = Bound::Lower; decreaseRange(); } std::string infoString() const; enum ValueType { INT, TOK, FLOAT, MOVED, UNINIT, CONTAINER_SIZE, LIFETIME, BUFFER_SIZE } valueType; bool isIntValue() const { return valueType == ValueType::INT; } bool isTokValue() const { return valueType == ValueType::TOK; } bool isFloatValue() const { return valueType == ValueType::FLOAT; } bool isMovedValue() const { return valueType == ValueType::MOVED; } bool isUninitValue() const { return valueType == ValueType::UNINIT; } bool isContainerSizeValue() const { return valueType == ValueType::CONTAINER_SIZE; } bool isLifetimeValue() const { return valueType == ValueType::LIFETIME; } bool isBufferSizeValue() const { return valueType == ValueType::BUFFER_SIZE; } bool isLocalLifetimeValue() const { return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Local; } bool isArgumentLifetimeValue() const { return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Argument; } bool isNonValue() const { return isMovedValue() || isUninitValue() || isLifetimeValue(); } /** The value bound */ enum class Bound { Upper, Lower, Point } bound; /** int value */ long long intvalue; /** token value - the token that has the value. this is used for pointer aliases, strings, etc. */ const Token *tokvalue; /** float value */ double floatValue; /** kind of moved */ enum class MoveKind {NonMovedVariable, MovedVariable, ForwardedVariable} moveKind; /** For calculated values - variable value that calculated value depends on */ long long varvalue; /** Condition that this value depends on */ const Token *condition; ErrorPath errorPath; /** For calculated values - varId that calculated value depends on */ nonneg int varId; /** value relies on safe checking */ bool safe; /** Conditional value */ bool conditional; /** Is this value passed as default parameter to the function? */ bool defaultArg; int indirect; enum class LifetimeKind {Object, Lambda, Iterator, Address} lifetimeKind; enum class LifetimeScope { Local, Argument } lifetimeScope; static const char * toString(MoveKind moveKind) { switch (moveKind) { case MoveKind::NonMovedVariable: return "NonMovedVariable"; case MoveKind::MovedVariable: return "MovedVariable"; case MoveKind::ForwardedVariable: return "ForwardedVariable"; } return ""; } /** How known is this value */ enum class ValueKind { /** This value is possible, other unlisted values may also be possible */ Possible, /** Only listed values are possible */ Known, /** Inconclusive */ Inconclusive, /** Listed values are impossible */ Impossible } valueKind; void setKnown() { valueKind = ValueKind::Known; } bool isKnown() const { return valueKind == ValueKind::Known; } void setPossible() { valueKind = ValueKind::Possible; } bool isPossible() const { return valueKind == ValueKind::Possible; } bool isImpossible() const { return valueKind == ValueKind::Impossible; } void setImpossible() { valueKind = ValueKind::Impossible; } void setInconclusive(bool inconclusive = true) { if (inconclusive) valueKind = ValueKind::Inconclusive; } bool isInconclusive() const { return valueKind == ValueKind::Inconclusive; } void changeKnownToPossible() { if (isKnown()) valueKind = ValueKind::Possible; } bool errorSeverity() const { return !condition && !defaultArg; } }; /// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues). const ValueFlow::Value * valueFlowConstantFoldAST(Token *expr, const Settings *settings); /// Perform valueflow analysis. void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings); std::string eitherTheConditionIsRedundant(const Token *condition); size_t getSizeOf(const ValueType &vt, const Settings *settings); } struct LifetimeToken { const Token* token; bool addressOf; ValueFlow::Value::ErrorPath errorPath; bool inconclusive; LifetimeToken() : token(nullptr), addressOf(false), errorPath(), inconclusive(false) {} LifetimeToken(const Token* token, ValueFlow::Value::ErrorPath errorPath) : token(token), addressOf(false), errorPath(std::move(errorPath)), inconclusive(false) {} LifetimeToken(const Token* token, bool addressOf, ValueFlow::Value::ErrorPath errorPath) : token(token), addressOf(addressOf), errorPath(std::move(errorPath)), inconclusive(false) {} static std::vector setAddressOf(std::vector v, bool b) { for (LifetimeToken& x : v) x.addressOf = b; return v; } static std::vector setInconclusive(std::vector v, bool b) { for (LifetimeToken& x : v) x.inconclusive = b; return v; } }; const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value); std::vector getLifetimeTokens(const Token* tok, ValueFlow::Value::ErrorPath errorPath = ValueFlow::Value::ErrorPath{}, int depth = 20); const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr); bool isLifetimeBorrowed(const Token *tok, const Settings *settings); std::string lifetimeType(const Token *tok, const ValueFlow::Value *val); std::string lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ValueFlow::Value::ErrorPath &errorPath); ValueFlow::Value getLifetimeObjValue(const Token *tok); #endif // valueflowH cppcheck-1.90/lib/version.h000066400000000000000000000010631357737443600156520ustar00rootroot00000000000000#define CPPCHECK_MAJOR 1 #define CPPCHECK_MINOR 90 #define CPPCHECK_DEVMINOR 90 #define STRINGIFY(x) STRING(x) #define STRING(VER) #VER #if CPPCHECK_MINOR == CPPCHECK_DEVMINOR #define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR) "." STRINGIFY(CPPCHECK_DEVMINOR) #define CPPCHECK_VERSION CPPCHECK_MAJOR,CPPCHECK_MINOR,0,0 #else #define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR) "." STRINGIFY(CPPCHECK_DEVMINOR) " dev" #define CPPCHECK_VERSION CPPCHECK_MAJOR,CPPCHECK_MINOR,99,0 #endif #define LEGALCOPYRIGHT L"Copyright (C) 2007-2019 Cppcheck team." cppcheck-1.90/lib/version.rc000066400000000000000000000016371357737443600160360ustar00rootroot00000000000000#include "version.h" #include "winresrc.h" VS_VERSION_INFO VERSIONINFO FILEVERSION CPPCHECK_VERSION PRODUCTVERSION CPPCHECK_VERSION FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS (0x1L|VS_FF_PRERELEASE) #else FILEFLAGS (0x0L|VS_FF_PRERELEASE) #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "cppcheck core library" VALUE "FileVersion", CPPCHECK_VERSION_STRING VALUE "InternalName", "cppcheck" VALUE "LegalCopyright", LEGALCOPYRIGHT VALUE "OriginalFilename", "cppcheck.exe" VALUE "ProductName", "cppcheck core library" VALUE "ProductVersion", CPPCHECK_VERSION_STRING END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END cppcheck-1.90/man/000077500000000000000000000000001357737443600140215ustar00rootroot00000000000000cppcheck-1.90/man/buildman.sh000077500000000000000000000006641357737443600161610ustar00rootroot00000000000000#!/bin/sh # To install required tools in debian: # sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive-latex-extra pandoc manual.md -o manual.pdf -s --number-sections --toc pandoc manual.md -o manual.html -s --number-sections --toc pandoc reference-cfg-format.md -o reference-cfg-format.pdf -s --number-sections --toc pandoc reference-cfg-format.md -o reference-cfg-format.html -s --number-sections --toc cppcheck-1.90/man/cppcheck-design.docbook000066400000000000000000000130341357737443600204130ustar00rootroot00000000000000 Cppcheck Design DanielMarjamäki Cppcheck 2010
Introduction The goal with this article is to give users an idea of how Cppcheck works. Cppcheck is a static analysis tool that tries to completely avoid false warnings. A false warning is when the tool reports that there is an error even though there is no error. Cppcheck is a relatively simple tool. I hope that this article will highlight that it is possible to avoid false warnings even with simple analysis.
Limitations of static analysis There are many bugs in programs that are really hard to detect for tools. Here is an example: // calculate the number of days int days = hours / 23; A human programmer knows that there are 24 hours in a day and therefore he could see that "23" is wrong. A tool will probably not know that there are 24 hours in a day. A tool that tries to detect all bugs could write a warning message for every calculation in the program. Then it will correctly report that "hours / 23" is wrong but incorrectly warn about "hours / 24". Cppcheck will only write a warning message if it can determine that the calculation is wrong. In this case, no error will be written.
Control flow analysis When you review code you will probably use "control flow analysis" in your head to determine if there are bugs or not. Control flow analysis is when you try to determine what the possible execution paths are. The control flow analysis in Cppcheck is quite simple.
Buffer overflows This is a simple description of how buffer overflows are detected by Cppcheck. If an array is accessed out of bounds somewhere in its scope then an error message will be written. An example code: void f() { char a[10]; if (x + y == 2) { a[20] = 0; } } Cppcheck will report this message: Array 'a[10]' index 20 out of bounds No control flow analysis is used. Cppcheck will not try to determine how execution can reach the "a[20] = 0;" statement. It is assumed that all statements are reachable. Cppcheck will detect the error even if it is really impossible that "x + y == 2" is true. I still claim that this is a correct warning because the statement is there and it has the error. Cppcheck will also investigate function calls. But then control flow analysis can be needed to avoid false warnings. Here is an example that logically is the same as the previous example: void f1(char *s) { s[20] = 0; } void f2() { char a[10]; if (x + y == 2) { f1(a); } } Cppcheck will report this message: Array 'a[10]' index 20 out of bounds If the execution reaches the function call then there will be an error. But if the condition is moved into "f1" then it will be necessary to prove that "x+y==2" can be true when the function is called from "f2". No error message is reported for this code: void f1(char *s) { if (x + y == 2) { s[20] = 0; } } void f2() { char a[10]; f1(a); }
Memory leaks The check uses simple control-flow analysis. The control flow analysis assumes that all conditions can always be either true or false. It is assumed that all statements are reachable. Here is an example: void f() { char *a = malloc(10); if (x + y == 2) { return; } free(a); } Cppcheck will determine that there is a leak at the "return;" statement: Memory leak: a Cppcheck doesn't try to determine how the execution reaches the "return;" statement. It will only see that if the execution reaches the "return;" then there will be a memory leak. Lack of advanced control-flow analysis means that many bugs are not detected: void f(int x) { char *a = 0; if (x == 10) a = malloc(10); if (x == 20) free(a); } Cppcheck doesn't detect any error. The "all conditions can be either true/false" means that cppcheck doesn't know that "if (x==20)" is always false when "if (x==10)" is true. So Cppcheck can't establish that there is a leak. Many other static analysis tools will probably detect that there will be a leak if x is 10.
Final thoughts You cannot trust that Cppcheck will detect all bugs. Cppcheck will just find some bugs. It is likely that you won't find these bugs unless you use Cppcheck.
cppcheck-1.90/man/cppcheck.1.xml000066400000000000000000000603621357737443600164710ustar00rootroot00000000000000 .
will be generated. You may view the manual page with: nroff -man .
| less'. A typical entry in a Makefile or Makefile.am is: DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0" manpage.1: manpage.xml $(XP) $(DB2MAN) $< The xsltproc binary is found in the xsltproc package. The XSL files are in docbook-xsl. A description of the parameters you can use can be found in the docbook-xsl-doc-* packages. Please remember that if you create the nroff version in one of the debian/rules file targets (such as build), you will need to include xsltproc and docbook-xsl in your Build-Depends control field. Alternatively use the xmlto command/package. That will also automatically pull in xsltproc and docbook-xsl. Notes for using docbook2x: docbook2x-man does not automatically create the AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as ... . To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be found in the docbook-xsl-doc-html package. Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` General documentation about man-pages and man-page-formatting: man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ --> ]> &dhtitle; &dhpackage; &dhfirstname; &dhsurname; Wrote this manpage for the Debian system.
&dhemail;
2009 - 2016 &dhusername; This manual page was written for the Debian system (but may be used by others). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or (at your option) any later version published by the Free Software Foundation. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3.
&dhucpackage; &dhsection; &dhpackage; Tool for static C/C++ code analysis &dhpackage; DESCRIPTION Cppcheck is a command-line tool that tries to detect bugs that your C/C++ compiler doesn't see. It is versatile, and can check non-standard code including various compiler extensions, inline assembly code, etc. Its internal preprocessor can handle includes, macros, and several preprocessor commands. While Cppcheck is highly configurable, you can start using it just by giving it a path to the source code. OPTIONS Analyze given C/C++ files for common errors. Check Cppcheck configuration. The normal code analysis is disabled by this flag. Show information messages when library files have incomplete info. By default Cppcheck checks all configurations. Use -D to limit the checking. When -D is used the checking is limited to the given configuration. Example: -DDEBUG=1 -D__cplusplus By default Cppcheck checks all configurations. Use '-U' to explicitly hide certain #ifdef <id> code paths from checking. Example: '-UDEBUG' Enable additional checks. The available ids are: allEnable all checks. It is recommended to only use --enable=all when the whole program is scanned, because this enables unusedFunction.warningEnable warning messagesstyleEnable all coding style checks. All messages with the severities 'style', 'performance' and 'portability' are enabled.performanceEnable performance messagesportabilityEnable portability messagesinformationEnable information messagesunusedFunctionCheck for unused functions. It is recommend to only enable this when the whole program is scannedmissingIncludeWarn if there are missing includes. For detailed information use --check-config By default none of the additional checks are enabled. Several ids can be given if you separate them with commas, e.g. --enable=style,unusedFunction. See also --std If errors are found, integer <n> is returned instead of default 0. EXIT_FAILURE is returned if arguments are not valid or if no input files are provided. Note that your operating system can modify this value, e.g. 256 can become 0. Print a list of all possible error messages in XML format. Used when certain messages should be displayed but should not cause a non-zero exitcode. Specify the files to check in a text file. One filename per line. When file is -, the file list will be read from standard input. Force checking of files that have a lot of configurations. Error is printed if such a file is found so there is no reason to use this by default. If used together with --max-configs=, the last option is the one that is effective. Print help text. Give path to search for include files. Give several -I parameters to give several paths. First given path is searched for contained header files first. If paths are relative to source files, this is not needed. Specify directory paths to search for included header files in a text file. Add one include path per line. First given path is searched for contained header files first. If paths are relative to source files, this is not needed. Path (prefix) to be excluded from configuration checking. Preprocessor configurations defined in headers (but not sources) matching the prefix will not be considered for evaluation of configuration alternatives. A file that contains a list of config-excludes. Force inclusion of a file before the checked file. Can be used for example when checking the Linux kernel, where autoconf.h needs to be included for every file compiled. Works the same way as the GCC -include option. Give path to ignore. Give several -i parameters to ignore several paths. Give directory name or filename with path as parameter. Directory name is matched to all parts of the path. Allow that Cppcheck reports even though the analysis is inconclusive. There are false positives with this option. Each result must be carefully investigated before you know if it is good or bad. Enable inline suppressions. Use them by placing comments in the form: // cppcheck-suppress memleak before the line to suppress. Start <jobs> threads to do the checking work. Specifies that no new threads should be started if there are other threads running and the load average is at least <load> (ignored on non UNIX-like systems) Forces cppcheck to check all files as the given language. Valid values are: c, c++ Use library configuration. Maximum number of configurations to check in a file before skipping it. Default is 12. If used together with --force, the last option is the one that is effective. Maximum depth in whole program analysis. Default is 2. Specifies platform specific types and sizes.The available platforms are: unix3232 bit unix variantunix6464 bit unix variantwin32A32 bit Windows ASCII character encodingwin32W32 bit Windows UNICODE character encodingwin6464 bit Windows By default the platform which was used to compile Cppcheck is used. Only print something when there is an error. Use relative paths in output. When given, <paths> are used as base. You can separate multiple paths by ';'. Otherwise path where source files are searched is used. E.g. if given value is test, when checking test/test.cpp, the path in output will be test.cpp instead of test/test.cpp. The feature uses string comparison to create relative paths, so using e.g. ~ for home folder does not work. It is currently only possible to apply the base paths to files that are on a lower level in the directory tree. Report progress when checking a file. Match regular expression to create your own checks. E.g. rule "/ 0" can be used to check division by zero. This command is only available if cppcheck was compiled with HAVE_RULES=yes. Use given rule XML file. See https://sourceforge.net/projects/cppcheck/files/Articles/ for more info about the syntax. This command is only available if cppcheck was compiled with HAVE_RULES=yes. Set standard. The available options are: posixPOSIX compatible codec89C code is C89 compatiblec99C code is C99 compatiblec11C code is C11 compatible (default)c++03C++ code is C++03 compatiblec++11C++ code is C++11 compatible (default) Example to set more than one standards: 'cppcheck --std=c99 --std=posix file.cpp' Suppress a specific warning. The format of <spec> is: [error id]:[filename]:[line]. The [filename] and [line] are optional. [error id] may be * to suppress all warnings (for a specified file or files). [filename] may contain the wildcard characters * or ?. Suppress warnings listed in the file. Each suppression is in the format of <spec> above. Use suppressions defined in xml as described in the manual Format the error messages. E.g. '{file}:{line},{severity},{id},{message}' or '{file}({line}):({severity}) {message}'. Pre-defined templates: gcc, vs More detailed error reports Print out version information Write results in XML to error stream Select the XML file version. Currently versions 1 and 2 are available. The default version is 1. AUTHOR The program was written by Daniel Marjamäki and Cppcheck team. See AUTHORS file for list of team members. SEE ALSO Full list of features: https://sourceforge.net/p/cppcheck/wiki/Home/
cppcheck-1.90/man/images/000077500000000000000000000000001357737443600152665ustar00rootroot00000000000000cppcheck-1.90/man/images/gui-newproject-addons.png000066400000000000000000000502311357737443600222050ustar00rootroot00000000000000PNG  IHDR$sBIT|dtEXtSoftwaregnome-screenshot> IDATxw\e8WT@qQ.R\q0,Kqe+Y6,r;3'ʕ)@Pa\E{/2z><{|?^> TZj̝;wu''<Q ]LSNxܿII2t'9?յk> NFQF1Kq 9::/\_~IG%yt/..h4d2eu4DžڴeΞ;'WWWl\'Oj=CI=M(I샇c7l"nnZ4ZmZ5jO%b3L8m&}>c9?UjT5k鵞=G ~\(ܹլ.$ 6\٫OL3uHʿʔVz%6zS?_ɓ_YWz*T Tۖ F?Hڴ3n!}Ol;d҈z{@uo<:}Oclٴh2u௼Vo\QC9P$_ۂ$I~ߣe0t4$D7nܐ=RDjT@y2e$InU)*XbܵUUɗfboܐg)\׭wvm̑C mKƓc/˽e+&&V5UXnkzؾ'zrrrҷsx }g99'AC?hw5w4KHwNZھsF8A^|J988K/=PK ն_ӻ#GR 4~hVfM/w 5_O111r͓GwbEJf~Be2zWӧ5kR-..N)ɨKX] [ӡ}[}vڭ-+giϽ}ҋI+6:u$?yRS>… i՚= rkN=zWtM-Z+_y/'NTҥɔI3>DCin`GcǨS>~XH5U+e~0R];wRhx;W쑳BzފsRD$EjO")h* }Ӯ "nnZt._jlNg]Eڵ7iѬYiZ⢿G .Kn}4nUbUS0}<..r03gNz4c7־];cf͝=+ɩ ٳ tI2\~=/t~Tnڰ~ *$'GG-q*T(%Kd`2̗h?JxrN&Ο{˛7gx_ءqcFlٲt~enݍ7|S:xfVoW׮]U|\WjTH~ ]#4z/U[exbEkj[ppGK<(^|6TbQjִ\\\jܹsSJ{{_ۿgwĜ2bɓGI̽2LZn>6U-{Nҝt:YYȝ;}oiȇTfMн k^0jAZfԩc$TǏ%KUJML1-Ie+TW_sȳԭ6oܸCbjZAƢAA+̯EGGkTZuU\USTT$i5oYN}T~CIwNLK־c/*VԾC'֩۷5q=S}ʩGݷ/Ix_;~ VjU5pL&]rEow{vk 5K9Jiݒd4&(4,LF_a?pF$hϞ=~,-YPŋOӘȑCk{tE:mܰ^3n$i'rԦuk =}MK־c/ z_?}Kmۢ%Jcr*}5 ٵC ԗN)^Z%齑hƍ՗n%K:'3CLL.^dt$i!2 C$:o6oܠjUs<9mΖњ }CVogϞ]ԺMkIRV~zH._E3Ԡ~}UPAC1/oի5[}zN=S=jW/2I,۵ӳrww׫]pAW\hTM%Jqƙ0:we?Ҿ+碭z23g$9:쬞=^Rm$I<}6[)O<:vxӷ׾]6s{ZH]vMFQ UP!IJ5q-Z^;>н.^ٲ)hJ-]|lٲ>x&Q"n:$sZ:ot7^͔bOHHH}[0Y/4v(5kTt-2R/ТFy}7gƍ}K{5pV.SjwDN\rT ̙SL.ݟOz7Z\ٲeK\z5y^saнTqNг~MԺUK}0}y*eS;ۛiZvVZ/hUsҸY=ڲoɀmc+WTUH]p^5jV׍7auаP%$$vZɴkX~2F4o.7$g}u-s8cҥ^Ϫgϗ5pyyyZ?f4,sGZwJiLN5sQdLI , 'ݿ>8h*>o\my*V-SԽk<ɖu-vg=t9%$$zjVǙK.xԠVDFLi?)O9jaz4_;':~JF1AUHH[ګ^-}4hxgxeSn]5x0fٟSJugMJ+jɢ+5|vZ4oUb"&W~4.msnn7dB/_Gw O<~`H 9::Z-]Y_JTըFˌ@j&'fNef< e)7*V 'IΞ=;~zZlߧ"nni^fZK 53Lì;ε)!h۷ok}ݹ:wo;.=ϝyuYӾ-cR/In%GM&~+J&JX=wmUFu}?[֫LڊOH5Ka9kG[oٗ#.+88X?N ??5k*Vx`={$wؙ|TkC;c\v*͝3G#?1YX+=?iY@F˒%ڱs܋Riooyh/&~g8ibccӴxyz`0˳on޼XʗWewU-ZT0Zׯ_Wioo}dxnKN>o%M?~5kDŊKu{oSHU\YA˗vZI3J'O}ԿEsNg;?ڷe 5qҫ"#J*ZzefZiS&k̸ձSEEGS#Igo%I_~_2x$i1H_͜Rψwʼnm IDATct%}t}t=l5U>}F$UaB5oݺuKeJ{k1ֵ>JҥKj٦._5j(h2eϞuxۣr_"(Ųw4/_>v'OZ*Ϟ=L)'+ݽn2/Һa?g͜ν}F` K>nVfEFEٴL^WWUQ3"~eȨ(zM?)%􃇼(!!!}1C=J)**JGCB=],f-Z?>5k۸ -]3IOҫR^eTH1uu4(饼 E6 9v!G $S͛⏓ɓ䵔.\_zOH&N- Tzui ?y\ujR6mqټ̶`Zo {ۣ"EԲU[ƚ^VʕMt!L&I/4kl}:|p [qO,SuQu˫Q뒤 MTٲe KF}R%K@p99:闍L4Uo6[xb4i5:aoS = Νu 6+m*wѐ$_`,IУ4g]xQJj/G Mx5sַ|'rpH ʑ#%9ifk\YϵhV3g*gΜʙ3_d2U|R> dI>w\EGGwʕ+U888hgͷҋϫF)?[ $IgΜdWzޥ,_54qeסCչkwٳWG)xeJYfi21cԯ_?ک]:w꨽ñcSg24w ϫ|rʖ-gϞn^WW]|%k׮E*GnW5k!>e,P@ժUաCհ_6{eJ>}hĉڰaN8vY}zjԨ.w*Tu>4Y>k T\\ԍV &y-_q]G_x^ڴWppwWfs2)Ao޼|||zj 2D4` Y5k5Zx\]]ͯ`0(,<"lZ&רA`2,AR۴ۓ}lJ Oq P߀5ko֦u'카z7ڌ+zk*V6^ŊMu($GGt2,AyX9sY[lTBB$iŚ9smd./?kfVɭKyL0Q[3!B@z=5֯_ UP!պE1^ 999ɽx.9}FUY&qQQQ3g.^RJ[ -V*OP6ml.s+ߣFM%o)**J}+*^BoWw5zʔܮUf|,66VoWKD)o <4ؖZ_>y[5;PTbIJ5v5n*TzMuU'I>>Zz "I0`@f:E{K[Ѻ}3Fb%I>MQgˆϩ06VAK/+4,LU,'-q{Zzn޼˖[.2 Vx;88( @FQ!!!TswV(Ue˦]w'[Jxx*㣼y] mҥe0w>g0\Zjl}0jbbbR]OZNt͚6VYVsz;ؙLI\Ϟ=Ϳd>^/KqYG(wʞ={IKܒ榐>sF'N$e˖M:vQbTRF@:ɴU7oգGZmNmZ /PMQgˆϩ0+ZԦRꧭm[j־2V$n#4u:w>K`]}NUX1I=t<>>>rrr-Z4ךU+gQcc.7U%<<Կ__Q޼|KmkR'Cn]m6]f7GG꫙h*P zSCt!$*:wChHbccvOi@KIZrUh]tIuhrέٳKgɓ*RMaaڲu.^ ',ֶ-go̕ `Y35u$-[MIZt\\\$IMD[T.UG7no۶$9}/gXѢZʔ y)? Y,jq[kwXJ~\`ֻ]6*\*W^#߆ 5lujzZף[}~6ڳ7@2\tdm卛6CǎEUttɓm)W| ޲IM׶*H@5mg"z=eDy)"BTu7s OsT?ofsɓ|r<h4 Tr/饣!ǴxǺ;U(_^˖du$A`H$t :v;@ A`H$j}i^fEʰ=2*Jo2{woXg;o++WN}Rhdc =dX.IyM,&&F9s攃ugٛL& 4X,՘ѣ4~XkΝLs'L`m4=5֯_ UPbVDžon*YzZxeM⢢4g]xQJj1>!$ɵ签r/ᩢ%ƛ}y:xb=jԤ\Pi UNsJu}@Ȓ}ܹV޽+W.o޼GCԬITҫΟ];Ӟ;uI ]sy||^z :D׆4jm[:t`C ȔSccc`UZUk֌3~Y?Oh4{>sFV_'^N/`nS&u[$ӧڷo7|S'Nէ/.ΫXѢ9~J];). E6o\>>>ZzN8!I0`ʕKe}|e6լY#:$E^9r<|dLA2 QJԨQ#MΝ?lyR%-[6ڽF2&q={4 `ݺvQ6mTN]n9ÇW3y'OZ/ }ƍ:xVZjnnn 3gt4 i 6oެG6/o04{LM2I˖qfjҼ,]&Iwn׶Mku%*R\]nvmTPaUZCGoNSxzǴƍ?t/<]/<=ٲlٲi 4qB/^xൢE#iӆu6/(ժgT#IDAT:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@@:$   tH@@hYR+mwuiE+{p}}9*lI z%O 4@@:$Pd5s֬XX?P oEt1z/<ʇgƔY hdkwQVt^E߃YTvn{eOEDl"үxxF_gkl-woQa}%@UXZ4q(Twv&yKEQ*:pט>y]Kp(n 6o lb[d}$@CH~m s[RT9EHT3B(R@U|r!+Ղ>@) Wп@oT־m6o޽Gle>|D~zw{wӪZV.߯:+zt;Cѱ`OO]]yz[o.^x]^)W0Z7.nmqiřg}QL<9,Y*Ūm8(Nr9~rSOO>%λ;gN|M1x]㪟)qQG\ jj@{&N[o5zݻwqvUi7>*[n',}brt%"" GO8>Ə7v\իcN?Vi͟U'{l\ri {lE?``<8ScmknȑFQdI}uTU^qQxɒOGr/.2ۅ^GsLlѹ/"O{>ƫElAŮDZǟ..E[+VO= 1|D;qb̝Vyq+3fDQ̘sN<31lذ8#!@\;x1~ܸӧOEt1oxI1W_O/VN[v[Q.cXgzDD|k@^՟xv[ ,]~;vme-;'N.]Ɛ]v{;6jZ0_+_ap_~1i#M3FCM3^ݻ403[iӈX0n/~7/eK|fuW=?5c^zlz""i>wL޽b֋'|rcWr.ƌ+/,-|s.om(+x㎍;>>øm@hqw@ՍCw ^ӦM/No.]ƬYL މyA̛79c6?1sklxcƌWc6=4ƌ|Xx1q}ϵFŔ)ONJeN1hmc1cFL4)=:~}qEJ|s7p8.9s+%*RV*EUW/='ث.'nh cacnjې!/ư|/$40'/N~rqG(>Hlv,XFߏg.Ti;*? Ⱥ\.P*555ň{q>8ehh0С&swkμfw`f{1_Wv t*F\DTNTSSrM}ͣ\DM Y:DC{m^y 6$^>-RD{HQ.1ѩS&cӸ1{FZtuJ)\.⒇v=7T߃TWghѢZ6:u}w_sijo.lݶyls8jm*:O@5=@@ @:$   tH@@lOAIENDB`cppcheck-1.90/man/images/gui-newproject-pathsanddefines.png000066400000000000000000001071541357737443600241040ustar00rootroot00000000000000PNG  IHDR$sBIT|dtEXtSoftwaregnome-screenshot> IDATxwXwaU EkTTT{l1Ėk4Ec71&$^[ػXPl6au+>{=3|ٍΙJmժU?SBB 胈*X,..**ʼsΥwy/^pXM>|;cּ-g*um7nw9,nsAmƢ6nR^y!""Bm@DDaCס 9%o7@P:k԰Ʈ`bbR*uJ `¸h߮YYYغ=>y6.\kkk<\^y!""n|;iX4o.z,\|g)WLЉHe5aoob#p9&bo;̞1999u:>{32FDs{hԠ֬V̹s(**F-0q8ԨQo>z =%zvxDFE&L mK}+_HtOtZ˗췢Db1&Lb?~ zrc;[[h惽0rpXZZ`-073C`Wl8uG]%/k@<=Eu`8:wp=܊տe#>Bzºӧz,\ ӳdA^Zw)Xfӧ _1BDDՇ[ݺz M/@ )S:k^&DDT~~>&'hՃ [ofMbgAdT:uD:us8~]uŘ׸q AvNvރ,,[0gB=~>M{``meQT\苗4ujF-GW k;S+q(/ܺ 'O!l.w'000/p>9tX:s"Z͋:J؇|ܿ?n@M{{_J~OύЦuk4mRl-8y4 ?U&NBB|6y Q˗,*wLЉH/g̔&^$1Ÿy6"Цuk,3Za&̞1]j+1Wq%* 'cF޻瑑d=~|b_9K}9&/C{nRf?:]i2+ج,-?@ar>MB‘!aמmڷk;;[?y yy066''@`Wܽ3/qr(o//XF}2$VZwz%7suq? ITTt4~[]]S}閚X{IqRrt,wLЉHF ©#,`S6 $)U{v$++]@hN*{ ݧ%䚢,6+KKɒge .ľ#?j֔F__?ęsЮ[Zde^ԪxruvqqdffVJߟcySSS1Qa 4?P0A'""ތLUbr"A__--s'?2Wg$$&J[Ŧ]տ"3 4H6}{6C! 2/bժF044PZnݥy;bP JnK{ MI9w>><+W,Q]LЉyΝB~rssn^͚R8|AOO)]*;;'G؀P)20b_ udnSl9~Keѕy);^xY|B"sТYs]VE^߃P(Ěc$w'"m_I+M{c/U/t""t֭pY̚nuc.aᄇ;:匙hܨ!222̟= ѡ/aia SZk!>!bG|X؅B!pn=ƌ0p"ܶMuPS6A8{.=wqS.wߑ/Eu ܊Òߣ=v=U[ڮÎ۹  +/H܎'/Y$9[rrL%]?%H 94Wca֭puq~}|3 >1QQ000%_OG‹X8p){7+z꤉ptp_C06&wuyպ&3+N]}RT޽ظe+9hTreu+R9`Q\\UuH9vMj) \[hҽIZ~~> vmKiS楠r""gЉe{MP+9v$=k$煈HۘQzkU`O><[8/DDDTի8t(5hdܘqeãJiA'""""""9. p/Νr#*^V@DDgЙlSZxp\8U_;8Fq'""]Ϡ:Qw"""""""%7mP242F fZD(.}MܵKVǎYMj,uCppΟRL4DRR͟ĊvDנ;;;.b]ń 1v8q}͛@MXe>7ŽwnZ8::lݪV=VƅhDĹg`mmukj\.|;aW$a (G0pP)}P5*T Μ9[:*K~~>|¥h?˗h;J'wᄊCT˖'?ʎ?]:>+"ƷXJ_{FTZ[=EDDT]hxB..Βw<#t:?*rb5Jc &LD޽`aaR I/I޻{'LW-GGÉ'1˯vm۔llܴM4k[ 9s;B+TOy!>t{VVU.8u Ξ oooC/vh.777oAÆ PNmmSi"퇝"û v8:ݵ;<>".,<{vKkX[WJ;DDDoϠ|G("//|3^015G]7π۷?̝O@OMH۲y+Zl 3sKo>>abj:u1u4kbK.G۶W=۷_Rѣ3\\`„HN٤ ͛JĘ1N=z 2gb`Ԩ5 KkxoqPCs&}NjV-]8~D*`aaKQ (wB26/SSSm3b1"ѳG[oa歷^333ŨH]ۡT{#uLK6 bk֬1̌y+c,vE;$"? hFMp% Am7ܹ{Xx vG7 pr#Y L|*4kM|0iTdfl[PP.D[! Up,]gΞ Wb/ox/rss,n߱-ٳgppr͛$9sN.sss1iT4j >[boP6$`ffxۨ`֜nгO_%򪔿|*={#йk\뺣y0{<7j'K.Rsݰ1~Y=zg=Qo4EHPBPv)+HEd /A붾p`*6/9 "" ޽{@_h3s QFb:&&&%bc_K=x@t`HJJBzz:=KH$B׀ppp@ڵѥK(k~|HMMœ'O[+ѡ}{4jSL.99BRC{ng&Ǟ={ժS^Ww 5)??sP\\?~SǏNflڵ QΠC"q'"99߇C@bb"fϝ+)z7!g\8qcǢN:ظO|>e2|۵GI8q5%;;)WJJ `XR%KY1y)ϧ}(_mgggreciSN0ad$\]?‘C*u6WQcTѸ<{ c>CŨHe/Ss #>!3gѱC|6a"D"ZmiJľ :v耈}ʕ_1*.YjΝ}ݼgao 7ETouua4נuvv™ӧzPXڬc%BFF&QXXR[? ϯƏؔ߹`I;xhݺؔ?~ӧpqq F❒;{xJE"by-;{)_1OP}$޻׷9fJ?mѢEs%P=r(;Ξ%UGpq): Njƌi_~URؼe+vYtTT5kauMMMq'6LLL0hۨUŮ044#|.lߺEdۻ̛@VaCi&;o:wE^=͌p[Wzs7"w{ᆪRycTٸ?H?JkHNIAvvJmi'Op!:.(9̚*=c{@*m߽>Jr-xzx*7gқĹ8VZesҥ+tCprU}nل4iŋܶ"m W$;R8wF̌t ?E(.*p[ԯjB-GG\¥(\ qX0~Н;DRmqqq_zV[mZNu0ryfbM0O%I^woKڷ!1iTٟ;w͛ˮKVƍaؿf;uSѸQ#ge=F[ݺճH9TTVR+,(P-MٿjԨ$'iӦ]SZPEp%|0b|g=xEEjǩxgNta4I+7+}8~]]ٸqWVV%XmD"^Kzt=c73~۲=/O\r_啭Zy:d ԅ!Ν=޸BI[J&.B/{8R?RrR Dopb4*]9s@DDT8^ypQ$''cѣekxnVP_BlݺHMMAXPeo{{; IFDD}{ee'Ocа0(ǟVٵ6\j;ヌx9|vK[}`a,E3i_~k|Wk5j+ۛwŴ/+1õX?p`ccHs9998sdrM{{ܺ}=BBbbC%' //E3}:ŤɟHipEk_۶G8u4ھ]Ҿ-f$VӲ4p6nڌ+11HHLĦ[0kÕ#G4̘9 FFRAPV^q)**BΝHMMųgpmPaYY~dQںB IDAT-M8?o;_/^J?e}J{PfM{ܹ{NFjj*_<}Z8UoU.&i4zaxar~ ] V-G\*&?>UŅ싀*,5Yogv?b/^x [$-E eWv\Ů~GGPW,\0 /#F!#96m=.͜3{&.^}OV-G A=K,2/^ 3Ok֭GN]аAAx͡~'O"&O?w6u/Wcg/YW|W|ϧNo?ݒOץ+jԨprr+*=i]x91d v))٧вE /S4.Ϟc_1YʂB)r^eд}ѫgWn:i{#"ߥOYyuR21n'1j4M1(d 6iRXUoU)=5믵>DDD$HOyzC 8d j2UgE.ik>._fZĸWVV5V6ǩ~&~wrTPVSvi{*-Zh;""W'g礌cM,YU]N~IDDDՕ :Np-D"[DDDDDz8"M;|z폡CqFHr,qGhUTnp/1^aP5r3jʉʋ=%DxR(q8wqv'"" 'tuH$o"Ν Ǩ vS}"B#S?qR|I[ ++ui}|S:Ģ|tDľ Mm/^BAU.Y3,D`^ qvr8M+1q8yTºu#6z(J -A10$RۈD"޽!Aڵ Ǩ)]U٣;+!2ѥyoz<v(DDDDj;]% ,;qtء\uZZZnb\emCaoENN.|<A>%DFd4Fm>D"Ē +!*z"0nX,X]j;""""zMUi V6 nW_πwCtĸ ,:KjV6 .DK+((YsPa԰s@';)զ ΟB箁0A@;N8 )6orByyy2OHP)#S ɲL|XvC-XdddJ3QatD`^t2 V+Yc?!;wf=hٲ֕LᎰp4kf5QӾ j'N@hd*{9n3p_V<ڕwUbɥjIS+gUQH{ zY.o>Um??e2e˔cAޱٲ/Iu}Wy'̙zq """"*MЋq5ZΟ;OѲu[?y5OOÇv̜1~~QKѯ<'OBV-eRnnnؽ3L>=CTM^HpFD kjj~= =$cѳgwU[`ߋԧŘ8i nݾt?E}g8s,kmW۪;v[`,--e˛OU۬Ϗ"ʎ͗)_yǂTY`.?f>Mѭg/xBʎge}EV-qaGDDDDTU~ C׷À~剩S&n:-JLdC[`<=<051HO֬+[o-f~3͚_VKݒEݫ'օj]w&qV68x(FCe֛r< ) /?gg8;cggOٳtOZZj kk+t c}% B!7n$y_߾3g7nD\|Y3D"áfMvuEΝUnCpw R+Ҟa_۰h 6IenqUX,FXN(OuڬϏ"ʎ򌯺ǂ:/P(Ďp*:Ts-}QVoݛQXP(s{}}}4۷HK:u7oJ'}ٛ=T~zՉ$#S Z`aHNI}y5},UQDٱ2ePcbXᾊ+Ulhh|WJ p% 7nOOO=wNjgϢqFrj*ɚ:ԍn:044ĹH7RGGGGس74@n=w;fldgg@`YN8ڮӱWVV$)-]Y}WK\V~zy+XargEeUR:ʎ͗)_Ue굫hԨ}W9-5 U"""""|~￑TL|*10$6650t\L,n)\qc7nARnUcԍG& :"rssqJ vTo&NB\|!GCrJ ,\GKq|+b`L2o@NN"y#tgEee}~^Vv>/S6ʎSI&N3S3)WJĠw烈HOП>MFU!p066r!ݫ:SǏ*]ۯ_Դ&>-0rG*] un"C {Gg : WwU?Mε0y4ܿ_>vuE=//=z $=|ojZܰ>[vލ7J_\A>hܸ ]1ud 6ZjYh޼sfa`j+b)˕pU >[;"㎰pw rDz$YcYE^e}?ʎӯcG@vvڶ xW/Em);9''gE"0A'""""<%APp0`irerproWU^U02?"ϯJ[x BwB ]x,,,*L̬R(m|~41b߰l#L_]t D,+Z~1޽fI---u.9ז59/..Ɗ~Ӧ29'""""a^Es"U֮%ƍQemnN><;^c? e11ӴJ۞J_JU&D^VNN֮]WJc š5kp5mBDDDDD$+P<ՇIxp=:L$NtKtn9ݻ'H$Ž;t UYZZ*D^qw"" Qשn_>s-EKUΠ㫯ѵkW\xQvvv8ts!-- {p]w}Wɓ'G ۘ6m<<<C7Hڛ2e ZhsssmQQQRDFF&&&GAAf̘///XZZcǎ8{TlBǎS4DDDDDT :++42W38Ņ7i?,حLHT3*KǏ{b͸{.Mwww EaiiYJ 2{EAA3<<Vٳ!0sLy󠯯3fHNJJ… qej x9Æ ôipucҤI믿qF$&&:u WxǦCyH((mÍs_-(*5LٳgXnV^֭[ݻw}b1v؁V틼<=zpe$%%a 055Ś5kb [˖-add$F~ضm|ٲeӧܐ_VB6m`ggٳgyXrZ}""""""'+30V8Y<fv\x3G~߸|b&2T*JAH$B֭… x1[ʲD^ݻ#,, ݺu "##ѥK|5jL6mmG__͚5í[$ {i:v(yO SNvDDDDD]m}zA/=*=OZhh(%g+R]vA$!<<C Q +++!,**2ym Q%V?=c##Bh]dkg{aqֆ]_g)Wd֥<8]_T% '(<#,k X,Fhh( Tdӧ222aš_~*+WbѢEX|9QPP˗/I&2˽ pO>ƍ""""">P4EFB$AT\ 'vʄ>Ɩ00xteq%Uaذa7n␝ǏK}#22yyyR]rߗ$K$wEhh=sss_|z!9KB5 &M_|cȑ(,,Ի~$''#%%'Nԏ e`ȑOpeddd`ѢE|2Ə_Q'""""" k&QPPb Ct[ܼ[灰.יު8٬U\,QcsJK.+|||ХK8::bҤIgϟ?֭,Ohh(v  UXX#..N!C 99Yrvem,_ǪXUɪuͶo> :|5kp044ƊH{"8T}IhzWJ_~ɩ:H;lmm! 4vD"lmmn%w"""""ׄ ߈E !Tr b"ܸ~6Jt.q/ꚠkj""""""(jܸ1^˗.!--M#5RH$@;deXʄXnkcHlFSM QJ&MWQ4iLF aÆcG"5%ES;{{ڢaE8QeeBEiFjz""""""*']8O1A'"""""" LЉtt"""""""H0A'""""""LЉtt"""""""H0A'""""""LЉtPS_LD-5Lj^* Bhd~x\kJ^IQB0x0e|::A$a xF-l,0}Lxy75y"(d.\l#P\\\XF8~D"233}zKޏ< 9oi1J@,#|N}5ڮ.]Imz$Ra juвe =t o~򚵜hV6W[R>sxy7e {zc/dc&7lۚ .]V>E>ok!,|'lѠQWb; 6o~ n8}Jйk ̭l weAyPgR1HDDDD&v z^=all .sTyqq1n߾.@GH$t {~'M۷_igعk $m݁E(ϑ&O͛WFOp)DE8LK}R5&UaoD޵{B^655 ooٳmU$Y3g`9#a9c::(?1eO> gΜp*w)SaPlc=n^M'O}qH} S? 777St|uƌwuuwnYf_{ eJEP@()nl 8:(3th R-M钴IڴIv4=G&M{roz_OA~MysںeQ_mo}ԴoߘIW}^umf8w8L޸g|-=<=?+ 9i|} 1rfҴiӴ xF ~/=/ip'Х\?i*okot[˗/Y_);y<-z-[ŋtnt׏~O}nOA͔ O}RYYQӾ}ZtzzzǬq5EffVq|pkjɒ*5stig[nmwuWU-ҳO?_yYjidD(G=wΝ2 C|rfN=U[,_~c'ҟt0 -Yxszۉ? p2}͚'PQI JᏞ2x|2c댕g?w(/7_v%ZS1]zŲl>564{NXFF$=Ьݝ:ݩސtGh*=\jvzF/ӝw=1Y>tú !)8C4MIbl"WeeN1?hHs<<;$)П|)|^}ӆϨC/}ΚKܬ;~Cٻ7Jw׫WV]ӂCMz)DŽvk 7~M6mܸIO}2*5 ?,ݪE(/񼁁=ﵿY--ުtvD}A74ŗ^oxhuSg~l_w;vWݯuuDΛ9snݪm߱cԚOg.Z[o][[|Ɨ\uÍ_ƍޡ;[7nҍ7^.Mܪ;w* iݺWm 7^+|#?3~x@"}OO!fҥKz͚Q8s ~ukk~_txK/Q=۷ءG3JMD{˳u޹eBsueƍGߑ{"5TH.Zu]ZZ[>%TRV׮#{0G{7iނ*} G|l6}Yׇd{~?\͘>C /~YǭIϔ%5b[F#~SY}䡨? @lp7c(/W/{B7wVc]0@ :q@ :q@pZ>hU̕W\aI_E &}=mY@hN4A t8@@ =fm*zb]Dʕ+׫;R[{$V4u>\*,,l 0I.>^6^aмUXWc**,:$PWH-)3֥L)**Uie w$OH{u[,IC{XΔ+5ݣy t~= P8rlf^X@b1M^yY<˒=ᦛ5Bzq;~֭ %@B2% f{KTA~~}Ey[F=¨`%=dmJ=SjٷGwun6m۾]k_|tij@6uCө;pۨ˗,4Ҫ?wJܦ_z^ IRRR-ZLӴ^WSSӘ>|Y%u~)iu3$rɑU^ ǣ-[5}^tB{m 2 C+W3$)l:ݮz *+gkUIIIji h|6j=:~=,A]Cu?l7ֿz]t**)7s:::?oSyY.:3u˭v.K>z{(VqQ500뒦 K+7'GN {}XyN?c˟Wrs'GɑnBMK8c; ڢd`G9wNen|aCEݣK҂s3hY},q?: <. Ôfa.gJ2 S۲1`nilTn~lv$[Ks ކF{()..:r-ǭ" @*).u @@ twq׫;R[{[K|^].X6_ahªX2&} -韀 &ҼEdʌu)SʬJUodYtH0m2aݖ:$"z!@2%BxlvO@d}tHL z!@2MS$T>}׼Z0^4=;[Y| Mv@ܜ 9p:[)SڷoҦ)%%E jЂ.yRp(99yB-[Ă ϠޒXc]w+lfSff\UVk&Eo*m4UU-`-:$I7pRR$VPHr:j;4ٳ+Ш͛;7rΉ(wqojjRSSSnׇ/Z[;RnK4-'Guzv?TÎc\SR~^ 7+4J?&絛CMnk<ѤϠ=c!#gq8y~mRiSY!QK : &yxeEqJ1eUQ>K!~TE "v:?oQXftH@1=v-.뒬#Q >6 /:$x]X>M[af0X3%v)mtH0n46*7?O6Md9N;kק k!tUZR _6oԼsTQ^})4Mڽ[*/%xZ2MSY 2 # Tffw`!f M.{ظGdٔ"4ק`0$I͙]뤒ceeeJ$efd(֦IRJr*͚3R ֦&)Ua]>Wn)S߁c'^ ҸnJʙ9C9Rm.˔nt:g''' Nў,4s`pv iw{)Sv]^O`P^G@@^GC13V> hB3M⒒Uyp>{Ųۇ>r,SGn^?]CFv=+ӧ:J)/gdcg̬ug:$ S}]i*99I6M]]c#2=9)ɒ!흝]yzܢ^|13m&:rb4T͘: Ô]WPaCP!shéكB=k&uwwk6^zd;qb;$#@@̙3TT=U.y1@ {F4wNCP0ئ̌!V?&ذʊkC ?opmK;=;Kӳ9|wGlc1^("@zhx: &cS6SߕDcE@c; X>~t@4y\n).0b]Δ`e<.ec ب<6IXL JQR\\uu󫳫3L n[.[EEA@TR\0 }^ ~+Wƺ@Ǻ@@ .t8]ܣh  b] 22 Z6=J6oެJKTZ\rQԤ6oւքtz-]8e,ӻe=JX'>+8@@ t‥?%gJZUZV.%}: IDATT׿3:::3ȗ5CTQ?P]T|뿪ݵ;f:3ȣW? _]'׫_キŢrifNWz?Izl-^v=*-7h?{$iӦ#;mvx\{j[ohƷ%IG_Z]Bu˦ ھ};eߴbµ0Qoii埻RW_ujwn/=s$)))I-TIq_ YXr(Ioh'_Fp{睧N;U􅫯ֽS8>n`@ah9c$PdCDc0}G;Ủ!4'~`}}}yY.:3uWoԬ xd{__|-U[FF$=Ьݝ:ݩސtGeãΚ-ZLwuwT`,b_G]z$Xz,o,l6u}ꟿ]B!K`8zWWަ&kҷw%+Ir UOO6nܤ'>F``@<{onVKK*==])))2 CW}=zMTj`$UPTYܕWkΝz/ҕW|nyw:O^9jƣ>Q뿥U탪ZL%ez|Z=t:߯ю;V ق{|'AZ%}֬Ygu = G?fӚիǕc E@ t8@@̬Xُ%ٙXHeYNzN0,{7ݿM@DQfV4 Gт c]`b;q@ :q{X V\y6^ݡ&)RaaecO`I.@c -Ċ䯯WQa% BhL.eJUQM,럀 M&|B;`M:Sο|/*}+k@}8Su=,gJZ䕚ѼUWa(9f&3c^}YӣQ7tfWVSO9vҺuz/R`Tdy:::^RK_^[?=/XZ9SҴhɲ?#r4oAHch񲓔PiY|G.֏j!bT|,4=kЖӊ ^'0$I|6=zf6_Wii 7}X> \{j[ohƷ5{{{3W_僁Գ<}{t]w[oӶ۵ηIvtk_7Dq:wZ|2MS--sWꫮTmzu9$%%%iѢ*).de/^t OJi*IW_ZM)5%U7z? #^z)|G%I7})Yn 7ެg>!3u3$rɑU^ ǣ-[5u^tB{m 2 C+W3$)=ݮz`"_ DX8[JJJRKk@3fQD=ḯO 2 #?a|jwI*Dq7s8-u]wkwѡP(1ל}}*/+>_q |50z{(Vy,c%s(ۢpWC4;t~ IR{Y;uS! >fnWܜ!`z,~??#N~ݮ?<~Ѣt]wO?Ϊ%ୁVӕ%ͦ`08=kgMֿAlxWv*hL~Y)nzYY$C_FJJ#)//f;6^RRd[u^QQanzͮ9 cl6u}ꟿ]B V@k@ l6^Z!5TeEΙ!뗕&ggg.Mܪ;w* iݺWmvI+ݥ^^n<KE>=[hnvUoݪng"n[_p״aûƍ''T{NLы/ͺ?Ԟ{#3g֭[UР;vyܬVUWoUzzRRRd5zl ,y߁R+|^eÒij_riZjrsr֣Qۭy/ M@{~ߪZP+?~Jۿ!/IEfI}R7nҼU?~[ WRR}LyZ\*fkTXPiG?y~J} M%}V7nPgXo.r-[auuviɒő?W3ª_uozKk~UdJ*ڵzwt_׿;wNV(t:LJR_ӧ0 dTOoLT+}u:XhZ`1/U,A4uvvvǺIs:)e^+G~2֛oNio+hnۮΣggelV:Tu/],q4wNl6q=1v6]nXZzVbKp1زuvviveOM۴Oa(5%E MԴ[|TZޚlkr<3#CpXr8PA= QMSݭ@-h0+:6e|~SR`0(ǭB{[t:zxi( iڴT 㽏>k:Q>ΩvUR8l{vIZ0qF{M6 bnwcz&cSy (Kܧ"%G@Da2 #L v]ar[6Lۭ=ϓnduIqΔaШt%E]W:::c]ΔvU\TdtH@%ű.c] :q@.zuBjkou)SS˥ @6 [XR䯯WQa% BhL.eJUQM,럀 M&|B;@@DDB;tH@DH/ L4A3tH@iʜĀޖju\ӧO^C ϗQ}Cgg+55U`6O6:$蒔!Nq0ej``@Wڴ4AZ0%O*:`R8%''OeKXPQ|!@[kL0knmlos`̝kJUAAzz{e]GdvsȻɭw'@"tGb 0vDj6ݗ ?fXSSR$I]߯[6Ms*+d;jiAD$/(;N)v*g)6p؏j_hANS9, (&{ne] @"sЧ<: چ>QcaE@KK#r0Llvr.0q- &֞FfIgOe ކF{()..:r-ǭ" @*).u @@ twq׫;R[{[K|^].X6_ahªX2&} -韀 &ҼEdʌu)SʬJUodYtH0m2aݖ:$"z!@2%BxlvO@d}tHL z!@2MS8{[yt:xTZZ&%)7'Gy2MS7hZ`U&{ LCJIIRIq:;Ҧ]wUedqTWWi*+3S`PaDs=, :$ ŽqHl6TR\$ tq$I93kwTrxL]钤̌ ڔ#IJINִTBr\ڿYsfWʕ>x~A~Ԥ"e5 *ݭ>e|;p`L :$ SsMI93g(77G]ݥ\v2sW׍_sC: #ړez Βܮ!n[==2e) (t(=z' MhI\R23Ң:*vu/_XvGeGk1HugeToPiq6̔LsAdjb|_TPv4M%''fk}DG8'%9Y9KRS#^ᰚ[+;z"x^GNlӳUoa>=[V8l(Q 6d6zp85={PGaۤn͜1#rfQ}C^v13B,qtDеrsh9s иG5P>1qvh VzZΩTRs5Y ۔1VYQv9v-^p< nmIsggizv֐>sZtmc~,F EtH@\ ADdl>fhX>~,qDKǏ&-0eeF˙v Ôl :$t[{'&)4Q: JNu~uvuƺ)rqȲ1Jc]Ǻ@@ .t8] RקtKA@+l*֥L)5;__BK'@ u4o"2c]ʔ2R՛6Y?L{GLwX%wL>^6 !A'k9i2g.0Lӌ塀z3&#p|5b[[0@`PgG%?Xw6hPOOTZR"+=[ (]#8n;ho>Uoݪ%KpLvŖ#@"]M0CmYy!m @"":$ S"fitHH&{xN@d̠:$ 4eF1Z))rTP/qNdtH@蒔OeJ𮊋lP~~Z[VrRJJKE3ZP@sێ:nvۻSUUFЄΔ;$cTUWW744Y^׿^-r\}>O٧n,+NжFmGwSUE r9JMu[f74ȝ:~WADO]Ȳ, 1FWyԮ^OuTWWr:lr9Sj%'')>>W5:@a#Yjnn$5:$׫Jn1R\\? ô0xTH㕔GRJJr{dgiiQk[4ъש;եJ\N~˩ۏ3.,Fajx85:>Y8amHWfFq'9Ӿ~:{Y? :xw"@_ : zޱUa1~LqN,f˲HsJ,#gA@>&PYi33d$"]R3Jv 7EQ(''[F%|HsJp8t(';A@Q.@ɉt 8=:Q@ D:Q@=6[o :|yO@f>Pnm\[[ۓSNgmA p,@ D:Q@=U]]HrJ4x\F~v|T*i*;3"M.͞yAU޽8{.捒FvҟHMV-*i>)bB]m&L:GOP gnuWyy2ưwY}v)bBUU CRUUM_{dѱ!up? c۠l!F"+ i,C@V ]5H =X!s:b #={^7Y] effi+ 5{_,> gPư:gDx<]ZX{,7^WڐںukDj#bF_@oM}ݺ%jmmUS =BM{cLDVr,K?ZmܸA.=#*+-k矧Y5Թy&4g{y2N@crx mܰA999gO"'\_|wO}WZZ$iر^PEߦiG;GO?JKK#Rb fI}hFa /To-_$]4?oW)STW_n^xI?2@d}gqtfA!+~tD`٪Djf%,+ҥRvV _s\?u`5p̀aw1#ԑ˾,_J}Z eB }ZE@3L:pw IDATxwtTEߦPC'J:J -4{ R+" MzI"%&$ %dKe޹3ϝ5R5M; G..!L&SB||'O3|{$d2Nr^q̙ov(ԠȌ&wwٳgO!wI~3g|k׮ccc݌FL& S2Lĸ[ժUk+W.a…G ?x7Ό xxzzFשSiþ}~oxT?~|!""⸻Q@xyzzvف(3L^nH$t\ :._۴Ԭ_*...]u߾}[ϼO2Cbt=(L&X^{][R.]udƪn&z$ohO[]>޼uպ28#A HUXQAh":s~}ꌎ/ԙ3 oБmٶM}(=խ5i dh,n.of̚2Ӹ;<=tկ/I{AVVߒ`HSՒys={ ժ={IK1 bɕ+nݺ9[}$IGݻ7o^]~ݮ2ӸۨT~LG W޼y%I7n!CϞڲ4KY!ooy{{ dR-+/kϾ}:p׭O(oʏڼuGD^UZUjѶ*UNt+2R~?I[mS||jլ}(_;_zeln_ܹsU ;$I (I?d>$ќ_E QC_1>d;ޕh>[*qTZU-_RrΥf+gN5kD,$8Hة/ƎkKnܸ';tT_nKu|5jRڷSu4$Dy[/(>.r7bݒKpP?pP2Ԧ՝~3 iĺM&}0x6mެwNڧ8GRos =`BBCUD4K*&&F._;wٳ*_<==+IT=QU"///L&}4tءFA TdI-^L}׏^J%d2iޥ"x2ݺuK-I6rmؠ+E`ݺuKyQ]QTD )s6T. gP&*[սKm.Y&~F=խ<==پޱSaNڲEFQ-5SdddKu$ZbjTN 4utV,.oצպGDzD4ֺ 5mL=^Vm׌Yif7iۥ:? ƨO~:~?@AOHb4KC?G[ޫQd2cھcUK#}"5ԧoM1S ({}եcګSOkq._u6Z* ޳ovGAV͚)nGzuS,̦Zlyri$br.IUTVe4";^A]:uԒeeթ hælٲjæMrs3Y&Zt3.!֑86ʔїI盽js Y|ٲ՝/Ç۟mO:vڥ?}|ַ_N0'ҝ+b.zIUXQEV.//PPʟ/_2w 8y狪^:愤zժʖ-K,NI/Z'[vuuz X-OK1 ޥ6J+WfMȧPdeվM4mٶMu;vv`-ȟ\.KHq(S:@KV[$ƍW]Ӑa#d4<8X9rᩬYT,[C(le2$I-+;$Kj0(ٲ|uĉd>HbWƍݤիѵkeڶ~R?Mkplfdq1dWʖ-d޺l2f؟%K͙7_;vPܹmڮ\ Щӧ>sFoo"Au{2\$i;ԩC{ :|XoV}9,<\5Uoy2e$I_.:-gUbݑQQ6&yj$/OOM~nߖ_))zujmSld<%2.IZ =~BQQ֝Tz=K?'0'M6m{oBԧo>sFiS&^t@+_j [5tH҂K}_6lK*UTDD.>dJE`fzWUddrʥwS"E$I~QOd2՗_JS*3gjS+/[-66VO=&ϝk>tI)dLmifmݶ]Z4WlҴ=I¸3R;v$ /&._!IzݫR[nKݩI϶']xqҋւEpuAŋ Qҥ~z_!iC`0asfΙ4雯U:?u}Ap@ AHp ΎG۷<7#<+9;ykaMGOqHp$t\ :.@ A<2'tM] P@ʓ+ dv 98!77w|١];K&H󊊊ԭ[r^^^ajܴq9X "q+Vϛ cg}\IWd%gϝW|| G$dҹ3\~Eya?JAsXdus%s!p|K'-^(C(M?=D;#JC )+Au 2,2rss ԰.qO:ȹ slqwȲ>usWt8'&$p02te̘9SnWTBE0eJ5>bN:Fx})k~+QJuiY6R2TRej#M7ڲek˺t둬/֋/?챻7oꅗ^r[?H.^gһ NWX19}JgN;oWڸqSu떦LQLw|#F|f]? ތ؟,-&&F#?ZOԕoI?U_|Y{uvhh4f>Ͽ 6OX4?+=}pR%l k˕hĽHZb$)::Z7nҠ?P@@_[h'CT\9i͚qvh.ߧ݊`Zr}igr9ߧK-R?p"t-[ȼ<_޼'*Z~~Zb6lh5Alr2EEԠ-Q`yyyTυ Tv-0{vĜn)h$yeK5h{ڱs٣%kħ׹sueUREcƌV'05֬]6ԍ7ZbԮm7Ny#7&&FÆ 5jh1:XRJ+jwf6L&V\ǏS-%yvm'G9st>N>i5q\|2;_|pwx9tw95jXzWZkԟ$I>l& jļEܼJVAڸ˖P*^_k#R#%\4IjٺJU:u?o߾~RjZ>q^.Zl~͛@UV]+WUuMIRj/3|J.:Hs `b?XR5&;^s;:Z+VM(x=8]ؿ_~~7`~;WӧOWh15nX7'՝_|I.۶j玿uX4ȼ}| ͘1C'jw秥KhȐ0(H qڷo]wvf៎T:uumIw0kPǎ$jgD*S:w}WիWSWG5eV5ڼ6`0(gΜ M-XRe 6\[ʯtYj6%,߽Y%tԩts%4jX=QʖWs׮do!#XrU;SklgmyfXH4j{^-ec@Fx zLL֬C[mSU%I5Լ9r䐆2T XǏkizw@խ.]8!-mTjUN2Lz^{MMwl%ܤIjڶ$ =~\CMԠ~}N_FIһӎ;4ud͝3[ŊKKe˦Z ITy._[WĉdpI҄/U֭u2nlk[>hO=<+:w骞=_Q.]eƏ֭[ϯ"""4ywzvm,XPC QjٳZt|}}wy[˖-tgNIZʛ77o.pfdd._3$I`Иq%ݹ]#IpZv&MN;ޮ>zdɒiڼ4hNɒp{]xQ}\ZժVU=qMFMaJ(}Ξ㡟hɒ7ڱmׯ:'Gg}͂5kV5_|J"kGiH4j{^-er1~s璝 =vmHxemZ?\rȑ.߾WvmΙݻiEеe4@ޒd1qv-TΝˤC3f0e͒E/Ѽ9͗ϖ+WVF|jW$*\G/^$pA~]a\W{jUgj'$$X>獋+-7Kc8MnwHhFׯg~`i'$IFLwo'W+z.ܐh9t2gϮ/ƍSOl~deɒEׯ_׬sx|UvXZ\vϨO*+kjؤZ?JH~JUOaZb.]}ƦÆQf}zD2CsfP%I\N?2\6φHڹk>ylhx'R9?k3CIi$\[۞1I(\}H ȟ$vG_ݸySQQQ_RzUKmQ=ukjI]vbcu%$$zj6iIXXy;־{2m~yۇއY<5 rww||$I7")[l6 >xoPիݻ߀z8qRFQի9`Iҹ*;9-1q1~{C)"Bɒϫ}ΪW̟wOrss)?hu)5hX_{em;ɵG\\ve>;7 = gOY]p.mR=}hܠm2e0Rlޢ*Uo=%Kmۺ-Khyr0-78{G]{ըQ]SW^~Yc}RI_)I#!L/=B{Gݴ ׭[ocJtAU(_>Mӻٲ?;v&{;T\95c}]vM[n9t95kguY]-RHd4=ߋ^NM S_ _~/][G t k իWu1ȑ#{mIԮm :Tǎ۷fZ%>ҥڶ}~ÇJK?ݧh8xPZeSlXߞ>t_5A[?Ν/֬]6mPѢ)qcնm *nzh޻OTTԷ 3]ܮm;cUW^U\~ս|9LъWk޽Ԯm[?~uA|8X'NTTTlf;gΜڽ{bbb%J׹tf:o}ksך[nc1sۧ'Nhtg{ímc?\p]rE UYͰeyRlͫAƍtAM}ʝ;֩͛f򇽏s@4=ߋmS08S ԧכzjҬn݊Tʕ[h Azoz:OAZZU]Vok֌;F}|Ըjz\VPѢE-ÔUZuU^Co['NԢխkd}2D-Z4׫*W_ӁlKӇg։e_`! IDATVO&{/k֬;fZwc%K=J!Gy3Bǎ!w)gΜ7:s"]Q*R߫kWizyzzc8XI'歛߯$ 8i$i1T:uǪVאܹ;W ͜=G{_tAխ#GB;潣%gfΞO?f-Zi/hO7m$UvD]~5kV+=㨑Y`rOլ][6nԊeKlDٙV-RJj*Zb$򇹏s~/˖L)Wp-ڽ[ժPBB#czܺuK^^^帻kݺ?թs9kL&eɖSkVRANE2hjܸC9z޻k|ޔTY>V-[iAΜ߸5kpv(܇C ':pFrc63YOr%x\JSժUp]AGֶM݌0*t8p9rpiewn4e4PX}TY8觇cg}\|?\ z1*S@@Oe~z0vGe"A0Ϡ'(!?(`r2Y8觇cg}\sLɮA_ǟw H2htegnvAةQFn~իi~@-\hW=ŝ3@:8pdvixv"AHp$t\ :.@ AHp$&[GwܵΜ=mF%$$dx)%K9MgzSNDQ\Tz2OGROfx~ Ν[ŊIJ/Gj@ۻ{Ѝ&$)66VCS6 ֶmۓV]NHmܸIYsJ)SW(O~omP;w2/2tʔɿt9};{Ϟ\y cUǟJbbbTr5yt S{` hԩӧCԪe *$I0pΛ?OUȑCjҨ7׉'Z+tB (>&Jٕb ty}ڱM5jyֺ~=BԼYV\.h1o;f~5RU``MuL&yzzJ*U#8#PXAooϗWWpv(f.:|HFIUtv(G!:|*Tp$e#ԨI3gxDa=\x\)uG :.@ AHp$t\ :.@ AHp$t\ :.@ AL^!1L?_^ڱa;wlWHT|>|X\0g z{+|*_C1s]*Tp G :.@ AHpOM&:4AOHH Ad &I a5KW2Lrss`pdsd8$Ѩ۷v*䭰pEEFݑM0 JHHO!=gΜʙ3# S)t\ :.@ AHp$t\I_6:ȧPZp)%K9 kԎhԧ҉'mn#kmظ6ۖ=2CI3NkXzFQ 6w־JsĖv{gzu${`Զ}GL&G@d2w[o'[+l"uhNl:ӈԩ?.V||F ;(2< 2j?}|:r|a<jl{gzu${\?#+_tf͞];Tɒk:LK'Bt[s۷S=5>alcR9sT^4r7mpx9 'Gh@~ɒ{A+:ʓ[u4Νl^QUPa5kgy9*:ʕ*>V57n7{ɷkoRD Nt+^JGLu;ȓ[ "9t!s 7#kd}ڙ(F/-Է߀Tc|Ҷ|wQfʓ_ǏXW^o(K,jӮz߼d2/^W׬s럔)66VCS6 ֶm-n-޾%7n^wᢪR[%yT2tʔɿt9};{s}ӘqUFVOc*S *>o#јvZ _N ﭲ+U۴nRt+WQJHHЂTf-̝O~e5s[*gMzm<6#[K-v:i#q&yd͡*k5V)yTHjۧ%N[ֵ4?ӳ]jqYכoh˖ڿ19,A֡C̆ΟgЮXj#lZ޷-_R3ǎjg^CcFԡ{XS||܋/ŋjֿcV Ѡ&ӮC'5ǃ-߃=j{U歞͛7wo~K[lռ_gk*^x׳Ͽkg%ʕڱcZ6o.Ij׶m8.ט}Ϟ W-,Ӏ4w|MyBRF8{4mJ,ͳ–z=U+kOS/]͛ko~Əc@޶Y._RZO(Z7͚&-_2mVc4泑:4i^iN$-Tݴxƚߛ7uk׮g ?Gajժ}u_-m<6#[K-j@1Q.*%}ZTj3yZ\ (019,A?sFZGnj,X4ʑ=_`uի4ioXSyQfʛ7yݮ]:i&*VΟ׵ku9-]\FŊɷX1V-[Bl;>>^O=gϮS&`0Of*S<<<+Wjίs7_^Xa%ijd+*%7}{$Ij۶($4T|J=Sz>b~aPk'qv&+ծ]K А?RjU5qc66ED qkzYU~mׯeϞ]:vPܹ]aT[ʕ-کL8J,͛\ٲڻo_hvm*(|K/+ MԦOКwP%KԽ[W]vUFQMȧP!(^\5[*e?O:>/\in:FUK[*-f9uڧLOie_ QCv)2%Ŋ`0⅋vUX*g{. hb,wr)9r)k\i] $dz*UٳۼRBBBm^'%ǏWBBj:t'VK y{" V@m$ioԹSG5iHV6׾}TL:NP^=r #)omlmi[(M3.)gӳ:GҲnZ4=qZ7-Ζ}ŧ.q9,Aϑ#ʕ-u6ؽn||ݭ.Oҙ˗Oq5L1Q7uSqё$>sfEjYt$Mo򶈏zmbo }IFzU+]|Y;wR6m?q0 kck>˞k:s7WF fo:s)bW܎u{t殮˖+::Z/Pn]e0y왿(qUVScƍo}[˥!5U`9=:sػnz%=qZ7-Ζ}%K,)2>$nGhugcccw^%ӖHR~Tʒ%mOac=v(uY|ChsRʚ5;*ҥK`0h[LmQpa'mj_ij@9rPV-ZpjԨ⾾vżu۶do޺UUJ>Gc66mlٲ𰫿7nܤŋ޽TlYɓ;M2n[M:7-_%K{eA-[4)x'M[˥!1Q_҃nUR}j=m%em~umwIe_~EE >HCݺm6U<hi_V.),,\lYKNV,X@O讷SHh"##q&+W.ڵ[o޽d2I…}$I};oVoʙ#:w\rɯT)MeΞ;uk/3S+{]P]:wRuaEEE%{@۽-o)::ZO0WBy(^\#>6)/뫲eF}/oWK/www>EDИqw>sIئ<7Mߋ,C> =~\֯05ڮ\ںI_s&szw{*V@G||:W~EVΜ95kdY+ˌZhqϐέdD}>>>:|Ξ;c!!vUR}zoe-}T\qhn04e$;Z,TfjڼΛ////sK.^*SBB돵+[l6-oTJe7kb% 9sƦƍmZʻp1up-[Λ7F ?HQQ)_"'Wuĩd0udݻO?$OO>lzyU"ETz˔ӂ,5%W2en[ ڷkaajݺ֭ZKԾ $}5 ~:w.2evkúd/6%aZ jP5o%eiڤ诮ݟRu-*=u[[\|u|1M>CUT2Zt̚q}ʍ3N|88]"ϐ-K[_vmTȻ*WW^}]A =V9bm]{IM{kOm~w6RҾ۶Y3~0\ hkS;uytR>E}5yUwܼySrrXyKF jv9YllrʫPݺuږ]>Et^-S&C2t.\)S~?y :|p_x Zp6yAǃcoQɹ$z :uIH]Xx$Hьy+K|@F'^>76CŃq ޠ$ :-OӫCq9G&O 1cƩB;gv~%ك2-|X*L:|X jذ{:|*T(P$P~z5i0&ׯ_SP2zUpvf.sY;R.y;;3I@#AL$`pvɐ@`t;VzN􇜛p5 ŋ]yjijh4jĈ:q _׻Md╆+ z&)SJ*ϴmK& z&ӧOg6 3pKcbbL2ʛ76mݻwKsTF yyy'Ў;Z[nnI5kjԨ9r\rZvyٍ7ꫯH"*TDb IDATz쩈Nti.\X|վHKu1.ˮ_^$O",X`RϞ=bŊ߶-%m`ھ}={v8RJ\6A|r͚5KNҠA]2}95J{Q``uu[CcǎѣGUV-K{uEرCwֱct'oӦ6lCG?jժ)88X7o޴SkZ`V^%Jdzz5h :tqv\K^~Uڰajժ%IjѢE2O=J ,ܹskY\ޥKuwM_|Q3gԵk%Kٳ$Wo~⎏Wݕ={v2 ӕ+W+P<==%IO?7o.Io`͟?_m۶Mkʕ+={֬YK*UJR1ݸqz)զM4 &s=$$DFќZjժѣV[wb"k~2yRˍF-u2D}GQm7 %! 5zK( EڰϑRĂAA+XRPDz)x^:$$&f%͖&xu<33{gggѽޫeJvڥ-ZnǎjܸUYfJHHp~{d6u233//~~f@bt y{NԎdgg,;GPP$鯿Rvvstg}͙3G_~6m$Ij޼UY/ߖ^a6%]C^h>E$t= Đnݺ.}b .h۶mjҤ]]j׮RJin=m4 Hr[# |\ݜ/կ_<_[pbzs(h땲vRaaOl2iܹz6لs8Fg"@I`/Uڋ%Uƻ+e@OMM-Pp^2%3X{&WRK/3W bÝܝ#ŪJE]azpPEm޸APԥlڸAAv_ =԰aL>&I+Xbh= &_AڳgVZԔ. r* R NHv֭[t2d!VW|W VppFu)6 ԰aâ.F4.0:@@ `^V۫뼝of3P"XVf-kKgeZ#qVUEYgŋ^[WzժU,zsUxllVHHUë= @\%wq `t 0:@@Wz^szb襗'kJ*U Kz+'YYY䓙ڹsW\++WN"zHX~Zﯾ7ܤ UԮcgmݺ-״QuW JJNֳMTThR5;KOO׃G(FmU 3gm?wjAYO=d!ZfM6^͛5U8eddHujڱ}Ԯclb?iS_1/!t1~6nXD=4~PsPOڧ_V؞kN\➗o117t=wK:EbmӷooE˗׮]U^+C2t\gI|||휯ϕ[镩3>Lgx=_:vx嗢XlN(%%UǎS5e()9Y?^))4y=T=Qh-:{oׂ$]9DJRSӴ{t7x=5EԨ두xtYj~\2en]j4vj}K-Z4wNQ>uwJhvo$ig_YV /v&T54\=hmܟ/\&&T9Uyժ͍*TY'hGy(J5mT%:վuԾ2gLQXDM=pS{W QN]if9SOA&ʒ$;wNM/X8YYYzpEԬFM#TN֬Yx-odeei؈YG"i̸EXk;{#WŁ~ cOLj*^C 1*s5D髯H}ィx::S_~PQqu;;'#FFQ[FԙKշwl7b811Zx9L֘q㕐AhɒpႤKi…8$);;[w{ƍ[6]\?Æk߾x>CEFFf򤗴cuYzbu"mSOc:sNO-czz“EQ̚5Kz^|^_-[*w_>sFvr4~r<L&M|q$)WO>Xm Aso|رck2G5ZxjT(j%w<*9Ϩ\=[K};wґ#G|史Zm۷+55Uzj~GEYGPWs[\ݎ 3G}(u1_6_$= :OQbzvmUNm-[Vrʗn8;^Y#IÇ_nɊZjz{4F iԪeK5oL<<+<<\sRrDg-/ٺs=*[>=L&/WYYYYaY,͘1C>>>9rK˹m`eQTݺ7vj֬ݻ^(5S47պU+UPAcbr}Lz>x ]ߦ*Wg͚/I:u,vUzܹӥNv8WdgDmaoq0d uQdd>U-゚T;9?9;3wƛoUҥ {s="5-M~~cڵI jG,o<9|Kv%wyFRsqJLLRRR$iɒet)ٻ\ǎTb˰^јuj}osu;;'чҢ?蝷TxxШZn>qFMlܤACnWz  ]xQWZ`$iMAAyݯ''bQ͜'"s'G=4`EVmwܥn>qF TL^ܩV,A&'IkdX`" ؿ5&ھ\]5Yzηڼe=z^zZhƎ+Iz e9T9{ՁM&|}/}||4ٚ,%'WVm4uN)W\fGlxW+x6W\x i\J O.SjZwPΝKW'Nh-;Yg5gqN;;'gG콏 ɔc8:Yve>qDYvWU^*/k'UzzէO"XՋ6_v3"W.uCy,s:0Mԣ8 5JE{QƍչsBYwddI区u#e2r=~j;ΚL&C O>@>?QYNoop'؍#poWF J?4Ra#%R)8XCv,Ҽ rMsW7^Tou~[tY1 ¨[s Tz^4Y5""BWۯh:{qre;3g$сӨGFk-:{kŶi<O)k.7v~ZVeE?3hގ15oV=b{nW=>^ԄUFtuyZpnVUԬekaWX&N|NO>-kժU5tB];oM{8ը]G{B;ק).6Vܮz Ul/fE@ .֗_|f.ɫ[Q5j׽7[c̙iã=…˙u/OPN] *So5C㒤צCioznA1=z)~ë}W8/xKժUS+~ΛS|)(g0jo2ԯo_wl/=uqƬ3W+q.W9 }O~u;NwuBk;xWۏ;n>qt**ڿe6GUJN5ڵkԭk3iuѥyJ"o݅ X~it;%>גu-,{?A*N5VZl:vvFSjF_ ,Fq=ű݊- gvRaaNdҼsӭ7̟*VVؿUyKMM$P]vskEߺvsڮdbc{c1[II~azMbmo_~YV}bUBE255hP_ի~#ZrѷG۹vsmڭp\|-vD~cXbJU5Dj޽z- v\;' `t 0:@@ `t *Wbʫ^Ο?_eMQYfJ[M~pXV\ud6nKqu Uw=ۿV"5r=WNz$Zx MMYUBԠQ-[n?^O?4Sp룭۶9sW67|PeEkǟ|*>p9OZAUB35gT?Gb&g#ǟh FMK4vsBêE5bCQ~mU˶ZгOTff)m_^Ջ6_vc+Oyc̾M=7Œ{mYvWU^*/vƕӗ9+Q;:VWpǫqFy1QM﮴=sO~J*&?;ϟ?;vQt[\l쯔>}zʶ@a3t@Ç)$4D whmיtMkԈBBBg^:tHI ,F=2Zlٳg=>^ -tdwhnXNj_|Ci_ٹ*UJoERaaa7l358l6l@իGIO#""T/*J/OyU+UmCkqJJJDۘ9|i= Wҥ$]noA\9۾ԸkϞ҂ TS@rcF;wM=ə}rjR~ZhWuQպؼ/To5V,]Ѻk'WNJ;+T=:q.SC\=&8jŕ:o rT=G?Ӆ8˙cemqV\b{ԄQ zR4}T;w._X r5ԏV闟uIU>Uj5kZ~p.^W>t]w+4n.m$]xQ7m}ǫqjܨ|kːt 4(_nG58ÙL&W)))|llO?~\}viWzԴIuhcO1--M}1[-Z]ȨZp3g [m_^ޘڷkU^|,:csh4ђM=}r~k.7v~ZVeEno+uؼ/ߙUWTh͝7?,+vruxs?qB;uQJLN֏˗L2vwਦwwJϞ8y\Tc'qu?r~G1]X9vffe AgA(%%vvg TQOtk78r*U ʛ3 ۷**nQp+V?Tۛn,r`@)0\QQ"jw)U=ye?,Rǎ<\ rJK;0'>34o\t *;܄sؓ}<)?JB\~s+lGD@7?B=v\{K}#G(33KL}M W݈{c,u9!PRRXfXbJU5Dj޽z-Ҁkʲ+ԩKz5mZ/Pݺ:^e`6l#ϣ;NNxt`t 0:@@z@Z^^|Հn6 jl6{m~^[le=+*L&oZX,:{V/^zЫVTeefכkffBBzm^  *(;@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `t 0:@@ `~ne2y m.ysziS)GNZ(Qʗ/.}ʂI@@ `.:UKiE]JPreU T:n {,C(ʊ6uQ ޹K;`TIdQVu)ʖMH*NHA7na3?Fvrz.qP"dgE6KhFvu MxV+/ I{o׼p;!--MVvKZZ6yB-t+t%AkKL&V*'b%|N@P\^5LX r# V+{^(;wԷͪV-L-F?H^Z,XevX,׫BCԦUK=4jN8k>qqTJeIt%(3F~}YM~z9s#p6}!!`ZNE{c&;n>}IK4^ԘcTF WLѸqj_BVf%$hK/i-ܹx2ͲZ;Q-YT3?D{1cTV-YVt1[FUz'sZC?$k!׃(Z;w9lhăô}6 1+m%Jk~AY,廬vmo-7S@޵K֕$[ڷ$3t>S?vLeʔѬYb2jRӭ$Zx~IFÏf}vPVlj*$$CC_jK:tN-BN{97P+0L&9zTBNq&ڴi(33K.^zoW$pAbQ˖-u$5nd{b\ ȗM{ q͹l0'NJHH?~\7mkShV Qo ˸^\e5k~;ճW:v蠥?,RR>UVO/VVc PQt>'WiefꀂogZؗ-[V5k3gg.5li=ݧ6ߠjiȑzu"ݽ+9J <|1:rh]V>Aխ[W2d }yNڒv]5M͚5TT)m'b`txZ$oSi'z^SݨzjۮK)W.ã*E[EVJ {bծCG}:svڣ;w>_|UC~eNH+Nѣǜ]4x 7^IIڵ*!!Qt߽1cyVeeZ۲hM:{ǶבP&Mԫ{[%$$h͚5zdHmIDATK25dKpșz9:|p*:ꑇ$ <%͛7_׻;Wեs'~yPm^͚]VC^ޜ&)Oթ'|Rd˓^T\\^ըUSw [;m>5zmVԧ.>|||tf~1x||_Ԥi,!뀁nX/N|HwTQU-Rԥ+>>>Zt7LZO,vu:@uzpk3EΝ"(9=L{Y 47İz+Wq8)Z8^E;E] `t X`4vE : : : : : : e;L^.IENDB`cppcheck-1.90/man/images/gui-newproject.png000066400000000000000000001051601357737443600207410ustar00rootroot00000000000000PNG  IHDR$sBIT|dtEXtSoftwaregnome-screenshot> IDATxwxTUNI{tE#bW,ĵ v* ei*)*E@j(% RhL!cd23g39Sf;;{~x6**D"qA """""""jjZP(J/\p'\PǯXɓ'KTJT*T*U&""""""D"D"bŪU?pF bŊs=rJZvDDDDDDD7%Z'E )׭[wN'ݻwEmm$""""""H޽{/$%%mi퀈nU[tXi`nU "M$ѭL*8[;""""""[ZLЉlt"""""""0A'""~ y { ~ gֶ*2^&DDd4_P(P8|$z|*NJ3ק7ޘ**++obC(.)Atd$蚐\ǢCf ,ZBD0stxxGO.+Wa?qebԈHMMD9s}zG(Ys9n˯?`ѭ>#Jƌ9/@RO?HGrn]떭xG+WAaC㗵3羈#GmJKp񈉎B>}nyW>Gb1p1SSvl꣏@Q[\$z׮d@pq]OVyo~0s&uэ#"<INs'C ]\R <W=й}ojUط?c\DDtÅ>s3.s 3O>xz^/͛o'''zFuE#ZsSp81w| Gn>s6~\o{O&ɤS4~yIL<|%={%3}i'%Я/zv﮵z\}2\]@or ;!.6k֭GΥKL079ož}سoD"Æ ƆM<󒚖g}8`xf&AU޾]/$8}$=9}oK"4$/0ȶ`gjtng|eNDDzuG`?\d2DGEa@~htT`Aq?T}$$]`XC/֬߀574*r*2fЀ:[\]5[ N[ð!XwߍpQ֣'<=5,󒚖nR1QŸzrSJ 0ͷR1|P899nR|9/JaozC^ oFf,Z hb.20BѨ޶d(6S 4_./ 1{3;ݏ?;!ugdG 蘛e^PTT*=M>Vu^~;;;e &7WWb!""VPXfL5YYxn,F_ :H#0n,AgQUUȈfG\9/?ݺtiV矘4a8xjptp%ʼ4/Cu4Jt`u7Ҿ{ HͲ5w'"ۜI} `_~mVLЉյm={`xw6B$lv|Td$=KcxիxÆ?v`3ѯoTTT;%,EZzj5YK$!+;_/]76\z* ~ukcGc8x0FGӐ-#>h1q8T|ɧ_]ԭKK^_whHQTWc Xa#&w^mIIMELt4>}E}/Դ45d"""# |ǏéXz5B5o6'MDZF:'&B*Bu} +/|eeevF aC7mBjZZ]ϝ5~~5(((D*EdD"#"tn6m={jn23bذVE,4/}uL&CffZ &$t6P"A$pttGRĢ%5vȆs~F[ٽ<=÷_ }DDtK65VSS=6I۳[Ҧ-͋\.#ucF3DDʯ_,J+]ܻ1tH$煈ژQ*,*’o^-[@[ߖy!"">&DDԦNŽ]ѡ}{} 3f␗?؁yCkuUUUXr#,,ᴚ-[Ńoplٵiz(Dܺ?brwViVct-H$kGTt d_| ѣͷƞ={!KѩSU+W{p]xرSS5kN2GbuƦmo?|'ի\\[i++*O? !a1c1Q6k D"\Z\iI)z_  |)j'k׭;ڵԀf Ů~SܛH, 0 <޽{a޽U'%bذfa d..899Y;,VeV̟*FȈ~m+۬^svv9bCi5YH>sJJKͱ??_MHčbrttl󘈈n&'gWUWc֭ػoz5aÇ ?!f̜sSiF̟0JE-NjK$X}==zbO@V#/?S<.dc?1j(j[ @T|y}I>ǦBRA _L/?cm 5y|ѻwo;NÏ>W8x))x7੧ĉQZRsg6kC1l\+X3:t'0vDFDŋłW~m1&c ls-++SYs梴Ls\.ǻ-@>Ӯ=&NING/0 n^wm @6|@w*,,_`0Ν;y ]UUYsC.H{Q֒A3tcLu| CDDt,F=ycʛQC-8d(B#ѵmx㭷*kס\ȘA\|G|x Fu7"cq[>رsQm-[KBgL?K ?C7s@g`٫B#ѭvwfdxsLS:DDDנgffB,B,Y悉OӞ_{v#,46n܄}CЯ__0Q絹ӟ\m+vx㭷4{ ؁%_}Gci  sfO^v9{wZTTT 77O̝3 `*1{ 1 /Xy*5*746w|ǘ1kv$TW\];~GLwJʵQ}RXX~S&Ouz <`YSƼT*%1<߷s3fBRԖlٺ Æ=-[6*7 cd瀮.mku;UH=wo:^uƣ1m-%q :`Ѡ@ؿ Y59 ?@bb"JJJQQQ&mhkO}5ĸqc1`x䑇136ms.8Xӎ9P*ٳ ?n^v5P#Bݝ#5* j} {fVMC3^4:)JCb5Ͻ|vXr9u v[oިK/coıDz ucذn tR`;Kkۥۥp!-qϔKήUSTT6ի4[b05d`ԈӻfXܼ *{}ܹ󈎊2)Ncoэgo9A`κDXXjѩs.@-iN*Xr:tqu7|A^SyM5P*-n˘MCKljc8q,I'bz*}m? Dq*?*A@޽ڬ2=n0'B~~> z}]tT6sv9G󸐖)۷;Sc5sE(Jt.CcV:v耟WĶ_n6ͮO,c:"j+oF KDx85ǎǫ^ktP_YC\̫ˍnRm!77;wFuu5v_JC`M0Fy4\p <: $t펈XA59Ncۜ99 ""$$w^ 7WD1*J}M `䈑زy~;{m>*+Lo[[ן 8x*[x 1{5;;;:xȴq?gLƎ%.Box8??F_hSwxbuw;ٙqrN`Mdךc/ m[֭+} :|qR(Zד1E$aٷ%qE8/`YC1-Kٲmе;:u{6wCP0F9`K.a섉۷֮'Yq3ɍ0DDD-aݻw#77._ҠgΞEvv6RRSWGի<$I3Ln[{ƤI1k9{زe(//իWqk^;~e|G O>8=;GEUU%>76nh`\;vC2MAxG..!!J8zTM""gϝ٦J6}3ɤ$,] kLJ`\s._z\dDXMqCck-Q((`tr2ڷkgV1" E _JeL}%CyFg_y5 cd ] }v8xAAx񩈎KXƌ)sn-QKY|>C `{ѭG[=>>9?u/?]"," 7nOVj= kmC|%9+"bf:}_,BPH(C!5- vzq}FSo_L<Nsߔ1)xq:9e|ї8'x2 ? ^|e}2 8m۷<==1a8>|}}pE~׮1m49 ""$f]`!30h<<<0lj %՟U[j..DMM b֛oh3GǨǠݻuk`oootySƥ:oy:˚2}Д8,mmsfΝ-L}I88:йS'c5f9ԼW^YPw;v„VۗF&`54hz5?F>X,G.lb\U^^Lf9N:t§KC巺;C볥nρUwڡ5cWǚZnp4j $"" :( ;* 2lUoGdi;w]bʽѱCkCDDDDD['ZjHvt9|EDDDDDmOɢT*T*^o s\8ݸ8wqv'""j '4/'b̳ѻT*d\ Mz)}Gb={j'[\%5sKC5&S[nkIh&;~FZݦѭ<λ 0lz ŌT\Hũssc߾nK~3fQϘ>B?@0ilwTUU5:FRaӦ_1qxTVVbٲqtrccn0jdVrli[{>-!..] $VI$iuc.Ϭ:]]]q1#B4ϿAUUo *+Ӱc..1\L02 i4Fkk>T*[,\ QY֭8-Ӧ 1tkCDDDD76=~n=a#<ӟJn_B{}߁Gih ~cP:tQ~8r$ C[+{Ab]D"AuuzӍEzfC#Rͱ555xu|/_ q'NcM0ZiӞ0t`lظ17mF[µ֣Kpv@DT,^Hسw/$N&b8,jc]m}7&J<4!4< 3gi^>3>|sH6[ӰMZM_]kAZ6vVIӯjku+c<홧qA:uZ8D&JNE_ȡ{ {'}V.e59.wƱCѭ %9s_Ͽ/C3

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

K9r$-Z_ǣG0`mի_c̙(--E||<еkW$%%!!!b2 aaa:BqÐ!C|ήw?mЯ_?1^'W[\L/ӕJ%Ν;|\ssc UoFLLΝ si㭷Ҿw-/F?׵kW8p{A.]n:$$$ॗ^2)͛1vXDFF)))8sLSƀj3r/l(l"˖-x ???8pd} Ɠk֬EP( dž? N5w?g)M9>>999 ŋw\a۶mFPP еkWTҿ)9[8V w3 99x`;8v[L<fm)%wMy<>% J%wP:8;;sHsje"D+j*N*]W7H$c;v`„ W:KVy!p5իaXƉHÕ@ 1|  >\Fث$=iݻ:u޽{Zo=_!uH^e5 &Mk/ N<> x瞈dh'Oc{-bVP(ZU}?eX훢ͩ'uM׿,dff['SB۷-lG%1Y[ p(E͢h 6pH$uV I00TVVٙ3Oǎq1bY$|wAnݴ lm޼y1sL<|Vm]t~'N '';w^1{l8q"3`wwwX,Duݻ:X;5vm r 4k4YNpCIPg ddf}븢ӧO:uX흛E! @kOMM ze˖aرXr%!d>*xaooHRlذD"A@@>-tR~~~ؼydxgagg___|:(++Ø1cLN̥R)֭[`H$ڵkXx1ٳgk၅ bH$2dΟ?oQ?@mm-># Cvv6bZؽ{7 jڄc…ڿ>}9ooo|wǕ%T.VTT && Œ3SN5ZJ4[\_WWKgϞpvvưapY\?aL:U'rr2Vb |: |ҷ=]'OFp!rlNP 66;w͛g MΝ;W^6m ]|v_@ +'Oj{ cp }bmiӰm6$&&B"W^h=%wԑ|h?=ܻrCuhD1oAy7Ra?GS/""ƌCrr9 6bcch…DDS>}VXA#"rqq!:pݼyj5Pll,\.ӧj"":qB LF(//R)۷JJJH(ƍnݺE?|qqq4~x^R)h厉SNQ^^ۗhoё#G%%%iFGGSrr2Ӝ9sٙz1cѹsSҙ^!Rbbb(%% 7ߤ.]F1%ٺu+Kwnn. B.\@b߿ODʒqo\y_u߿??iNlj(,,ϟOwEEE'OlB @@YYYF1x`ڴiI{LΝcG5LO۷oFMsmTl9y!hɒ% uTXgqc[KESes|soi׿E oIg=Jׯ_3gUTTPII K.i?~h6t-cDjyy9Qttѹ(}Ι3x :2%N'ZM*T:RRmM5V+FQIGT*ңT*SbQ^T*:i%SΨQTM'Nӧo99t1ï?wgϞ%tmr;;;L&#"݁t5@i4]vʯi(%KgH+Wl$m۶5J72DFe(pRRRߒܹs׉h͚5[o]|V\I#G4ZGqe8岋yI(,Je[W4o<Φ~V\I޽k09.ۿ>D"m_=!!!=eUUUZruu 0я|2d{㿜 IDAT.nJTYYSΘ͕H$TzFGGG"z*GΝ;FN.[.\cq\[17fS#07L] ^̙6lǜ9q9 nh*T*cǎ74_z?#k4h R('ſs^r\Kx :"778;;bo/ _}w~7nݺA,C,#**Jgg#F`ԩܹs1x&b1 233 H$TTT@T6*ףGDFF⥗^¼y֚wbܸqQW.]heG$vZ4SzχF>-W[ҙ^oBBBB:tQQQ Crr܄ eL+s)߼h40`y1{l|xЭ[7,[ ݺuCbbbaÆi _5ZgCwpp@DD)))pssCHHt@h7`2K J兢" 88zmNV#$$S%KlXeSۺwѺk0QIC6V̍b).;mCX:7;Dֈ SA,_~~ebL7|ˑ}`K=vk pF~<~뺚Ⳛ75%\7E]v1|βPT{tgbϞ=ؽ{7 汶 [:EzSv^{ B?@F7~?pw$ct6661\p[lu… : 'C|3tb޼y K#Cj80FÇc׮]Xd @Dd8qNYcM]]rrrt&r[=g e,z['""'N@BBɓd8p v ߸j.!Mnިŋb_\߳gOӧ97!ҷW_}bGRR&Mͻ|r_Rʐ:w׬YV߶6׫W/6 ?pFik׮E޽1be9(}bT#Xsop]XgG=K}5h a`o2WM̙3}Z-s=x"`kk \3V7l؀h̝; ,0#mҹGiI1=Sp2-'ӏԉt9ygOӧy 2Μ1⿮l݊j5{͠AD D@@B!"_xݻÛhNNN1cf͚,TWW#''IIIRXaňƴiӴxzzի~,HMMEqq1V^3H$DMM T*{W\#lmmBO#GP\\,XvvvEvCM'Oܹs*<W:ӋtF)S@&Y[>}+WԾѵkWb:W͉;y,XW^B}ZdΜ9HHHqL6 >>>/j4.wwwǴi0k,䠼֭CNNNƇ GGI&a0`oߎ˗/ŋꫯ,`\ ٿ?\]]1|po***jR_;vĉulUVV6z\Ro|UUU7}] Sϗ_~qlق^xÁtʊD"۷֭[9s@("''{.wa\bOF9hb)Cڵk2 Xⳬq\{Ds A7o\{||]fۿO"=H'?~S h@3WÛI"ҴExat Ѩ+bzRܚlct F@} R555ZA(B,١o~lm[[6 -PRR- "@y>IBve hM ~J%*++QUUh4NNN# am򐖖q3u~2E`0 lW(J-`FArr2֬Yr>f~k`0 |iTTUL1 aG%%BOAuEEʠhV[M}<<<8,==4GO?͛7*A,az-4]?$ ^{5Ϳk.߿l :uPd2VZ@۷[>@4ȑ#!~-[n+WWWBEE9zia ]zaL֢qc\RRFK|h.l6k8xuZ6ږO?1TB*QJ ~?sEeXjZMkpH$y v؁K.d R]4f{nopwwGdd@C|xWZAR!"̙3f˜1cC!22݋N:a޽V:Gdd$&Mڵk'Og}rEZba6uy*U(pqvр$&"k!VQ4 a|nj!ZFS`vKUUoߎ#G`Ȑ!͛7YYYik~F3én~~( BY` :> :ܸ,̬oWTT`BNsh"hA޽l2;+WDzz:L<~l$33/2QPPT 6`H$Çe.] 7o6z=2 >,>@'=##eee3f8v@*bݺuD"AXX]ŋ={Xp! D!C>={+ͫ->0vލZ=6Xpo"/Os}qe8շ ;0c xyyԩSͦD.CѠ_~sR=RRRt/M60:,]={3 g &`ԩ: Z+V?Y7X|+?i=qSrr:44US>ۘ~qy\|Y[gZZN5v ֌a666G"`СZroLFSp$_?S.:'{WPtvPnӟ(7(oF6gh*EDDИ1cH.\.aÆQll,UUUQ@@-\>cӧъ+(44w}DD...O7oZ&44}t ZMDD'NR($T*}QII Bڸq#ѭ[֑/..Ə+T*111t)ˣ}_~7:r$mhJNN|3g9;;ӃGo3f̠ :w=xu2W:Ӌq4D*RLL Paa!ԥKh4$[n^zi%P#߅ H,{\Y2N+믿NSvv6_ɓ'[LgIMM D"JKK9?e={oZMC ˗͛_~ٳgS=(##JJJhԡCqㆶ<BSN Puu5 BP|#!#":t(}'DD4n8S)2e ݛNlj(,,ϟOwEEE'OlB @@YYYF1x`ڴiI{LΝcG5LO۷oFMsmTl9y%zGDѼdTXC.Z7,\:7CE?vܻwХKy?Nx]c!Gׯ̙3͍***Ĥdԇ6񥏾%xܺIߠCyŢvWTtj&JQ ;&NHӧOrrbx8ɞ{kARXXHH.k%&&RNH$;v-eddhO}...:FXڿϞ=Kvvv&LFDF}5@i4]vʯi(%KgH+Wl$m۶5J72Dt)rmP:ӋtS*++3%sׯњ5k護"|2\Fiʒqe P(Y+v'zX L*--gҀhʔ)<۷oݻB ".ۿ>D"m_=!!!=eUUUZruu 0я|2d{.nJTYYSΘ͕H$TztGGG"z*GΝ;FN.[.\cq\[17fS#0s|ڏ?H 4HWM͙6lӜy5~s¥sS1oxmϦƃ5c1uT*cǎ74_z /} 驹rW^N pqqX*[oKBW_ݻ&ߙ[nňٙxĈ:u*bcc1wF漢[[3ԿUWWDdd$||| HPQQR٨\=^z kxVV޽qoJFǠt颕YHڵkL-??\mJgzᗮ&^^^ > }!DEE!,, s&LЖ15|@`]W_}*l2PVVoشiͪӆ ='_h HHH !!!ҥ ܹc|ҷ$HRxyyDuu5=j=}S qɒ% "QSۺwѺk0QIC6V̍b)c;/_dWW-SwWKԚ~%di;KԼ5p$E,_~~E`&ǐvk pF}tȑx! a;`CJ~PTPTh4 pqq[9"44#F@jj*n߾ oooyB!go߾Xn6]&aԨQML6* "1[l-CL/28x JKK03ɸwΟ?0o\5| >|"%%ڵ+ĕa„ y󐛛 X}\o,M ~ 555HHHI ?~2(޽{QVVxyyi''|wG̥^vcotϊr\t Fۨ}sQF\[1'fr9Bsسgvލ,37僥:4Fk;uAR5mr$}_X27o*v'/> ?ؽ*&##b''8 oo~qL{4… زe ֭[?.\Io8SC`` \\\TA"0zh>|v’%KPUU"L&ĉuomꐓ3۔z?e-cL/:8q0|p888 <<'OL& O>㪹 X,6b{#??烈QJM6_~PTZ6g>zi W_X,I&i._ׯ7ya|T2]5kշmˤo?ܿQڵkѻwo1`نo8hk+|bf}5#֞?;6l؀h̝; ,ژj ~eǔ~Lkkk ^6u]\; IDATŋ 2)!g\ч1=5'r_WS_nEg{f BD"" BB/MPWSc^'''̘1fBVV$RDll,xbDGGcڴiG===qUr\~d}鉂W֙pJ$dff* =QZZ+WEaa'ȑ#(..FII ,X;;;"ܦɓ'cܹGUUuOj+pF)S ɬr-A>}닕+Wj_ڵ+zjGM,XWBP`߾}-vKl2=zFTT&O &e6mf͚cݺui=ATT}(6i$? qe\x_}4ՐÇ9o&ձcGL8Q<(_ee%tjHRUUUHLL)CHHvލ̛7[nEHH̙P 8oީrG1s\\~a\s?;\]]f,Z sS>XC.Z7,0CA`` /^`Xu ֊a A7o)6/}5DSfZ:s?ll`#x<BB\MuȐV-PQQHGGi;vйZmq3, n`kkSN_lmq Çk׮W^-(]#899Y\t=`{~a 4 {5$b0i磇?67֨+bzCFܚlct F@} R555ZA(B,١o~lm[[6 -PRR- ") e71jI\]p!;2 }>}.NNT*QYY*@prrf0 k47gyC|| 틁!C[[`~WBT*mmq l4 f矇L&{b?Ǚ3g{`0-'v?`0 `0O`0 `0  `0 `s;p,U] H` R[[<`0 `0v ,x|"QDbh4@ue)JnD?7aW`0 +p>4***AC^l& 0M~쿶nd;gC$Coo$FL4<jIIIfIOO=*++h4OqͦhKnn^i˲ܱ$_k[AV?7$ F_Q㥗^T*şgr^ӶoiiiM&LqeB6LVBXXXcmDb4B;pҥuazi<۷o˗\%vVm>uo(4:!4Z@]@ P$^(4*.@"غ14 a|nj!Zú=zӦM}PZZ ڵkh"ӳ_~%z~ 111&{k#/"00Ea0Z={ofkb՘舿Xz5Fe:4o{iy3&QQ W!P@eM!TRB|Шa@YSYwEEO///t (/(ߢE USS޽{cٲe;v,V\txNff&^~eۣR6l!HÇk,]6olzd2}Y|NzFF0f=<BϞ=ꊰ0dggj,L/eݻwcppp@``;->34V'LSONNGL1}o}pqqcǎi:`&g4n5M-U(EΝyG;m4l۶ H$իn3m͛7cܹAxx8.\M#"b:缽wc>Wފ AAA1cp)ܹsqi\x3f}5KPwX~2E]].]={Æ ٳgup̙c|-c kD'ٺD"СCuO,#Zt'JMwXSyPrLɇӓDe={ *}ݗ7۟у?[K|*.NE&""͛7IV RZZr>}:yxxZ&"'NPAA) dH*Ҿ}B!mܸ֭[?GǏ_*RJJV:uQ߾}Ύ֯_O9rPRRltt4%''S~~>͙3棷3fPPP;wd-cX?&ϫ=Jׯ_3gUTTH$ǏQ3b2#!*m".cC"F$cLl1osLubG;/5ƸZ!EKV$J 25nn1(Y.s}L=J7_w ymw oRzkUNJTJfSYl*6Jr'Wx*yn_znK7 k6Ue[TTDۗL^^^$hdggGΝmTO2Tp9yiii߿o\zrrr\]]mI?0 S[[K'O NUUU\?Hm۳go,!1=}rD"kds>iL.Q8C6Xs[b6!j; уCDO;#isu*_W)R2jP]/lV*hBsc p$"Z/Y200bbqqq(//93gDBB͛CvHʊ)|}}YYYE^ HPSSB޽{#66?<,Xf% .\}s:^鬏H$ Aצ\CVcȐ!t3u.C]紐K111HJJ m ]y#77W\µ񟚚nMbG/5 R `'uzc3݅2xyyA(lMg}||G=8DEE!557qDNeiƷnAAj5looom,mڹNWIM~6bn@ @dd$7r8KQ=%y(61X,qu#Z_BVB(@sS-hjqVFCU47CReK5FPԨ7* @JJjϰHR.Fg EEEȑ# B$''oEAAD\&a7w6J"~sW t\Y/﮴ߣ III:uqm466Ê+`oo0>|~~~زe 7n9ƍ(z[SWsO>|ҒGOcqT*Z1118rr9rrrh޽{8'[K}nx{{B'?$ pttĸqp)d2</g* .+cmtflu"Nj̙3:s ř,$s.chIʧM ܌_Mȑ3κzrjUhn[@8H# 񀍽?v`c [0 S! +hlqcQx1g\pVV(HHHŋl2L<f^Fnn.quڃ7 rlڴ w% Roz*`gg鬬m~gpq-=7\yzzbڴi7o erZӧC&u]>@ӗ8{իMK[[[L2>._=Q׿X-_HMMEyy9,Y{{{SSK1׷o=kaѢEECC96⺥zkS>`tG郦&7 _ ~~~ æMt^tlu&x뭷tb3222`ooFot?Ν;1p@9Xx:r. ~w]>34VJJJs~`?DŽ 0qD#11@dd$W_W_} 6O>s~888oO-Tߚ;wDϞ=d2 )4'R=M1g0#x駑8q"1~xntt4kLv؁#F_Τh 3g`̘1Fef,w1 Y"K}v?@ZZΜ9JΒ_KH2Pm cȐ!͛7v5441kʊRdrcǙ]P(WEW! u)T!9B$vZPyo \<o;D"899=yzzb:wչLvy8tW憼<7 KV cxIK5f\iåKV---ӧ1|p?ׯ_ͣV,:,N>>b̝;[l'0vGN* ۶m?6'-lwz<<2yp UojP(.n q l`0Lw^êa>Ώg#Q/RǗ_~M] +puuG}>}OFSS|GUЙRĕ+WVك;=v~egg;;?{=ju cL~~>Μ9`D"AaaC֪}X-֭{q L6(`0 `0(:ܹ7n-Gh}/ wwwxzzG􄧧'\\\ xˬٳ///$$$sK"$$ &<3Xz5Ə#33fkgee#PPP|':t($ BBBp1UV!88ۍ#0`Nsp}DGG'OM83gԑ={>0v߿?$ e˖!88xwVjMh# aoo8aaa `0 a)/ZؚܷMVC,c#MMACCjkkQ]]jԠͭ3fϟǥKpun¼n:YqFD"Z Gڵk "•+WRiӦaҥEpp0T*e\v QQQx7رcJl۶ ,@^^^9Lz 8}4ƍSG&aرpqq1j6tOJJ”)S~zm믿/ŋq= 4HOO?O|8|Ѿ&[۷iX,Ɩ-[!C`̙mނa0 `0 >th;wAg>3,oeD"|_ڿy//|}}h":t]vO?ŗ_~[bϞ=k< [n}HOr9#F@HH0i$>xjFSO=:m8^n J%N8hii1uT^m7m4 >aaa8q"~i,]x饗:Y-v-^ӧOG>}L֝5/pO^zA" q{Fll,y,X*.\@ii)^y竛#bbbHKK"""Xvmjyohyfbǎ`0 `0,SVׯ6mڄYlJC @.(//GYYt4JJj[R+#G"==%%%ٳBoQPP~!11+d=z4\]]ۭ>Ԅ$L:U mg)dCa˖-HNNK`0 `0 ibΝ2dH ;;;8::NNNprrD"3xO΂`kkgsEرضm.^Sy ,XaaaJm>kо /رcطoV\zd2^{6!ƌXC!%%SN̵h&[4\zӧOǮ]Я_f0 `0 KGD @ ܂UUUgggۘ3g.\Fdgg#%%P(ŋcٲe}:d2Y`0 `0Vs^^^}֭[1aL<Cvv6`۶m~O?Eqq1111B߾}1sL( èQtRL4  Bmm-̕oڴ _~%yrݻCJJ !wwwlx >>b,ENk>#ܼy}VX,{/v`0 `0!(5z/1v\^O?O~i]8PVC @(.'BD*^TDžD"899u쪪*!//O?tǚla0 `0 >1|ej j"^?7,CE<=}ZZ!vbp;]L&ó>&d `0 `toW uuuE]] T*D" DV`޽:=X- `0 n3wpp_VG~~>Μ9} 33yyy "L:UGNFF|M,[ +1f444<4[ϟb-66S73DsbΜ9FZZ=XTW?Ob̺8[1:Nwg}T*лwolܸG]]W{aРA5k+V***®]`ooovvvXp!;fLѴ< Ze)ŋW^վ^y0} @@-Z Fx1X.HPXXjb˺ut޼y3k8qӦM{4J1FǮ]pq 6 }v… :t(`ѢE:ǭXC AMM \\\~!.]W^y׿< {?{\\\ۥKII@n!C:& qoŚ5km6_!mƌؿ?;hn t9s&駟bٲecX;n CCW=zɓ' XۨȂ0( T<J*(@-MX}>(/ &OOkjj0{l K"$$ԄgyW~zdffB  <<ܬ<5, O>CD"AHH;j*ؾ}Q{d2 {{{)?w߿h6xzzrДn'N̙3u䧦gϞFnbb"D(aٲe+}]jZ-Bۣd`pttDXXg瓊17+uDqq1j5=Ν;pppֻ'OĄ :666xy*h.n`ժU FgTZc˒%K0x`H$ 6 ϟ7*O;577c +p%zbڵkLcd _?ٳg1glܸ{9 .ʕ+k}AA?<<͝;\\\+!XoSxx8/TYYI:imcc# B26|l7~')VZEsϑB)+W҃Ņ""ziʔ)DDBvjs!4eJ?P||y:u]r?駟7syH{r`}L/~i :yE"2}Ҕ7O l=SaI&ѠAt% :u***flL=J7_w ymw oRzkUNJTJfSYl*6Jr'Wx*yn_znK7 k6Ue[TTDۗL^^^$hdggGΝ6hdjOV__ONNN\yss3rmd\ GXX_f6jƌCormes[}'iӦQ)++r9={LӧOשWYYIщ'lmm޳gx~D"nZx1/**O999qƽT*M;w4XE'HDO6hJƮ DvSgxӶ\"ea]nM>| sMNdo99BhΜ9mʴ'֭P&Xo䟈23[sY?1!I$Qzz94\,Cs`mLMŻw~:m޼f̘AtJCӨQcLۛ6P(SNq œɿUR6Bceʏ_ BTB\45^-47IEKDb'4_DJe`` b1b1tV9r$fΜ̛7LX 555P(mݻ7bcccmVpJKKkۃnARR -- nnn`Yyחk}m6oތBرݶ<}}6|7 uuu;w.,Y Ν3ZwѢEx饗0uT">|8x ̛7JRϘ(((J2xm:7w/b ADD= 8z(ѣG1qD޶y9жjVWcJZ MePTQ[y WX EK -P*!{@Rި\777]TBTBVYU*.g EEEȑ#`]Pd|(((@~ȕd2=Gx|hjjBRRN} ߶kEáCe$''s 1a a5tŸ~quuŞ={PUU*~~~m BaÆ >|8=nܸSƍ~O@Hd,KƽR4+ߘn||7O?Y,3,v>et>r"S~?b˖-Xlf͚et~TkB*b͚52۷o/gLh&&[{9),.'&&G\.GNNTܻwϟYQcoh~*Q{lu5V9oRAe8B[6B{88@(]Ь3*7((&x"v؁Dl۶ /^)|YJff&`A*AZ _Ʊcǰo>\ "d2kmw!:C3bBJJOyk;KiMhz*O]v_~b5~F~~>._l##** ʕJ%?W_}CB ̙3:,FdK}KK ѿnZPߟ 5k?6lt>cGxװh"梡Gdtd}Lӷo_c5aaaشiɛͿۃ%zKg,>%%%k;KѶӚl>7og}}s'{h/ ??]{0q0@>q8CDxgac5j٣S'((k֬A`` |}}o>67;wgϞG@@d2ܮ,cS1EL8?~`c #j j"^%nmllëll֠7(-D"'Pˠh P7u( \@N-Ú}/ =jv~bgg;;?{_qǙ3gpGJ&[a`0 dV͛7=d2cت'GhhCyšla7#G`0 **Wg0 `0 ;l`0 `0 D7_n` 5 R 1X.HPXXjb˺ut޼y3k8qӦM{4J1FǮ]pq 6 }v… :t(:?`֬Y0a`\.C]zO߿ 2j2ρ ,Z~-֬Ym۶!-- į m3fq1DGGs7l؀h:www.Oڶ2"ѣN< XbE~ұ'MuQSWaP4@xES 9T-UP(*jUZn\}P^M`񁗗K.EHHwW < V^c̄@ @xxYy@k2YYY|  D;v;fժU=}vd2 0￯S~9ܿFm)&N3gOMMEϞ=m;DQQQòe WWW&ݚlF(555&8p#8;T!0߆^37$V1p@n? --wׯnݺ=-T*Y=͖Z pqq#pYv@klYd  DaÆFiǢf,_puuETT.]SXL[v->>|l7~'׶C ʕ+̌ oc`|2tet_>r"S9HC>}b1K|8{,̙7w:xzzb…Xr%w/((}(jjSyF{AKr\S=qoܸqXd 7g׳gO߿iZt}j7 \SSx#<<o6|||pir}ci>o.7[SYQJԣqE}i%oo!C*JLw>+I~s!̦T?t`[HWLʎh*..b1b%$$Q}}=В%KhŊԷo_jjj""kRdd$oyDDRtMRT$J)!!222fϞMRjhh LFڵkDDA$"  uV*++Bu[x1M0ې kL$J;v̙h"m'J)>>N>M׮]~=}t-:~86vZ-\zA񔖖FEEE믓/jX3|_7L7Q>27$H$QFFӧӻ˻۷)44&NHYYY4l0ڰa/=w}zMΝ Zf Ѝ78QQQpB*--嶲2"2=<I;Q^cXNdoGXjs=G B,**V\I< o_)SQHHڵ1صky1Cq13X3 z駹srrH(ŋI,ӃHS<4 +L!'MD ӥK?&t)07|| t2(ݸ~))IEoPCEV9*MۗSY"*͛Mey *ɝDws_+8}Yu/ ۗ_TUiTnQQbn_rr2yyyqH$ݻw;w+ |I6?TJ>{,zL&#"݋b^^ǩj}A#S{lLzrrrʛՕk#Sm/wʕ4`=hm6[Q4fz7 kۥ-#--Mg}}0g\?iL6OYYY$ٳ4x`>}EujkkiDbéH$qculEDDŋ}QQQ@gsrrM{1T*ۛvi\߿O"N>m.S ύ]񤑧mD\;L\5?|3暜Ȕߚis9rМ9sڔiO׭[GMbY[Maijc*fg:ݽ{׉h4c rtt+WZ_Fg<_mзB!:uۧP(,>|_lHƚ˨Cu@Z ͍%hj(½[hn*CK Nhi6f@bbH=rH̜9 7oj{B}WKK ++ ի$ jjjP(׻woǂ ڬ,}W^1y~9::"&&III4!""emuk___ &[ټy3 cǎv$b1Osa̘1 իҢ>:둛+Wo׫c ??j#F"##ʕ+ADܦ%^$aРA3F| @wv}@kY:4ғo;Xb%z0|r"S~^:̝;K,W_}sh"rK:u*cm榖5T`qϴ>>>ѣGGE\\8q"o[<Ԝoh\PPZwM)7ɿR%ZP2(ZP}㨭<ګhn,ʖj(=RQ__oTT*T*V۬*J@gqȳ"DFFbȑHOOGII ziP(Drr2[_~HLLe2F{ZGSS0uTgX-:-[ 99...]rkŜ\kq={ UUUHKKxillw}+Vaaa8|0e˖Dɲd+Jw?-=R,mSF?c*'2/lقe˖a֬Yhnn6XW*bڵJXf oyk{io?X"GS1=ȑ#ATT{K|CS)7*.<*e Hݢ) yEfQAAA5ŋc$&&b۶mxN"n|YJff&`A* ڎ&/رcطoV\zd2^{6!ƌXC!%%SN̵h&[4\zӧOǮ]Я_~R1}TQWkk#??{wE;3܁ Bb89vE Pxp , DUEUXcPDQE A$1! 3G833'4<ꮪw1XS=p|iFm9J¾},,&+rƝv_QQTtA@Dau]p!^~eߖWmOּ:rW>\tmqVU%gc"GYe޽Xf VZZs碲˗/ԩSqiDGGz\Vڪyp's { 6 | pwƷ~D|Gj ݉ww]KcCEJ/߶ g(<}" ޭ}QІm(_t1i$L<Fii)RSSe^DŽ 0}t̙3FB||間p:u :?j#<<صkrrrtRVEJJ `0i& //'ODfǏ#33ƧzepF3fUVc.r*,@U3tPc57xᇑX}vݰwszcΝ(((ѣG1rH7QQQ.ѣGñcLw`|'5>qWHH1yd+V@jj*LbnQQ-^.;v ''6m|||LJZjaaa7ny{z;f :=zիq |xn:nOmړ5kׯNî]~z>\tqV˶hL:Ӷ">>g6}(UVᥗ^r<{l==nڞwұ`\Νŋ@LL .]7pnUӦMéSPRRO?Դٹq׵46Td *0x»-򉀗o{h=o~?Ghˁi JЖ^r%QF!$$#GDjj*^Caa駝@ә~aذahѢ:wC;L6 ٳgDqUY.]{O>$n:tزe D"$$"}[epرcq;c.r*,K/ܹs?izn3fPu1?x jد7Pw:v%K 22F€vZwyC+V`ƍ߿obȐ!1bڴid۷moZliڼyKŋ۷/ݻw@:dnժU[q]w!<<ӧOʶJڵkrJ|Ǧ"?#hZnOmۓu!551113guOOO}Ե\tq>%Gc"GY^MGA||< Cnq6gPڨyp'[ST>|8rrr0dqqqưan[G5o322O۷uz-w}7"##_X,wvnu UYN6H9x DiM#/Wd~ 1fR jT?hF]\YPUf _IOP1l8{쁯/jh4_Ĺs;ubJ(˩S0zhműcJs`0 !!zJºuluVq <Ngܕz]^^^xQQQj{PEaPT5^|A6CE9 Clܸ?3BBB0bĈFp\틬׳UoEO?4&O8$''c۶m1b 'mm6 ߭ݵPUVV:t/g}EEE3f@oz̓XիW_Ǥ!XN)ŕ뢻\\O{{/ۿ5B#è9`Th,*TBR mHKE@ڳi3~1ؼy3^}U˵Z-2229W /[ ׯ?7i2E7͛/̛7:˗/ٳqx t}Ǝ y...իcoM68|0@%֭[7>\\Yo˖-mۚիW]Iܶm[k0m4|GXp!^{5$''O?}Q|ؾ};L/Yqqq8~Ic0?Db,8zoˊΣ07A-B_2e0PYqz}>XY(-A->e=-,,ĉѲeKh&L0}:{loIdYY:vC ŋgT*tiz@խ))))____#44*Vcm,X(M6x7'11r |||{bp%-CXXQ81~xpM7a֬Yv]u+V@nݠj3g`Μ9BPPz)îSAaa6n܈=z111rި}[uv}#Ǘ_~C};z`4q뭷 G$'';͇~ ,@tt4Я_?߿r:e̙ѣZ-CMϼ/*//ܹs ѣ-ZYfY\)N{ηСCPT8q)ݻwCRwÕrٺҵ˕lјZN0k,{=;ؿ?&O_|:t&,, SNMtlذgwglή[mhS:a͝~}>x9soAdd$&NhM7݄?Д"cbGuV 1vXk>lgm[zRt鈜?:_ο"y矗˺r«rI/r|;7Ur'J1;'@L6lʼnNN' &Hqqo^fΜ)""͓Ν;KYY,ZHrz""%;wN*++%00P&L wN''N0oFӥD>}ZDDBCCO?\QղrJΖ ꫯ,7}t:to[e dS~Jyyi˴iӜ@;vݻWN>-]ty;vٲeKr*,N<)*JN:ewP;v$''Kff}X`j֬ivo ˪Ul..]$Fk\9޵AD\.n{NϼΎ>)Wa\"?093_|ɓ'Xfȿ -a!۸;:u4u7wgJpxۜ}>_O?ȲeG???9q ŋˀL۸vLjoͼnX9''Gj|"" +JC (-;^Ѡ{8s|V6n* ])u@s=UsvmOW;r't\϶9PTT~3gᅬ]wڴiýދ1cƠSNn۸;cGXWwWێ/ѲeK۶ml۶ #GDll,L >Xk1a^tFѣ"CFjPeC_Q(ہ{Qr$KuWPQPTVQ\\l7`U``h1[k`` XLRܑ㮻®]ptM6Uؼy3>#K.Xbiybb"Zرcg 3fD;w),նn݊W^y7oF@@@C}[j ѮgPT8{gϞEddd렠 ]W\+W oooDDD)|T*h4r ˛+eNߝצ=P甮 gkDm]wn ̙x\700-B`` .\r5VkX:N:_생6l ㈍E\\pE:tb^N0X\LJ5njT ! @%Tjww[Qa@nڵI9z +Vk#GX,7͕ܵgDFFbʔ)A```g٪T*w}ؾ};6l؀磸"DW'eРA֭[e3ƴٱsWu9Tj'O?իWK.uJFeW79jv}= All,njz`0`ǎx,u^رcuRo>; qWTT 55ݺu3gk \=Q?Aj]o.\_~7Uvwۓ5okNܕ`*][jm٘QuVmٻw/֬YUVAVcܹn3uT>}Ѯ 뢾uUKp's/ 6 | pwƷ~DQoNxxx8>)2D0Wxo`oh}:̙QF!>>tGxx8N:N~iztڵ 999XtA"%%eee0 شirrr'OYfǑY42 #GČ3ЪU+ٱsy9T|2x=F?0kخ7`y]_Ν+V )) 9999s&|||,qVΝ;QPPGbȑ7nۮ A||<&OT`ŊHMMŔ)S,-**Bvvūԥvc 77ӦMCIv^-,, ƍ3<4cϞ=6UonjCGXz5N8o6֭[VM{vatصk֯_qG}=3`ٖ[guږRc٦/j*K.gG7J.ufNm;XsvsΈŋMㆈ`ҥzwP5y1m4:u?Ta(/w[/ {~6ЖӬ-rJ :FBHHFTkBO;N3aТE tǏ^w^m 0gƃ>ݻիϐ,]|Iaݺuڵ+"""e$$$றc… ;v0g]TRYॗ^¹s윇f̘Ç"--V8a|5Q}k٣>jx'ֹۛcɒ%ԩ~ܹukAǎdDFFbԨQ0`֮][/x71d1mڴArr2WV7x-[xm޼٥vEQQQHKKݻf2j*z뭸뮻ӧc[VRaڵXr%>cӭ}Z[eںu됚̙3ݺuK>Z.Rے1zN /@Djx# >>e9sQx}u>lq;xۚ}c* ÇGNN bz?..6lm޸21nz wvܥrMCgp'Jklzy"CPx/1 KPkAOT!(.1U 󀨚KѠYf+W3go),Դx°f͚:?@׆=ݜѣ4rDDDJh~W`QND'dThޢ<="&deJjRЗ4r4V𤤲P&" ƑGކ))2~B!4,Oj /Ci@V} j xyy1o$֭1YJIe!xzSSgؽ{w~U gDՏ4۷7nlԙB|Q}3HJJ²e˰nͪݗDGG7On4%k,5sΦ_jݗ"g'""""""1'""""""Rg7( c /]#""""""rgjCEiJ.FxCh.A(-C/pZ񇂈ߡmѫ #"""}yq| j 5@X("c4QYYq$,,4___(hċ/swV^9PSNah۶-cǎ9n[`@BBzJu\o֭;x::.]B||<:t耠 u]6-ļypM7Abh5 0*>suվz=G6!!FXXZh#11vEc\ZXXnsT?޲e tŋիWٳg}YT*Yހ`[K1b[DII :t_|>,Lg̘ݻ#>>/͛H+33W[y233vZ;Mǣ =>GRk[\˕13EEE8~gMt#=- ?!vә3HOOǹtW|oQ ,P H1!-w;]:mhϦ5h4bxWm.jh\ՎR /Xl2_0nܸݰ7o/0o<,_gx7бcG|g;v,`͚5hٲ%^uvHgΝСCsE׮]z/-hݺuվȕlق?h۶^z5DZm۶EvӦMG} ^Crr2>S|PGŇ~۷#..%KǏ~)"" @DDL!Cc爨ZC]9j~a# @ F# #"@{u)}E~_Vt)jt/˄Aʊ+TGi j/siaa!&N-[E0aٳg}OбcG<2d/^={@Rk׮Nn7III_"==xWѧOhZo۷ ,@TT|||ЦMv˓[n>>>s=gt!,,Tm?~EIII馛0k,Ǯ:+V[njř3g0gDEE!((O=aWRY̩jz7nD=燘S9oT꾭|ۺuY/4V>A=]t?l,?? rgfEE,Xh_~ؿtU}̙3ѣGhZy_T^^s"::MVIDATAAAѣG-ֳק-ZfͲRvgϝTokۡCRp SwJ2m8ۇ+uk+ٚ11QN:a֬Y{xw~L</":t`MXXN目ذaCkkPظq=g&G}5GV+㱺QGw5Gz9soAdd$&NhM7݄?Дk(gaI~ _Fj`%^I۶9tE?/uʅWʅ_wnO3cDwOr1}*'=l0N':N'&Lbi߾̜9SDD͛';w2YhDD%**J>s9wTVVJ``L0Av-:N&N(aaaRYY)""|󍤧KII$&& 9}ʧ~*""Veʕ-W_Yo2tP߶*ɦ_ rӶǏiӦ9=v2vXٻw>}Zt">>>/?,;ve˖TbY̝|H߾}eɒ%.YSOI+ .OOO9{)X:udee^" QFIRRO?-oZn^I&I׮]/IIIUg]oKKKEVKJJcJ?wSuz׶/ L|`k/wns:v~}sKimgdoL޺2>rd#vze2||ȦMDD䡇ѣGHe5ѲÇS"ίMWvh}g㱺Q8զr7>~jy˗/khڎml_&mce z1+D_Q.eR^Z"e%ERR|UHqZpY \˒mK"EWvcs%;md(g&JG©SȅCDwN9|Mr%{t333t:{7o-Z޵kh4Yfx{{ˁLˬ++?7~ .\ILL̙3@?ns;(aeN<`bi֬iyyycg[nGLL,^F9VsFQ $<2O#99YȥKnTupv͏+Zl]+ڵk%::ZD'4i|7!6l___ٺիWeԨQҮ];]ʕ+W\ʛ|eh4/Rնz-ӧO7٫Yfڽu4 .V/th4ٻwr9Ts{q쎎:=:L:;p\>\s>8zL:|@&O\cy /HttC z)9r86׸r\E\kW;G刻#W+oy`^@~'Yl<''NT}xb0`iWڎ] 5t+ eTmGA'xh0@_~PVY?,eyWB ' m۶F:wuƏ &gA>}Pu#yUTTRRR0bj Z5СF;SL1ÇezN6aÆ!!!`{Ǯu5nnݺXSRY-[ x뭷j]o6jQF]+,,4=Vׯ"###dž L<(..ƩSp DDD[nUۼ h4_~* kϷ|b/wڽFAq[minݺ5T*~WRv봁n{f+wN>Rm7&rTok[ɺ~i̜9>8`wiӦ!//{/ƌN:U6g.^htٵ6liݮ\Z>ݭ#y`8-[D޽m6m0rH"))].?[ s}0aDL8 & &=IONj j=BQ^ }EJ  oEՓ(/A_QCE RhQY_@JC!XaJ_nv+* ({M]vr8 ԑ#G[oaŊxp2كHL2111 ј̃J۷odž 0|CD5f lݺ[l1cL˜;w_ДRj'O?իWK.uJFeW79jv}= All,njz`0`ǎxL >;vDTT/_6-h4h۶mPTطo}Ť?θ+**nݺ޳QDau]p!^~eߖWmOּ:rW>\tmqVU%gc"GYe޽Xf VZZs碲˗/ԩSqiDGGz\ T]\icWkohʶ\N: 6 | pwƷ~D.1ت-\`~~IPh_`\-Dy/m x'ޭ^~1im=ߏ _xEM&Mɓqa"554c^DŽ 0}t̙3FB||鶞p:u :?j#<<صkrrrtRVEJJ `0i& //'ODfǏ#33ƧcepF3fUVc.r*,@U3tPc57xᇑX}vݰwsz7w\XIII̙3cĉwwEAA>#$$$'PuG{ǎ3݁' ]!!!ɓX2eźEEEζxw؁bڴi1}(iΫaܸqxgbٳ1c0tPWƉ'ƺu*=iOѮ];_:v8X>EglKDꭳ:mKii)1{lӇXj^z:f^o3gٳtǣk6ql˵?ߜڹΝ;#22/61"""K:忮qAmتUxzz_vTrerlxEѝ+?_N>*zB.,is%/5)G~^~iG{e֬YҮ];ˢEDDdŊ%""rE ˗HD:uV+󟥢az"5'N?h9sHPPlRfϞ-={4&G,4h4o\|||[o;vH$tsO*IVzRY;tyҥE'1/""s QբhLIQ$::ڴ?@j/bn>"5ϑv$<J%F,OO<`sw-ZVw^cb"5kDFFO>ĥ|:keeesI۶m%00P"ٚ?qCCCeذaҮ];j2`IOO7m]"y'UVjܹzꭈ|  ~rڴ'[׶={HLLm&+WOOOrG>jS.Wq¿>%Gc"GYe-5=ҫW/'fk?[}?PcYVFήMژUfrX](Gj[9+5Gz̙@~{sέ1IkgWDlՍOAԔpOz=rE+^c*A#(+BP\b@.3Q5FAf=+W 88gΜ7\[SRYi|Sma͚5uh_o;z6GE #""RvnQo 7(oTۡVw^|EENq-Zs!rmBVP4hAV( }A}y}0MCN WMcOJ* 5-o"`9zm" B49\AqY j<ڠ6Fnݺ:?{PRY9o"Y ""rbj6Kooox{G $̎xҰo>lܸRgJ* 9MDDDDGU*@T*vZ|+^ZZP#""ϯADDDnRk(**Vu{މ^^QQ=^3=q{;""_D~./ݞ]VK9xȥ\EngBjZbԴwIU9ݎBDDD48x{aB|-zۓ ,^4qUVMB@DDtCcODDtn~,Z&pw}bϘ bARU}ac;"""Y}d#d3Wkl7nO~)5؎Hxrd5zܚ0[n툈~ODDt][vrosfдܜ UގcODDt}߀، `ī/eˑVCY*uS瀈f,j@\ݚ7\ *^3wqpnÄ'ƍpw<#0Z-SI#"""Ko'xFl1/SَF3ʦ VmVoV툈nd"=*ΉKắr<#DDDDDDD>{9]ǥ_=!"""""""cODDPۧ'""o'""RརN'"""""""""mDDDDDDD J?""""""k+?R N!""""""s.?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD H)"""""""cODDDDDDp ?1'""""""R8DDDDDDD ;ސ """"""͒5ODDDDDDp ?1'""""""R8DDDDDDD H)"""""""i`QIENDB`cppcheck-1.90/man/manual-ja.docbook000066400000000000000000002517431357737443600172440ustar00rootroot00000000000000 Cppcheck 1.87 2018-04-23 イントロダクション Cppcheck は C/C++の静的解析ツールです。C/C++ コンパイラやその他の解析ツールとは異なり、シンタックスエラーを検出しません。 その代わりに、Cppcheckは、コンパイラが通常、検出に失敗するような種類のバグを検出します。このプロジェクトのゴールは、擬陽性 0 です。 サポートしているプログラムのソースコードとプラットフォーム: さまざまなコンパイラの拡張構文や、インラインアセンブル等を含む、非標準的なソースコードをチェックできます。 Cppcheck は 最新のC++規格をサポートしている、あらゆるC++コンパイラでコンパイルできるようにしています。 Cppcheck は 十分なCPUパワーとメモリーのある、あらゆるプラットフォームで動作するようにしています。 Cppcheckに限界があることをご理解ください。Cppcheckの報告しているエラーに稀に間違いのあることがあります。また、Cppcheck が検出しないバグが残っていることもあります。 ソフトウェアを注意深くテストすれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。ソフトウェアを注意深く実装すれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。しかし、あなたのソフトウェアを実装するときやテストするときに見逃したバグのいくつかを Cppcheckが検出できるでしょう。 GUIでのはじめ方 GUIの起動
新しいプロジェクト(New Project) 新しプロジェクトのファイルの作成は必要ではありませんが、最初のステップに最適です。ファイル(File)と新しいプロジェクトファイル(New project file)を通じて学べます。
新しいプロジェクト(New Project) - パス(Paths)と定義(Defines) あなたのプロジェクトはどのようなプロジェクトでしょうか。あなたのプロジェクトがVisual Studioのプロジェクトの場合、または(cmake/qbs/等の)コンパイルデータベースqが精製できる場合、あなたはプロジェクトをインポート(import)できます。 そうではない場合には、そのプロジェクトのパスと定義をマニュアルで調整します。次の図は、Visual Studio のプロジェクトファイルをインポートした場合のスクリーンショットです。
新しいプロジェクト(New Project) - プロジェクト(Project) プロジェクトタブ(Project tab)では、ビルドディレクトリ(Cppcheck build dir)を設定しましょう。これはCppcheckが様々な分析する情報を保管するために使用します。プログラム全体の解析、インクリメンタル解析、統計などです。それぞれのプロジェクトは、それぞれのビルドディレクトリを持ちます。次のスクリーンショットはビルドディレクトリをcppcheck-build-dirと設定しています。このパスはプロジェクトファイルからの相対パスです。 あなたは、あなた使用する全てのライブラリーを選択すべきです。次のスクリーンショットではmicrosoft_sal と windowsライブラリーを選択しています。ライブラリーについてはこのマニュアルを参照してして下さい。
新しいプロジェクト(New Project) - アドオン(Addons) ここでは 除外タブ(Exclude)と抑制タブ(Suppressions)をスキップします。これは結果をあとで微調整するために使います。 アドオンタブ(Addons)であなたは別の分析を追加できます。このアドオンにはpythonが必要です。
解析(Analyze) ダイアログのOKボタンをクリックします。解析がすぐに始まります。 全ての警告が有効になり、やや賑やかになります。あなたが注意しない様々な警告があり得ます。これは簡単に修正できます。メッセージを右クリックして、隠す(Hide)または抑制( Suppress)を選びます。メッセージの隠しは永久ではありません。これは次の解析でまた表示されます。メッセージの抑制は、永久です。抑制されたidはプロジェクトファイルに保存されるので、これらは二度と表示されません。
コマンドラインでの始め方
最初のテスト これは単純なソースコードです。 int main() { char a[10]; a[10] = 0; return 0; } このソースコードをfile1.cに保存して次のコマンドを実行します。 cppcheck file1.c cppcheck は次のように出力するでしょう。 Checking file1.c... [file1.c:4]: (error) Array 'a[10]' index 10 out of bounds
フォルダ内の全てのファイルをチェックする 通常、プログラムは多くのソースファイルから構成されます。そして、それら全てをチェックしたいでしょう。Cppcheck は一つのディレクトリ以下の全てのソースファイルをチェックできます。 cppcheck path ここで"path"はディレクトリのパスです。このようにすれば cppcheck はディレクトリ以下の全てのファイルを再帰的にチェックします。 Checking path/file1.cpp... 1/2 files checked 50% done Checking path/file2.cpp... 2/2 files checked 100% done
マニュアルでファイルをチェックまたはプロジェクトファイルの使用 Cppcheckでは、ファイルやパスを指定する事でファイルチェックを指定できます。一方ででプロジェクトファイル(cmake/visual studio)を使用できます。 プロジェクトファイルの使用は早急に始められます、というのもあなたが設定してする項目が少なくなるからです。 マニュアルでのファイルチェックは、解析をより細かく制御できます。 どちらのアプローチが良い結果になるかはわかりません。両方の方法を試して下さい。両方のアプローチを使用するとより多くののバグを見つけられる結果が得られるかもしれません。 以降の章でより詳細を説明します。
チェックからファイルやフォルダを除外する ファイルやフォルダをチェック対象から除外する方法は二つあります。最初の方法は、あなたがチェックしたいファイルやフォルダだけをcppcheckに指定することです。 cppcheck src/a src/b src/asrc/b 以下の全てのファイルだけをチェックします。 第二の方法は、-iオプションと共に除外したいファイルやフォルダを指定することです。次のコマンドではsrc/c以下のファイルをチェックしません。 cppcheck -isrc/c src このオプションは現在--projectオプションと同時に使用できません。また、このオプションが有効なのは、インプットディレクトリが提供するされたときです。複数のディレクトリを無視するためには、-iを複数回使用します。次のコマンドではsrc/b と src/c 以下のファイルをチェックしません。 cppcheck -isrc/b -isrc/c
Severities(厳格度) メッセージのseverities(厳格度)には次のものがあります。: error(エラー) バグが検出されたときに使用します。 warning(警告) 防衛的プログラミングでバグを避けるための提案です。 style コードの可読性の向上に関連した、スタイル関連の指摘(未使用関数、冗長なコードなど) performance コードの高速化のための提案。これらの提案は、一般的な知識に基づいたものでしかありません。このメッセージの修正によって計測できるほど処理速度が向上するかどうかはわかりません。 portability 移植性についての警告。64 bit CPUへの移植性。コンパイラ依存(独自拡張)ソースコードについての警告など。 information 設定上の問題設定を変更している間だけ有効にすることをお勧めします。
メッセージの表示 デフォルトではerrorのメッセージだけを表示します。--enableを使用すると他のチェックを有効にできます。 # warning のメッセージを有効にします。 cppcheck --enable=warning file.c # performanceのメッセージを有効にします。 cppcheck --enable=performance file.c # informationのメッセージを有効にします。 cppcheck --enable=information file.c # 歴史的な理由により --enable=style を指定すると warning, performance, # portability と styleのメッセージを有効にします。古いxml形式を使用しているときには、これらの厳格度を"style"として報告されます。 cppcheck --enable=style file.c # warning と performance のメッセージを有効にします。 cppcheck --enable=warning,performance file.c # unusedFunction のチェックを有効にします。今回は --enable=styleでは有効にできない。 # というのは、これではライブラリではうまく動作しないからです。 cppcheck --enable=unusedFunction file.c # 全てのメッセージを有効にします。 cppcheck --enable=all --enable=unusedFunctionはプログラム全体をチェックするときにだけ有効にしてください。また、--enable=allもプログラム全体をチェックするときにだけ有効にしてください。というのは、unusedFunction チェックは、関数が呼び出されなかったときに警告するチェックだからです。関数呼び出しがチェック範囲にみつからなかったという可能性のノイズになります。
疑いのあるチェック Cppcheckはデフォルトで解析に疑いのない場合にだけエラーメッセージを表示します。しかし、--inconclusiveオプションを使用すると、解析に疑いのある場合であってもエラーメッセージを表示します。 cppcheck --inconclusive path これは、もちろん、実際に問題がないものに対しても、警告することになります。このオプションは、疑いのある警告を表示してもよい場合に限り、使用してください。
結果をファイルに保存 多くの場合、チェックの結果をファイルに保存したいと考えるでしょう。通常のシェルのリダイレクション機能を使って、エラー出力をファイルに保存することができます。 cppcheck file1.c 2> err.txt
マルチスレッドチェック オプションの-j を使用してスレッド数を指定することができます。例えば、4スレッドを使ってフォルダ以下の全てのファイルをチェックする場合は次のように実行します。 cppcheck -j 4 path このチェックでは未使用関数の検出(unusedFunction checking)は無効になることに注意してください。
プラットフォーム あなたがターゲットとするプラットフォームの設定を使用すべきです。 デフォルトで、Cppcheckはネイティブのプラットフォームの設定を使用しますので、あなたのソースコードがローカルの環境でコンパイルし実行する場合には正常に動作するでしょう。 Cppcheck にはビルトインのプラットフォーム設定として、unixwindowsをターゲットにしたものがあります。コマンドラインオプションの--platformを使ってプラットフォーム設定を指定できます。 XMLファイルで自身のプラットフォームにあった設定ファイルを作成することもできます。ここに例をあげます。: <?xml version="1"?> <platform> <char_bit>8</char_bit> <default-sign>signed</default-sign> <sizeof> <short>2</short> <int>4</int> <long>4</long> <long-long>8</long-long> <float>4</float> <double>8</double> <long-double>12</long-double> <pointer>4</pointer> <size_t>4</size_t> <wchar_t>2</wchar_t> </sizeof> </platform>
Project(プロジェクト) CMakeやVisual Studioを使っているとき、あなたは--projectを使ってプロジェクトを解析できます。 これでカンタンにチェックでき、結果も得られます。あなたに必要な設定項目はありません。しかしこれが最も良い結果を得る方法とは限りません。私たちは、このプロジェクトファイルを利用する方法と、--projectを利用しない方法を試してよいオプションを選ぶようにお勧めします。
CMake Cppcheckはコンパイルデータベースを理解します。あなたはこれをCMakeで生成できます。 例: $ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . compile_commands.jsonファイルが現在のディレクトリに生成されます。 それからCppcheckをこのように実行します。: $ cppcheck --project=compile_commands.json
Visual Studio あなたは個々にのプロジェクトファイル(*.vcxproj)でCppcheckを実行できますし、ソルーション全体(*.sln)でも実行できます。 # run cppcheck on a whole solution $ cppcheck --project=foobar.sln # run cppcheck on a individual project $ cppcheck --project=foobar.vcxproj Visual Studio内でcppcheckを実行するための、Visual Studioプラグインもあります。
プリプロセッサの設定 あなたが --projectを使用した場合、Cppcheckはプロジェクトファイルからプリプロセッサーの設定を読み取ります。 そうでなければ、あなたはインクルードパスやディレクティブを設定したくなるでしょう。
ディレクティブ ここに2つの設定があるファイルがあります(Aが定義された場合と定義されていない場合): #ifdef A x = y; #else x = z; #endif Cppcheckはデフォルトでプリプロセッサのデファインのコンパイルスイッチ設定の組み合わせを全てチェックします。(ただし、これらのうち #error を除く)そのため上のコードは、Aが定義された場合とAが定義されていない場合の両方を解析します。 これを変更するには -D を使います。また -D を使用した場合、cppcheckは与えられたコンパイルスイッチだけが有効でその他は設定されていないとしてチェックします。これは、コンパイラのように動作します。また、 --force--max-configs を使用すると、コンパイルスイッチの組み合わせの上限を上書きしてチェックすることができます。 # 全てのコンパイルスイッチの組み合わせをチェックする。 cppcheck file.c # Aのコンパイルスイッチが有効になっている場合の組み合わせをチェックする cppcheck -DA file.c # check all configurations when macro A is defined cppcheck -DA --force file.c また、もう一つのオプションに-U があります。これはシンボルのundefとなります。使用例: cppcheck -UX file.c これはXが定義されていないことを意味します。Cppcheck は Xが定義されている組み合わせをチェックしません。
インクルードパス指定 インクルードパスを追加するには-Iオプションに続けてパスを指定します。 Cppcheckのプリプロセッサは基本的に他のプリプロセッサと同様にインクルードを扱います。しかし、その他のプリプロセッサはヘッダファイルが見つからない場合に停止するのとは違って、cppcheckはただ単に、メッセージ情報を表示してソースコードの解析を続けます。 cppcheckは常にソースコード全体を確認する必要がないので、このような仕様になっています。実際に、全てのインクルードパスを与えないことを推奨しています。もちろん、クラスのメンバーの実装を確認した上でクラスの宣言をCppcheckでチェックするのは有用ではありますが、標準ライブラリのヘッダーをCppcheckに確認させるのは有用ではありません。というのは、チェックにかかる時間が長くなり、あまりよくない結果が表示されるからです。そのような場合、.cfg ファイル (後述します)によってcppcheckに関数や型の実装の情報を提供する方がよいでしょう。
XML出力 Cppcheckは出力をXML形式に変更できます。--xml オプションでフォーマットを指定します。 ファイルをチェックし、XML形式で出力するコマンドのサンプルです。: cppcheck --xml file1.cpp これが出力例です。: <?xml version="1.0" encoding="UTF-8"?> <results version="2"> <cppcheck version="1.66"> <errors> <error id="someError" severity="error" msg="short error text" verbose="long error text" inconclusive="true" cwe="312"> <location file0="file.c" file="file.h" line="1"/> </error> </errors> </results>
<error> 要素 それぞれのエラーは<error>要素に記載されます。属性: id エラーのidこれは、妥当なシンボル名です。 severity 以下のいずれかです: error, warning, style, performance, portability, information msg 短い形式のエラーメッセージ verbose 長い形式のエラーメッセージ inconclusive この属性は、メッセージに疑いのある場合にのみ使用されます。 cwe メッセージのCWE ID。この属性は、メッセージのCWE IDが判明している場合のみ使用されます。
<location>要素 エラーに関連する全ての位置情報は<location> 要素内にリストアップされます。主要な位置は、リストの最初の要素になります。 属性: file ファイル名相対パスまたは絶対パスのどちらかです。 file0 ソースファイルの名前(オプション) line info オプションの、それぞれの位置につiての短い情報
出力の形式の変更 もし、テンプレートを使用して、出力の形式を変更することができます。
事前定義した出力フォーマット Visual Studioに互換性のある形式が必要な場合には、--template=vsを使用します。 cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c このオプションは出力形式を次のように変更します。: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds. gccに互換性のある出力が必要な場合には、--template=gccを使用します。: cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c このオプションは出力形式を次のように変更します。: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] a[2] = 0; ^
ユーザー定義出力形式(1行) 自分で自身でパターンを作成できます。例えば古いgcc のよuな出力フォーマットで警告メッセージを出力してほしい場合次のように指定します。: cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c このオプションは出力形式を次のように変更します。: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. コンマ区切りフォーマット: cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c このオプションは出力形式を次のように変更します。: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds.
ユーザー定義出力形式(複数行) 多くの警告は、複数の位置を指定します。サンプルコード: void f(int *p) { *p = 3; // line 3 } int main() { int *p = 0; // line 8 f(p); // line 9 return 0; } 3行目でヌルポインタのデリファレンスの可能性があります。Cppcheckは追加の位置情報を表示してその結論がどこから発生したかを示すことができます。そのためには、コマンドラインで--template--template-locationの両方を使用する必要があります。 サンプルコマンド: cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c cppcheck は次のように出力します。 Checking multiline.c ... multiline.c:3: warning: Possible null pointer dereference: p *p = 3; ^ multiline.c:8: note: Assignment 'p=0', assigned value is 0 int *p = 0; ^ multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0 f(p); ^ multiline.c:3: note: Null pointer dereference *p = 3; ^ この警告の最初の行は--template で指定したフォーマットです。 この警告の残りの行は--template-locationで指定したフォーマットです。
--templateで指定するフォーマット --template では以下の要素が利用できます。: {file} ファイル名 {line} 行数 {column} カラム番号 {callstack} 全ての位置。それぞれの位置は[{file}:{line}]のフォーマットで記載され、また->で位置を区切ります。例えば次のようになります。: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3] {inconclusive:text} 警告が確定的でない場合のメッセージを表示します。このメッセージは含まれていない場合もある、任意のテキストです。サンプル: {inconclusive:inconclusive,} {severity} エラー/警告/スタイル/性能/移植性/情報 {message} 警告メッセージ {id} 警告id {code} 実際のコード \t タブ \n 改行 \r キャリッジリターン
--template-location で指定するフォーマット --template-locationでは以下の要素が利用できます。: {file} ファイル名 {line} 行数 {column} カラム番号 {info} 現在位置についての情報メッセージ {code} 実際のコード \t タブ \t 改行 \r キャリッジリターン
Misra CppcheckはMISRA C 2012 向けのチェッカのアドオンを持っています。
要求事項 必要なもの: Python (2系 または 3系) MISRA C 2012の PDFこのPDFはhttp://www.misra.org.ukで購入できます (15-20 ポンド)
MISRA テキストファイル MISRAルールテキストの公開は禁止されています。そのためMISRAルールテキストはこのアドオンから直接利用できません。代わりにこのアドオンはテキストファイルからルールのテキストを読み込みます。MISRA PDFの ”Appendix A Summary of guidelines"のテキストをコピーペーストした場合、それがルールのテキストになります。 もしあなたがxpdfを持っているなら、テキストファイルはコマンドラインから簡単に生成できます。 (pdftotextxpdfに含まれています。): pdftotext misra-c-2012.pdf output.txt この出力は100%完璧であるとは限りません。少し手直しする必要があることもあります。 その他のpdfからtextに変換するソフトでもうまくいくでしょう。 テキストファイルをマニュアルで作成してするには、MISRA PDFの Appendix A "Summary of guidelines" をコピーペーストします。フォーマット: Appendix A Summary of guidelines Rule 1.1 Rule text Rule 1.2 Rule text ... あなたが無効にしたいルールは、ルールテキストがなくても構いません。ルールテキストのないルールはアドオンによって抑制されます。
出力の抑制 ある種のエラーをフィルタリングしたい場合、出力を抑制することができます。
プレーンテキスト抑制 エラーの種類によって出力を抑制することができます。つぎのいずれかの形式で出力を抑制します。: [error id]:[filename]:[line] [error id]:[filename2] [error id] このerror id は抑制したいエラーのidです。このエラーのIDを簡単に調べるには、--xmlオプションをコマンドラインで与えます。そのXML出力から、idの文字列が取得できます。このエラーのIDに*を指定して全ての種類のメッセージを抑制することができます。(これは指定したファイルに限ることができます。) またfilenameにはワイルドキャラクターである、* または ?を含めることができます。前者には全ての文字列にマッチし、後者は任意の一文字にマッチします。またWindowsを含む全てのOSで、パス区切りに"/" を使うことをお勧めします。
コマンドライン抑制 --suppress=のコマンドラインオプションを使用して、コマンドラインで抑制を指定することができます。例: cppcheck --suppress=memleak:src/file1.cpp src/
ファイルで抑制リストを指定 また、抑制ファイルを作成することもできます。例: // src/file1.cppのmemleak と exceptNew の エラーを抑制 memleak:src/file1.cpp exceptNew:src/file1.cpp // 全てのファイルのuninitvarエラーを抑制する。 uninitvar 空行やコメント行を抑制ファイルに記載することができます。 そして、この抑制ファイルは次のようにして使用します。: cppcheck --suppressions-list=suppressions.txt src/
XML 抑制 XMLファイルで抑制を指定できます。サンプルファイル: <?xml version="1.0"?> <suppressions> <suppression> <id>uninitvar</id> <fileName>src/file1.c</fileName> <lineNumber>10</lineNumber> <symbolName>var</symbolName> </suppression> </suppressions> このXMLフォーマットは拡張可能であり、将来さらなる属性を加えるかもしれません。
インライン出力抑制 エラー出力の抑制をソースコード中に直接、コメントの形で記載することもできます。このコメントには特別なキーワードを含めて記載します。ただし、インライン出力を抑制するコメントをソースコードに追加すると、ソースコードの可読性が少し悪くなってしまうかもしれません。 このソースコードは通常エラメッセージを出力する例です。: void f() { char arr[5]; arr[10] = 0; } 前のソースコードに対する出力は次のようになります。: # cppcheck test.c Checking test.c... [test.c:3]: (error) Array 'arr[5]' index 10 out of bounds このエラーメッセージを抑制するには次のようなコメントを追加します。: void f() { char arr[5]; // cppcheck-suppress arrayIndexOutOfBounds arr[10] = 0; } これで、--inline-suppr オプションの準備ができました。次のようにcppcheckを起動するとエラーが抑制されます。: cppcheck --inline-suppr test.c 特定のシンボルにのみ適用するインライン抑制を指定できます。: // cppcheck-suppress arrayIndexOutOfBounds symbolName=arr 抑制のためにコメントを書きます。; や // を使って開始点を指定できます。 // cppcheck-suppress arrayIndexOutOfBounds ; some comment // cppcheck-suppress arrayIndexOutOfBounds // some comment
ライブラリ設定 WinAPI, POSIX, gtk, Qtなど他の外部のライブラリを使用した場合、Cppcheckは外部の関数がどのようなものであるかがわかりません。Cppcheck はそのため、メモリリークやバッファオーバーフロー、ヌルポインタのデリファレンスの可能性といったさまざまな問題が検出できません。これを解決するには設定ファイル(.cfg file)を使用します。 Cppcheckはいくつかのライブラリ用の設定を持っています。これらは次のようにしてロードできます。cppcheckは C または C++言語用の標準ライブラリの設定 std.cfgはいつもロードします。ご注意ください。もしあなたが有名なライブラリの設定ファイルを作成した場合や更新した場合には、私達のサイトにアップロードしてくれると非常に助かります。
カスタム設定ファイル(.cfg file)の使用 あなたのプロジェクト専用の設定ファイルを作成し、使用することができます。そのためには、--check-library--enable=information を使用して設定のためのヒントを入手します。 設定ファイルの編集に、Library Editorの使用をお勧めします、これはCppcheck GUIに含まれています。これはViewメニューで使用できます。すべての設定がこのマニュアルに載っていません。 この設定ファイル.cfgのフォーマットに質問がある場合、フォーラム(http://sourceforge.net/p/cppcheck/discussion/)で質問してください。 コマンドラインのcppcheck はカスタマイズした設定ファイル(.cfg files)を作業パスから読み込もうとします。作業パスはcppcheckを実行しているパスですでそこに設定ファイルがあると考えます。 GUIのcppcheckはプロジェクトのファイルパスから設定ファイルを読み込もうとします。カスタマイズした設定ファイル(.cfg file)は プロジェクトファイルの編集 ダイアログで確認できます。このタイアログを表示させるにはファイル メニューから開いてください。
メモリーリソースのリーク Cppcheck はリークのチェックが調整できます。言い換えれば、あなたはメモリーやリソースを割り当てる関数またはその割り当てを回収する関数を指定できます。
alloc と dealloc ここにサンプルのプログラムがあります。: void test() { HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); } 上のコード例はリソースリークの欠陥があります。 - CreatePen() は WinAPI 関数でpenを作成します。しかし、Cppcheckは関数からの返り値が解放されていなければならないと仮定しません。そのためエラーメッセージは表示されません。: # cppcheck pen1.c Checking pen1.c... もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: # cppcheck --library=windows.cfg pen1.c Checking pen1.c... [pen1.c:3]: (error) Resource leak: pen これが最小限のwindows.cfg ファイルです: <?xml version="1.0"?> <def> <resource> <alloc>CreatePen</alloc> <dealloc>DeleteObject</dealloc> </resource> </def> このアロケート関数とデアロケート関数はグループにまとめられています。それぞれのグループは<resource><memory> タグ中で定義されており、その<dealloc>関数によって特定されます。これは、<dealloc>タグでオーバーラップしたグループはマージされます。
leak-ignore とuse しばしば、割り当てられたポインタを関数に渡すことがあります。例: void test() { char *p = malloc(100); dostuff(p); } もし設定ファイルがなく、Cppcheckがdostuffの仕様を把握していなければ、Cppcheckはdostuffがメモリーについて配慮しており、メモリーリークは発生しないと仮定します。 dostuffがメモリーについて配慮せず、解放などを行なっていないことを指定するためには、leak-ignore<function> タグ中で使います。: <?xml version="1.0"?> <def> <function name="dostuff"> <leak-ignore/> <arg nr="1"/> </function> </def> これとは逆にdostuffがメモリーについて配慮している場合には次のように設定します。: <?xml version="1.0"?> <def> <memory> <dealloc>free</dealloc> <use>dostuff</use> </memory> </def> なお、この<use>の設定は論理的に全く無意味です。この設定がない場合でも同じエラーが表示されます。これは--check-libraryのinformationメッセージを減らすために使用します。
関数の動作 関数の動作や関数の使用方法を指定するのに、<function>タグが使えます。関数は、その名前によって特定されます。この名前は、name 属性とその引数によって指定されます。この名前はコンマで区切られた関数名のリストです。名前空間やクラス中の関数の場合には、完全修飾名で指定されます。例: <function name="memcpy,std::memcpy">もしテンプレート関数がある場合、インスタンス化した名前を提供してします。<function name="dostuff<int>">.
関数引き数 関数がとる引数は、<arg>タグで指定できます。引数のそれぞれは、引数の順番(1始まり)をnr属性で示します。nr="any" は任意の引き数を表します。また、nr="variadic"は可変長引数を表します。オプション引数は、デフォルト値で指定します。: default="value". それぞれの引数に対する設定は、全ての引数に対する指定を上書きします。
非ブール ここで誤った比較のあるサンプルプログラムがあります。: void test() { if (MemCmp(buffer1, buffer2, 1024==0)) {} } Cppcheckは、この関数にブール値を渡してよいと仮定します。: # cppcheck notbool.c Checking notbool.c... もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: # cppcheck --library=notbool.cfg notbool.c Checking notbool.c... [notbool.c:5]: (error) Invalid MemCmp() argument nr 3. 非ブール値が求められています。 ここで最小のnotbool.cfgを用意しました。 <?xml version="1.0"?> <def> <function name="MemCmp"> <arg nr="1"/> <arg nr="2"/> <arg nr="3"> <not-bool/> </arg> </function> </def>
未初期化メモリ ここにサンプルのプログラムがあります。: void test() { char buffer1[1024]; char buffer2[1024]; CopyMemory(buffer1, buffer2, 1024); } このプログラムのバグは buffer2 が初期化されていないことです。CopyMemory 関数の第二引数は初期化されている必要があります。しかし、Cppcheckは関数に未初期化の変数を渡してもよいと仮定しています。: # cppcheck uninit.c Checking uninit.c... もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: # cppcheck --library=windows.cfg uninit.c Checking uninit.c... [uninit.c:5]: (error) Uninitialized variable: buffer2 注意:これは、ポインタが示すメモリ領域が初期化されていなければならないことを意味しています。 これが最小限のwindows.cfgファイルです。: <?xml version="1.0"?> <def> <function name="CopyMemory"> <arg nr="1"/> <arg nr="2"> <not-uninit/> </arg> <arg nr="3"/> </function> </def>
ヌルポインタ Cppcheckは、関数にヌルポインタを渡してもよいと仮定しています。ここにサンプルのプログラムがあります。: void test() { CopyMemory(NULL, NULL, 1024); } MSDNの文書はこれが問題あるかないかを明らかにしていません。しかし、ここでは問題ありと仮定します。Cppcheck は関数にヌルポインタを渡してもよいと仮定していますので、エラーを出力しません。: # cppcheck null.c Checking null.c... もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: cppcheck --library=windows.cfg null.c Checking null.c... [null.c:3]: (error) Null pointer dereference 注意:<not-uninit>は値について意味しています。初期化されていないメモリが関数に渡されています。 これが最小限のwindows.cfg ファイルです: <?xml version="1.0"?> <def> <function name="CopyMemory"> <arg nr="1"> <not-null/> </arg> <arg nr="2"/> <arg nr="3"/> </function> </def>
フォーマット文字列 フォーマット文字列を扱う関数を定義できます。例: void test() { do_something("%i %i\n", 1024); } これについてもエラーは報告されません。: # cppcheck formatstring.c Checking formatstring.c... 引数がフォーマット文字列であることを出力する設定ファイルが作成できます。設定ファイルの例です。: <?xml version="1.0"?> <def> <function name="do_something"> <formatstr type="printf"/> <arg nr="1"> <formatstr/> </arg> </function> </def>これで、Cppcheckはエラーを報告できるようになりました。: cppcheck --library=test.cfg formatstring.c Checking formatstring.c... [formatstring.c:3]: (error) do_something format string requires 2 parameters but only 1 is given. このフォーマット文字列のtype属性は次のどちらかになります。: printf - printf のルールに従うフォーマット文字列 scanf - scanf のルールに従うフォーマット文字列
値の範囲 有効な値の範囲が定義できます。想像してください。: void test() { do_something(1024); } これについてもエラーは報告されません。: # cppcheck valuerange.c Checking valuerange.c... 1024 が 範囲外の値であることを出力する設定ファイルが作成できます。設定ファイルの例です。: <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <valid>0:1023</valid> </arg> </function> </def>これで、Cppcheckはエラーを報告できるようになりました。: cppcheck --library=test.cfg range.c Checking range.c... [range.c:3]: (error) Invalid do_something() argument nr 1. この値は1024ですが、妥当な値は0から1023までです。 validの要素で次のような表現が利用できます。: 0,3,5 => 0, 3 それに 5 だけが有効な値です。 -10:20 => -10 から 20 までの値(両端含む)が有効な値です。 :0 => 0または0未満の値が有効な値です。 0: => 0または0以上の値が有効な値です。 0,2:32 => 0 または2から32までの値(両端含む)が有効な値です。 -1.5:5.6 => -1.5 から 5.6 までの値(両端含む)が有効な値です。
最小サイズ いくつかの関数はバッファーを引数にとります。バッファの最小サイズを指定することができます。(要素数ではなくバイト数です。)想像してください。: void test() { char str[5]; do_something(str,"12345"); } これについてもエラーは報告されません。: # cppcheck minsize.c Checking minsize.c... 設定ファイルで、例えば、引数1のバッファのサイズが引数2の文字列長より大きくなればならないと警告するような設定ファイルを作成できます。例を挙げます。: <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <minsize type="strlen" arg="2"/> </arg> <arg nr="2"/> </function> </def>これで、Cppcheckはこのエラーを報告できるようになりました。: cppcheck --library=1.cfg minsize.c Checking minsize.c... [minsize.c:4]: (error) Buffer is accessed out of bounds: str minsizes はいくつかの種類があります。: strlen バッファーのサイズが、その他の引数の文字列長より大きくなければなりません。例: std.cfg のstrcpyの設定を参照してください。 argvalue バッファーのサイズがその他の引数の値より大きくなればなりません。例: std.cfg のmemsetの設定を参照してください。 sizeof バッファーのサイズがその他の引数のバッファーのサイズより大きくなればなりません。例:posix.cfgのmemccpyの設定をみてください。 mul バッファーのサイズがその他の2つの引数の値の積より大きくなればなりません。典型的な使用例としては、一つの引数が構造体などの要素のサイズを指定し、もうひとつの引数が要素の個数を定義するような場合です。例: std.cfg のfreadの設定を参照してください
strz これを指定すると、数が0終端文字列でなければならないということができます。 <?xml version="1.0"?> <def> <function name="do_something"> <arg nr="1"> <strz/> </arg> </function> </def>
noreturn Cppcheck はこの関数がいつも値を返すとは仮定していません。ここにサンプルのプログラムがあります。: void test(int x) { int data, buffer[1024]; if (x == 1) data = 123; else ZeroMemory(buffer, sizeof(buffer)); buffer[0] = data; // <- error: xが1でないとき初期化されていない } 理屈の上では、ZeroMemoryがプログラムを終了させてもバグはありません。そのため Cppcheckはエラーを報告しません。: # cppcheck noreturn.c Checking noreturn.c... しかし、--check-library--enable=informationをつかうとエラーが出力されます。: # cppcheck --check-library --enable=information noreturn.c Checking noreturn.c... [noreturn.c:7]: (information) --check-library: Function ZeroMemory() should have <noreturn> configuration もし適切な windows.cfg が提供されていましたら、このバグは検出されます。: # cppcheck --library=windows.cfg noreturn.c Checking noreturn.c... [noreturn.c:8]: (error) Uninitialized variable: data これが最小限のwindows.cfg ファイルです: <?xml version="1.0"?> <def> <function name="ZeroMemory"> <noreturn>false</noreturn> <arg nr="1"/> <arg nr="2"/> </function> </def>
use-retval 他になにも指定されていない限り、cppcheckは関数が返り値を無視していても問題ないと仮定します。: bool test(const char* a, const char* b) { strcmp(a, b); // <- bug: strcmp の呼び出しは副作用を持ちませんが返り値を無視している。 return true; } strcmp が副作用を持つ場合、パラメータが関数に渡されている結果を無視しても問題はなく、このような仮定は正しいといえます。: # cppcheck useretval.c Checking useretval.c... もし適切なlib.cfg が提供されていましたら、このバグは検出されます。: # cppcheck --library=lib.cfg --enable=warning useretval.c Checking useretval.c... [useretval.c:3]: (warning) Return value of function strcmp() is not used. これが最小限のlib.cfg ファイルです。: <?xml version="1.0"?> <def> <function name="strcmp"> <use-retval/> <arg nr="1"/> <arg nr="2"/> </function> </def>
pure関数(pure)とconst関数 これらは、GCC関数属性のpureとconstに対応します。 pure関数は、値を返す以外の効果を持ちません。そしてその返り値はその関数の引数とグローバル変数によってのみ決まります。 const関数は、値を返す以外の効果を持ちません。そしてその返り値はその関数の引数によってのみ決まります。 ここにサンプルのプログラムがあります。: void f(int x) { if (calculate(x) == 213) { } else if (calculate(x) == 213) { // 到達不能コード } } もしcalculate() がconst関数であれば、calculate(x)は両方の条件で同じ値を返します。というのも、同じパラメータを引数にしているからです。 Cppcheck は通常、その結果が異なると仮定するため、Cppcheckはこのコード例に警告を出しません。: # cppcheck const.c Checking const.c... もし適切なconst.cfg が提供されていましたら、このバグは検出されます。: # cppcheck --enable=style --library=const const.c Checking const.c... [const.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5. これが最小限のconst.cfg ファイルです。: <?xml version="1.0"?> <def> <function name="calculate"> <const/> <arg nr="1"/> </function> </def>
関数strcpyの設定例 標準関数のstrcpyのための適切な設定は次のようになる。: <function name="strcpy"> <leak-ignore/> <noreturn>false</noreturn> <arg nr="1"> <not-null/> </arg> <arg nr="2"> <not-null/> <not-uninit/> <strz/> </arg> </function> この<leak-ignore/> は、リークチェック中に関数呼び出しを無視するように、Cppcheckに伝えます。この関数は、割り当てられたメモリを解放しないことを意味しています。 この<noreturn> は、この関数が、返り値を返すかどうかをCppchecに伝えます。 この関数は第一引数にポインタを取ります。しかしこのポインタは、ヌルポインタであってはなりません。というのは<not-null>が使用されているからです。 この関数は第二引数にポインタを取ります。このポインタはヌルポインタであってはなりません。また、このポインタは初期化されたデータを指していなければなりません。<not-null><not-uninit> は正しく使用されています。さらにいえば、このポインタは0終端文字列(zero-terminated string)でなければなりません。そのため<strz>が使用されています。
define ライブラリはマクロプリプロセッサのdefineを使用することができます。例: <?xml version="1.0"?> <def> <define name="NULL_VALUE" value="0"/> </def> プリプロセッサの段階でソースコード中に "NULL_VALUE" が現れるごとに、"0"で置き換えます。
podtype 多くのソースコードで、プラットフォームに依存しない型をtypedefによって定義しています。"podtype"のタグによって、cppcheckがこれらをサポートするために必要な情報を提供できます。このような情報のない場合、cppcheckは次の例でみるような "uint16_t" 型を理解できません。 void test() { uint16_t a; } そのため、未使用変数である、'a'が未使用であるとのメッセージが表示されません。 # cppcheck --enable=style unusedvar.cpp Checking unusedvar.cpp... もし uint16_t が以下のように定義されていた場合、結果にメッセージが反映されます。 <?xml version="1.0"?> <def> <podtype name="uint16_t" sign="u" size="2"/> </def> 型のサイズはバイトサイズで指定します。符号の "sign" 属性は 符号ありの "s" か 符号無し "u" のどちらかです。これらの属性はオプションです。このライブラリを使用しますと、cppcheckはメッセージを表示できるようになります。 # cppcheck --library=lib.cfg --enable=style unusedvar.cpp Checking unusedvar.cpp... [unusedvar.cpp:2]: (style) Unused variable: a
コンテナ(container) C++ ライブラリの多くや STL 自身は、非常によく似た機能性をもつコンテナを提供します。ライブラリによってその動作をcppcheckに伝えられます。それぞれのコンテナの設定にはユニークなIDが必要とします。コンテナの設定には、startPatternを加えることができます(オプション)。この startPatternはToken::Match パターンとendPattern に有効でなけばなりません。また、このendPatternはリンクしているトークンと比較されるものです。オブション属性の"inherits"は事前に定義されたコンテナのIDをとります。 <container>タグの内部で、<size>、<access>、<other>を選択して使用して関数を定義できます。これらのタグはそれぞれ、"resize" やその結果を与えるような動作を指定することができます。その例 "end-iterator"を示します。 次の例は、std::vectorの為の定義を示しています。std::vectorは"stdContainer"の定義に基づいていますが、ここには表示していません。: <?xml version="1.0"?> <def> <container id="stdVector" startPattern="std :: vector &lt;" inherits="stdContainer"> <size> <function name="push_back" action="push"/> <function name="pop_back" action="pop"/> </size> <access indexOperator="array-like"> <function name="at" yields="at_index"/> <function name="front" yields="item"/> <function name="back" yields="item"/> </access> </container> </def>
ルール(Rules) 正規表現を使用して、ユーザーがルール(rule)を定義することができます。 これらのカスタムルールは、ソースコードを高度に分析した結果を使用することができません。しかしソースコード中の非常にシンプルなパターンについて簡単にルールを作成することができます。 ルールの作成を始めるには次の関連記事を参照してください。: http://sourceforge.net/projects/cppcheck/files/Articles/ ルールのファイルフォーマットは次のとおりです。: <?xml version="1.0"?> <rule> <tokenlist>LIST</tokenlist> <pattern>PATTERN</pattern> <message> <id>ID</id> <severity>SEVERITY</severity> <summary>SUMMARY</summary> </message> </rule> patternタグ中にCDATAを含めた場合、XMLに干渉する可能性がありますので使用時はご注意ください。: <![CDATA[some<strange>pattern]]>
<tokenlist> この<tokenlist> 要素はオプションです。この要素がある場合、どのトークンをチェックするかを指示することができます。このLISTdefine, raw, normal , simpleのいずれかです。 define #define プリプロセッサの記述をチェックするために使用します。 raw プリプロセッサの出力をチェックするために使用します。 normal normal のトークンリストをチェックするために使用します。ソースコードをある程度、単純化した結果をチェックすることになります。 simple 単純なトークンリストをチェックするために使用します。ソースコードを完全に単純化した結果をチェックすることになります。ほとんどの Cppcheckのチェックには、この 単純ばトークンリストを使用します。 もし<tokenlist>要素を省略した場合、simple が使用されます。
<pattern> このPATTERN にはPerlの正規表現と互換性のある正規表現 PCREを指定します。
<id> この ID にはユーザーが定義した message idを指定します。
<severity> このSEVERITYにはCppcheck の厳格度(severities)である、次のいずれかを指定します。: information, performance, portability, style, warning,error
<summary> オプションです。メッセージのサマリーです。もしこのsummaryトークンが指定されていなければ、マッチしたトークンが出力されます。
Cppcheck アドオン Cppcheckのアドオンは、個別のスクリプトや個別のプログラムとして実装されています。Cppcheckのアドオンを使用すると次のような利点があります。 洗練された分析の結果を使用した個別の、外部チェックを追加できます。 ソースコードが可視化できます。 その他
Cppcheckアドオンの使用方法 現在、アドオンを使用するには2段階の操作が必要です。: Cppcheckを実行し、ダンプファイルを生成します。 アドオンでダンプファイルを処理します。 --dumpフラグを使用するとダンプファイルを生成できます。foo/ フォルダ以下の全てのソースファイルからダンプファイルを生成するには次のようにします。 cppcheck --dump foo/ foo/ フォルダ以下の全てのダンプファイルをアドオンで処理するには次のようにします。 python addon.py foo/*.dump
Cppcheckアドオンの見つけ方 ダウンロードできる、アドオンがいくつかあります。 Cppcheck プロジェクトはいくつかのアドオンを以下の場所で提供しています。: http://github.com/danmar/cppcheck/blob/master/addons ublinterは規格で定義されていない未定義動作に注力した"lint"です。: http://github.com/danmar/ublinter あなたのアドオンの情報をご紹介ください。(商用、フリーを問いません。)
Cppcheck アドオンの作成 Cppcheck は XML形式でダンプファイルを生成できます。このファイルには以下のようなものが含まれています。: トークンリスト(Token list) シンタックスツリー(Syntax trees) シンボルデータベース(関数、クラス、変数、スコープ) 既知の値(value flow analysis) Cppcheckはアドオンを直接実行することはできません。直接実行するためにインターフェースはありません。これは、次のような制限がないことを意味します。: アドオンを作成しリリースする際に、どのようなライセンスでも適用できます。 アドオンの作成に、どのようなスクリプト言語やプログラミング言語で作成できます。 アドオン作成者がユーザーインターフェースと出力を決定できます。 警告の生成以外の目的にもアドオン使用できます。 アドオン作成者の利便性のために、Cppcheck プロジェクトは PythonからCppcheckのデータにアクセスするための cppcheckdata.pyを提供しています。cppcheckdata.pyの使用はオプションです。
使用例1 - 全トークンの表示 Script: import sys import cppcheckdata def printtokens(data): for token in data.tokenlist: print(token.str) for arg in sys.argv[1:]: printtokens(cppcheckdata.parse(arg))
使用例2 - 全関数リストアップ Script: import sys import cppcheckdata def printfunctions(data): for scope in data.scopes: if scope.type == 'Function': print(scope.className) for arg in sys.argv[1:]: printfunctions(cppcheckdata.parse(arg))
使用例 3 - 全クラスリストアップ Script: import sys import cppcheckdata def printclasses(data): for scope in data.scopes: if scope.type == 'Class': print(scope.className) for arg in sys.argv[1:]: printfunctions(cppcheckdata.parse(arg))
HTML 形式での報告 cppcheckのXML出力をHTML形式に変更できます。これを利用するには、Python と pygments module (http://pygments.org/) が必要です。Cppcheckのソースツリーにhtmlreportというフォルダがあります。このフォルダには、CppcheckのXMLファイルをHTML出力に変換するスクリプトがあります。 このコマンドでヘルプ画面を生成するには次のように実行します。 htmlreport/cppcheck-htmlreport -h 出力画面には次の内容が表示されます。: Usage: cppcheck-htmlreport [options] Options: -h, --help show this help message and exit --file=FILE The cppcheck xml output file to read defects from. Default is reading from stdin. --report-dir=REPORT_DIR The directory where the html report content is written. --source-dir=SOURCE_DIR Base directory where source code files can be found. 使用例: ./cppcheck gui/test.cpp --xml 2> err.xml htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. グラフィカルインターフェースGUI
イントロダクション Cppcheck GUIが利用できます。 メイン画面は、このソフトを起動時に表示されます。
ソースコードのチェック Checkメニューを使用します。
結果の確認 結果はリスト表示されます。 View メニューを操作して、メッセージの種類毎に表示/非表示を切り替えできます。 結果をXML ファイルに保存して、後で確認できます。Save results to fileOpen XMLを参照してください。
設定 Languageメニューからいつでも使用言語を変更できます。 設定は、 Edit Preferences で変更できます。
プロジェクトファイル プロジェクトファイルは、プロジェクト固有の設定を保存するのに使用します。固有の設定には次のものがあります。: インクルードパス プリプロセッサのdefine このマニュアルの3 章にあるように、全てのコンパイルスイッチの組み合わせをチェックします。コンパイルスイッチの組み合わせを制限したい場合にだけ、プリプロセッサのdefineを指定してください。
cppcheck-1.90/man/manual.md000066400000000000000000000551431357737443600156300ustar00rootroot00000000000000--- title: Cppcheck manual subtitle: Version 1.90 dev author: Cppcheck team lang: en documentclass: report --- # Introduction Cppcheck is an analysis tool for C/C++ code. It provides unique code analysis to detect bugs and focuses on detecting undefined behaviour and dangerous coding constructs. The goal is to detect only real errors in the code (i.e. have very few false positives). Supported code and platforms: - You can check non-standard code that contains various compiler extensions, inline assembly code, etc. - Cppcheck should be compilable by any C++ compiler that handles the latest C++ standard. - Cppcheck should work on any platform that has sufficient CPU and memory. Please understand that there are limits of Cppcheck. Cppcheck is rarely wrong about reported errors. But there are many bugs that it doesn't detect. You will find more bugs in your software by testing your software carefully, than by using Cppcheck. You will find more bugs in your software by instrumenting your software, than by using Cppcheck. But Cppcheck can still detect some of the bugs that you miss when testing and instrumenting your software. # Getting started ## GUI It is not required but creating a new project file is a good first step. There are a few options you can tweak to get good results. In the project settings dialog, the first option you see is "Import project". It is recommended that you use this feature if you can. Cppcheck can import: - Visual studio solution / project - Compile database (can be generated from cmake/qbs/etc build files) - Borland C++ Builder 6 When you have filled out the project settings and click on OK; the Cppcheck analysis will start. ## Command line ### First test Here is a simple code int main() { char a[10]; a[10] = 0; return 0; } If you save that into file1.c and execute: cppcheck file1.c The output from cppcheck will then be: Checking file1.c... [file1.c:4]: (error) Array 'a[10]' index 10 out of bounds ### Checking all files in a folder Normally a program has many source files. And you want to check them all. Cppcheck can check all source files in a directory: cppcheck path If "path" is a folder then cppcheck will recursively check all source files in this folder. Checking path/file1.cpp... 1/2 files checked 50% done Checking path/file2.cpp... 2/2 files checked 100% done ### Check files manually or use project file With Cppcheck you can check files manually, by specifying files/paths to check and settings. Or you can use a project file (cmake/visual studio/etc). We don't know which approach (project file or manual configuration) will give you the best results. It is recommended that you try both. It is possible that you will get different results so that to find most bugs you need to use both approaches. Later chapters will describe this in more detail. ### Excluding a file or folder from checking To exclude a file or folder, there are two options. The first option is to only provide the paths and files you want to check. cppcheck src/a src/b All files under src/a and src/b are then checked. The second option is to use -i, with it you specify files/paths to ignore. With this command no files in src/c are checked: cppcheck -isrc/c src This option is only valid when supplying an input directory. To ignore multiple directories supply the -i multiple times. The following command ignores both the src/b and src/c directories. cppcheck -isrc/b -isrc/c ## Severities The possible severities for messages are: **error** used when bugs are found **warning** suggestions about defensive programming to prevent bugs **style** stylistic issues related to code cleanup (unused functions, redundant code, constness, and such) **performance** Suggestions for making the code faster. These suggestions are only based on common knowledge. It is not certain you'll get any measurable difference in speed by fixing these messages. **portability** portability warnings. 64-bit portability. code might work different on different compilers. etc. **information** Configuration problems. The recommendation is to only enable these during configuration. # Importing project You can import some project files and build configurations into Cppcheck. ## Cppcheck GUI project You can import and use Cppcheck GUI project files in the command line tool: cppcheck --project=foobar.cppcheck The Cppcheck GUI has a few options that are not available in the command line directly. To use these options you can import a GUI project file. We want to keep the command line tool usage simple and limit the options by intention. To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. cppcheck --project=foobar.cppcheck -ifoo ## CMake Generate a compile database: cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . The file `compile_commands.json` is created in the current folder. Now run Cppcheck like this: cppcheck --project=compile_commands.json To ignore certain folders you can use `-i`. This will skip analysis of source files in the `foo` folder. cppcheck --project=compile_commands.json -ifoo ## Visual Studio You can run Cppcheck on individual project files (\*.vcxproj) or on a whole solution (\*.sln) Running Cppcheck on an entire Visual Studio solution: cppcheck --project=foobar.sln Running Cppcheck on a Visual Studio project: cppcheck --project=foobar.vcxproj In the `Cppcheck GUI` you have the choice to only analyze a single debug configuration. If you want to use this choice on the command line then create a `Cppcheck GUI` project with this activated and then import the GUI project file on the command line. To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. cppcheck --project=foobar.vcxproj -ifoo ## C++ Builder 6 Running Cppcheck on a C++ Builder 6 project: cppcheck --project=foobar.bpr To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. cppcheck --project=foobar.bpr -ifoo ## Other If you can generate a compile database then it's possible to import that in Cppcheck. In Linux you can use for instance the `bear` (build ear) utility to generate a compile database from arbitrary build tools: bear make # Platform You should use a platform configuration that match your target. By default Cppcheck uses native platform configuration that works well if your code is compiled and executed locally. Cppcheck has builtin configurations for Unix and Windows targets. You can easily use these with the `--platform` command line flag. You can also create your own custom platform configuration in a XML file. Here is an example: 8 signed 2 4 4 8 4 8 12 4 4 2 # Preprocessor Settings If you use `--project` then Cppcheck will use the preprocessor settings from the imported project. Otherwise you'll probably want to configure the include paths, defines, etc. ## Defines Here is a file that has 2 preprocessor configurations (with A defined and without A defined): #ifdef A x = y; #else x = z; #endif By default Cppcheck will check all preprocessor configurations (except those that have #error in them). So the above code will by default be analyzed both with `A` defined and without `A` defined. You can use `-D` to change this. When you use `-D`, cppcheck will by default only check the given configuration and nothing else. This is how compilers work. But you can use `--force` or `--max-configs` to override the number of configurations. Check all configurations: cppcheck file.c Only check the configuration A: cppcheck -DA file.c Check all configurations when macro A is defined cppcheck -DA --force file.c Another useful flag might be `-U`. It tells Cppcheck that a macro is not defined. Example usage: cppcheck -UX file.c That will mean that X is not defined. Cppcheck will not check what happens when X is defined. ## Include paths To add an include path, use `-I`, followed by the path. Cppcheck's preprocessor basically handles includes like any other preprocessor. However, while other preprocessors stop working when they encounter a missing header, cppcheck will just print an information message and continues parsing the code. The purpose of this behaviour is that cppcheck is meant to work without necessarily seeing the entire code. Actually, it is recommended to not give all include paths. While it is useful for cppcheck to see the declaration of a class when checking the implementation of its members, passing standard library headers is highly discouraged because it will result in worse results and longer checking time. For such cases, .cfg files (see below) are the better way to provide information about the implementation of functions and types to cppcheck. # Suppressions If you want to filter out certain errors you can suppress these. Please note that if you see a false positive then we (the Cppcheck team) want that you report it so we can fix it. ## Plain text suppressions You can suppress certain types of errors. The format for such a suppression is one of: [error id]:[filename]:[line] [error id]:[filename2] [error id] The `error id` is the id that you want to suppress. The easiest way to get it is to use the --template=gcc command line flag. The id is shown in brackets. The filename may include the wildcard characters \* or ?, which match any sequence of characters or any single character respectively. It is recommended that you use "/" as path separator on all operating systems. The filename must match the filename in the reported warning exactly. For instance, if the warning contains a relative path then the suppression must match that relative path. ## Command line suppression The `--suppress=` command line option is used to specify suppressions on the command line. Example: cppcheck --suppress=memleak:src/file1.cpp src/ ## Suppressions in a file You can create a suppressions file. Example: // suppress memleak and exceptNew errors in the file src/file1.cpp memleak:src/file1.cpp exceptNew:src/file1.cpp // suppress all uninitvar errors in all files uninitvar Note that you may add empty lines and comments in the suppressions file. You can use the suppressions file like this: cppcheck --suppressions-list=suppressions.txt src/ ## XML suppressions You can specify suppressions in a XML file. Example file: uninitvar src/file1.c 10 var The XML format is extensible and may be extended with further attributes in the future. You can use the suppressions file like this: cppcheck --suppress-xml=suppressions.xml src/ ## Inline suppressions Suppressions can also be added directly in the code by adding comments that contain special keywords. Before adding such comments, consider that the code readability is sacrificed a little. This code will normally generate an error message: void f() { char arr[5]; arr[10] = 0; } The output is: cppcheck test.c [test.c:3]: (error) Array 'arr[5]' index 10 out of bounds To suppress the error message, a comment can be added: void f() { char arr[5]; // cppcheck-suppress arrayIndexOutOfBounds arr[10] = 0; } Now the `--inline-suppr` flag can be used to suppress the warning. No error is reported when invoking cppcheck this way: cppcheck --inline-suppr test.c You can specify that the inline suppression only applies to a specific symbol: // cppcheck-suppress arrayIndexOutOfBounds symbolName=arr You can write comments for the suppress, however is recommended to use ; or // to specify where they start: // cppcheck-suppress arrayIndexOutOfBounds ; some comment // cppcheck-suppress arrayIndexOutOfBounds // some comment # XML output Cppcheck can generate output in XML format. Use `--xml` to enable this format. A sample command to check a file and output errors in the XML format: cppcheck --xml file1.cpp Here is a sample report: ## The `` element Each error is reported in a `` element. Attributes: **id** id of error. These are always valid symbolnames. **severity** error/warning/style/performance/portability/information **msg** the error message in short format **verbose** the error message in long format **inconclusive** this attribute is only used when the error message is inconclusive **cwe** CWE ID for the problem. This attribute is only used when the CWE ID for the message is known. ## The `` element All locations related to an error are listed with `` elements. The primary location is listed first. Attributes: **file** filename. both relative and absolute paths are possible. **file0** name of the source file (optional) **line** line number **info** short information for each location (optional) # Reformatting the text output If you want to reformat the output so it looks different you can use templates. ## Predefined output formats To get Visual Studio compatible output you can use --template=vs: cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c This output will look like this: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds. To get gcc compatible output you can use --template=gcc: cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c The output will look like this: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] a[2] = 0; ^ ## User defined output format (single line) You can write your own pattern. For instance, to get warning messages that are formatted like old gcc such format can be used: cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c The output will look like this: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. A comma separated format: cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c The output will look like this: Checking samples/arrayIndexOutOfBounds/bad.c ... samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds. ## User defined output format (multi line) Many warnings have multiple locations. Example code: void f(int *p) { *p = 3; // line 3 } int main() { int *p = 0; // line 8 f(p); // line 9 return 0; } There is a possible null pointer dereference at line 3. Cppcheck can show how it came to that conclusion by showing extra location information. You need to use both --template and --template-location at the command line. Example command: cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c The output from Cppcheck is: Checking multiline.c ... multiline.c:3: warning: Possible null pointer dereference: p *p = 3; ^ multiline.c:8: note: Assignment 'p=0', assigned value is 0 int *p = 0; ^ multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0 f(p); ^ multiline.c:3: note: Null pointer dereference *p = 3; ^ The first line in the warning is formatted by the --template format. The other lines in the warning are formatted by the --template-location format. ### Format specifiers for --template The available specifiers for --template are: **{file}** File name **{line}** Line number **{column}** Column number **{callstack}** Write all locations. Each location is written in [{file}:{line}] format and the locations are separated by ->. For instance it might look like: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3] **{inconclusive:text}** If warning is inconclusive then the given text is written. The given text can be any arbitrary text that does not contain }. Example: {inconclusive:inconclusive,} **{severity}** error/warning/style/performance/portability/information **{message}** The warning message **{id}** Warning id **{code}** The real code. **\\t** Tab **\\n** Newline **\\r** Carriage return ### Format specifiers for --template-location The available specifiers for `--template-location` are: **{file}** File name **{line}** Line number **{column}** Column number **{info}** Information message about current location **{code}** The real code. **\\t** Tab **\\n** Newline **\\r** Carriage return # Addons Addons are scripts that analyses Cppcheck dump files to check compatibility with secure coding standards and to locate various issues. Cppcheck is distributed with a few addons which are listed below. ## Supported addons ### cert.py [cert.py](https://github.com/danmar/cppcheck/blob/master/addons/cert.py) checks for compliance with the safe programming standard [SEI CERT](http://www.cert.org/secure-coding/). ### misra.py [misra.py](https://github.com/danmar/cppcheck/blob/master/addons/misra.py) is used to verify compliance with MISRA C 2012 - a proprietary set of guidelines to avoid such questionable code, developed for embedded systems. Since this standard is proprietary, cppcheck does not display error text by specifying only the number of violated rules (for example, [c2012-21.3]). If you want to display full texts for violated rules, you will need to create a text file containing MISRA rules, which you will have to pass when calling the script with `--rule-texts` key. Some examples of rule texts files available in [tests directory](https://github.com/danmar/cppcheck/blob/master/addons/test/misra/). You can also suppress some unwanted rules using `--suppress-rules` option. Suppressed rules should be set as comma-separated listed, for example: `--suppress-rules 21.1,18.7`. The full list of supported rules is available on [Cppcheck](http://cppcheck.sourceforge.net/misra.php) home page. ### y2038.py [y2038.py](https://github.com/danmar/cppcheck/blob/master/addons/y2038.py) checks Linux system for [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) safety. This required [modified environment](https://github.com/3adev/y2038). See complete description [here](https://github.com/danmar/cppcheck/blob/master/addons/doc/y2038.txt). ### threadsafety.py [threadsafety.py](https://github.com/danmar/cppcheck/blob/master/addons/threadsafety.py) analyse Cppcheck dump files to locate thread safety issues like static local objects used by multiple threads. ## Running Addons Addons could be run through Cppcheck command line utility as follows: cppcheck --addon=misra.py somefile.c This will launch all Cppcheck checks and additionally calls specific checks provided by selected addon. Some addons need extra arguments. You can configure how you want to execute an addon in a json file. For example put this in misra.json: { "script": "misra.py", "args": [ "--rule-texts=misra.txt", "--suppress-rules 17.3,21.12" ] } And then the configuration can be executed on the cppcheck command line: cppcheck --addon=misra.json somefile.c By default Cppcheck would search addon at standard path which was specified in installation process. You also can set this path directly, for example: cppcheck --addon=/opt/cppcheck/configurations/my_misra.json somefile.c This allows you create and manage multiple configuration files for different projects. # Library configuration When external libraries are used, such as WinAPI, POSIX, gtk, Qt, etc, Cppcheck doesn't know how the external functions behave. Cppcheck then fails to detect various problems such as leaks, buffer overflows, possible null pointer dereferences, etc. But this can be fixed with configuration files. Cppcheck already contains configurations for several libraries. They can be loaded as described below. Note that the configuration for the standard libraries of C and C++, std.cfg, is always loaded by cppcheck. If you create or update a configuration file for a popular library, we would appreciate if you upload it to us. ## Using your own custom .cfg file You can create and use your own .cfg files for your projects. Use `--check-library` and `--enable=information` to get hints about what you should configure. You can use the `Library Editor` in the `Cppcheck GUI` to edit configuration files. It is available in the `View` menu. The .cfg file format is documented in the `Reference: Cppcheck .cfg format` (http://cppcheck.sf.net/reference-cfg-format.pdf) document. # HTML Report You can convert the XML output from cppcheck into a HTML report. You'll need Python and the pygments module ( for this to work. In the Cppcheck source tree there is a folder htmlreport that contains a script that transforms a Cppcheck XML file into HTML output. This command generates the help screen: htmlreport/cppcheck-htmlreport -h The output screen says: Usage: cppcheck-htmlreport [options] Options: -h, --help show this help message and exit --file=FILE The cppcheck xml output file to read defects from. Default is reading from stdin. --report-dir=REPORT_DIR The directory where the html report content is written. --source-dir=SOURCE_DIR Base directory where source code files can be found. An example usage: ./cppcheck gui/test.cpp --xml 2> err.xml htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. cppcheck-1.90/man/reference-cfg-format.md000066400000000000000000000445411357737443600203340ustar00rootroot00000000000000--- title: Cppcheck .cfg format subtitle: Version 1.90 dev author: Cppcheck team lang: en documentclass: report --- # Introduction This is a reference for the .cfg file format that Cppcheck uses. # Memory and resource leaks Cppcheck has configurable checking for leaks, e.g. you can specify which functions allocate and free memory or resources and which functions do not affect the allocation at all. ## ``, `` and `` Here is an example program: void test() { HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); } The code example above has a resource leak - CreatePen() is a WinAPI function that creates a pen. However, Cppcheck doesn't assume that return values from functions must be freed. There is no error message: $ cppcheck pen1.c Checking pen1.c... If you provide a configuration file then Cppcheck detects the bug: $ cppcheck --library=windows.cfg pen1.c Checking pen1.c... [pen1.c:3]: (error) Resource leak: pen Here is a minimal windows.cfg file: CreatePen DeleteObject Functions that reallocate memory can be configured using a `` tag. The input argument which points to the memory that shall be reallocated can also be configured (the default is the first argument). As an example, here is a configuration file for the fopen, freopen and fclose functions from the c standard library: fopen freopen fclose The allocation and deallocation functions are organized in groups. Each group is defined in a `` or `` tag and is identified by its `` functions. This means, groups with overlapping `` tags are merged. ## `` and `` Often the allocated pointer is passed to functions. Example: void test() { char *p = malloc(100); dostuff(p); } If Cppcheck doesn't know what `dostuff` does, without configuration it will assume that `dostuff` takes care of the memory so there is no memory leak. To specify that `dostuff` doesn't take care of the memory in any way, use `` in the `` tag (see next section): If instead `dostuff` takes care of the memory then this can be configured with: free dostuff The `` configuration has no logical purpose. You will get the same warnings without it. Use it to silence --check-library information messages. # Function behavior To specify the behaviour of functions and how they should be used, `` tags can be used. Functions are identified by their name, specified in the name attribute and their number of arguments. The name is a comma-separated list of function names. For functions in namespaces or classes, just provide their fully qualified name. For example: ``. If you have template functions then provide their instantiated names ``. ## Function arguments The arguments a function takes can be specified by `` tags. Each of them takes the number of the argument (starting from 1) in the nr attribute, `nr="any"` for arbitrary arguments, or `nr="variadic"` for variadic arguments. Optional arguments can be specified by providing a default value: `default="value"`. The specifications for individual arguments override this setting. You can specify if an argument is an input or output argument. For example ``. The allowed directions are `in`, `out` and `inout`. ### Not bool Here is an example program with misplaced comparison: void test() { if (MemCmp(buffer1, buffer2, 1024==0)) {} } Cppcheck assumes that it is fine to pass boolean values to functions: $ cppcheck notbool.c Checking notbool.c... If you provide a configuration file then Cppcheck detects the bug: $ cppcheck --library=notbool.cfg notbool.c Checking notbool.c... [notbool.c:5]: (error) Invalid MemCmp() argument nr 3. A non-boolean value is required. Here is the minimal notbool.cfg ### Uninitialized memory Here is an example program: void test() { char buffer1[1024]; char buffer2[1024]; CopyMemory(buffer1, buffer2, 1024); } The bug here is that buffer2 is uninitialized. The second argument for CopyMemory needs to be initialized. However, Cppcheck assumes that it is fine to pass uninitialized variables to functions: $ cppcheck uninit.c Checking uninit.c... If you provide a configuration file then Cppcheck detects the bug: $ cppcheck --library=windows.cfg uninit.c Checking uninit.c... [uninit.c:5]: (error) Uninitialized variable: buffer2 Note that this implies for pointers that the memory they point at has to be initialized, too. Here is the minimal windows.cfg: The `indirect` attribute can be set to control the indirection of uninitialized memory allowed. Setting `indirect` to `0` means no uninitialized memory is allowed. Setting it to `1` allows a pointer to uninitialized memory. Setting it to `2` allows a pointer to pointer to uninitialized memory. By default, cppcheck will use an indirect value of `0` unless `not-null` is used. When `not-null` is used, then `indirect` will default to `1`. ### Null pointers Cppcheck assumes it's ok to pass NULL pointers to functions. Here is an example program: void test() { CopyMemory(NULL, NULL, 1024); } The MSDN documentation is not clear if that is ok or not. But let's assume it's bad. Cppcheck assumes that it's ok to pass NULL to functions so no error is reported: $ cppcheck null.c Checking null.c... If you provide a configuration file then Cppcheck detects the bug: $ cppcheck --library=windows.cfg null.c Checking null.c... [null.c:3]: (error) Null pointer dereference Note that this implies `` as far as values are concerned. Uninitialized memory might still be passed to the function. Here is a minimal windows.cfg file: ### Format string You can define that a function takes a format string. Example: void test() { do_something("%i %i\n", 1024); } No error is reported for that: $ cppcheck formatstring.c Checking formatstring.c... A configuration file can be created that says that the string is a format string. For instance: Now Cppcheck will report an error: $ cppcheck --library=test.cfg formatstring.c Checking formatstring.c... [formatstring.c:3]: (error) do_something format string requires 2 parameters but only 1 is given. The type attribute can be either: printf - format string follows the printf rules scanf - format string follows the scanf rules ### Value range The valid values can be defined. Imagine: void test() { do_something(1024); } No error is reported for that: $ cppcheck valuerange.c Checking valuerange.c... A configuration file can be created that says that 1024 is out of bounds. For instance: 0:1023 Now Cppcheck will report an error: $ cppcheck --library=test.cfg range.c Checking range.c... [range.c:3]: (error) Invalid do_something() argument nr 1. The value is 1024 but the valid values are '0-1023'. Some example expressions you can use in the valid element: 0,3,5 => only values 0, 3 and 5 are valid -10:20 => all values between -10 and 20 are valid :0 => all values that are less or equal to 0 are valid 0: => all values that are greater or equal to 0 are valid 0,2:32 => the value 0 and all values between 2 and 32 are valid -1.5:5.6 => all values between -1.5 and 5.6 are valid ### `` Some function arguments take a buffer. With minsize you can configure the min size of the buffer (in bytes, not elements). Imagine: void test() { char str[5]; do_something(str,"12345"); } No error is reported for that: $ cppcheck minsize.c Checking minsize.c... A configuration file can for instance be created that says that the size of the buffer in argument 1 must be larger than the strlen of argument 2. For instance: Now Cppcheck will report this error: $ cppcheck --library=1.cfg minsize.c Checking minsize.c... [minsize.c:4]: (error) Buffer is accessed out of bounds: str There are different types of minsizes: strlen buffer size must be larger than other arguments string length. Example: see strcpy configuration in std.cfg argvalue buffer size must be larger than value in other argument. Example: see memset configuration in std.cfg sizeof buffer size must be larger than other argument buffer size. Example: see memcpy configuration in posix.cfg mul buffer size must be larger than multiplication result when multiplying values given in two other arguments. Typically one argument defines the element size and another element defines the number of elements. Example: see fread configuration in std.cfg strz With this you can say that an argument must be a zero-terminated string. ### `` Cppcheck doesn't assume that functions always return. Here is an example code: void test(int x) { int data, buffer[1024]; if (x == 1) data = 123; else ZeroMemory(buffer, sizeof(buffer)); buffer[0] = data; // <- error: data is uninitialized if x is not 1 } In theory, if ZeroMemory terminates the program then there is no bug. Cppcheck therefore reports no error: $ cppcheck noreturn.c Checking noreturn.c... However if you use `--check-library` and `--enable=information` you'll get this: $ cppcheck --check-library --enable=information noreturn.c Checking noreturn.c... [noreturn.c:7]: (information) --check-library: Function ZeroMemory() should have configuration If a proper windows.cfg is provided, the bug is detected: $ cppcheck --library=windows.cfg noreturn.c Checking noreturn.c... [noreturn.c:8]: (error) Uninitialized variable: data Here is a minimal windows.cfg file: false ### `` As long as nothing else is specified, cppcheck assumes that ignoring the return value of a function is ok: bool test(const char* a, const char* b) { strcmp(a, b); // <- bug: The call of strcmp does not have side-effects, but the return value is ignored. return true; } In case strcmp has side effects, such as assigning the result to one of the parameters passed to it, nothing bad would happen: $ cppcheck useretval.c Checking useretval.c... If a proper lib.cfg is provided, the bug is detected: $ cppcheck --library=lib.cfg --enable=warning useretval.c Checking useretval.c... [useretval.c:3]: (warning) Return value of function strcmp() is not used. Here is a minimal lib.cfg file: ### `` and `` These correspond to the GCC function attributes `` and ``. A pure function has no effects except to return a value, and its return value depends only on the parameters and global variables. A const function has no effects except to return a value, and its return value depends only on the parameters. Here is an example code: void f(int x) { if (calculate(x) == 213) { } else if (calculate(x) == 213) { // unreachable code } } If calculate() is a const function then the result of calculate(x) will be the same in both conditions, since the same parameter value is used. Cppcheck normally assumes that the result might be different, and reports no warning for the code: $ cppcheck const.c Checking const.c... If a proper const.cfg is provided, the unreachable code is detected: $ cppcheck --enable=style --library=const const.c Checking const.c... [const.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5. Here is a minimal const.cfg file: ### Example configuration for strcpy() The proper configuration for the standard strcpy() function would be: false The `` tells Cppcheck to ignore this function call in the leaks checking. Passing allocated memory to this function won't mean it will be deallocated. The `` tells Cppcheck if this function returns or not. The first argument that the function takes is a pointer. It must not be a null pointer, therefore `` is used. The second argument the function takes is a pointer. It must not be null. And it must point at initialized data. Using `` and `` is correct. Moreover it must point at a zero-terminated string so `` is also used. # ``; check or suppress The ``configuration tells Cppcheck to show or suppress warnings for a certain type. Example: foo bar In the `unusedvar` checking the `foo` type will be checked. Warnings for `bar` type variables will be suppressed. # `` Libraries can be used to define preprocessor macros as well. For example: Each occurrence of "NULL_VALUE" in the code would then be replaced by "0" at preprocessor stage. # `` Use this for integer/float/bool/pointer types. Not for structs/unions. Lots of code relies on typedefs providing platform independent types. "podtype"-tags can be used to provide necessary information to cppcheck to support them. Without further information, cppcheck does not understand the type "uint16_t" in the following example: void test() { uint16_t a; } No message about variable 'a' being unused is printed: $ cppcheck --enable=style unusedvar.cpp Checking unusedvar.cpp... If uint16_t is defined in a library as follows, the result improves: The size of the type is specified in bytes. Possible values for the "sign" attribute are "s" (signed) and "u" (unsigned). Both attributes are optional. Using this library, cppcheck prints: $ cppcheck --library=lib.cfg --enable=style unusedvar.cpp Checking unusedvar.cpp... [unusedvar.cpp:2]: (style) Unused variable: a # `` A lot of C++ libraries, among those the STL itself, provide containers with very similar functionality. Libraries can be used to tell cppcheck about their behaviour. Each container needs a unique ID. It can optionally have a startPattern, which must be a valid Token::Match pattern and an endPattern that is compared to the linked token of the first token with such a link. The optional attribute "inherits" takes an ID from a previously defined container. The `hasInitializerListConstructor` attribute can be set when the container has a constructor taking an initializer list. Inside the `` tag, functions can be defined inside of the tags ``, `` and `` (on your choice). Each of them can specify an action like "resize" and/or the result it yields, for example "end-iterator". The following example provides a definition for std::vector, based on the definition of "stdContainer" (not shown): The tag `` can be added as well to provide more information about the type of container. Here is some of the attributes that can be set: * `string='std-like'` can be set for containers that match `std::string` interfaces. * `associative='std-like'` can be set for containers that match C++'s `AssociativeContainer` interfaces. # `` Specify that a class is a smart pointer by using ``. cppcheck-1.90/man/writing-rules-1.docbook000066400000000000000000000106071357737443600203400ustar00rootroot00000000000000
Part 1 - Getting started
Introduction This is a short and simple guide that describes how rules are written for Cppcheck. The patterns are defined with regular expressions. It is required that you know how regular expressions work.
Data representation of the source code The data used by the rules are not the raw source code. Cppcheck will read the source code and process it before the rules are used. Cppcheck is designed to find bugs and dangerous code. Stylistic information (such as indentation, comments, etc) are filtered out at an early state. You don't need to worry about such stylistic information when you write rules. Between each token in the code there is always a space. For instance the raw code "1+f()" is processed into "1 + f ( )" . The code is simplified in many ways.
Creating a simple rule When creating a rule there are two steps: Create the regular expression Create a XML based rule file
Step 1 - Creating the regular expression Cppcheck uses the PCRE library to handle regular expressions. PCRE stands for "Perl Compatible Regular Expressions". The homepage for PCRE is http://www.pcre.org/. Let's create a regular expression that checks for code such as: if (p) free(p); For such code the condition is often redundant (on most implementations it is valid to free a NULL pointer). The regular expression must be written for the simplified code. To see what the simplified code looks like you can create a source file with the code: void f() { if (p) free(p); } Save that code as dealloc.cpp and then use cppcheck --rule=".+" dealloc.cpp: $ ./cppcheck --rule=".+" dealloc.cpp Checking dealloc.cpp... [dealloc.cpp:1]: (style) found ' void f ( ) { if ( p ) { free ( p ) ; } }' The regular expression .+ matches everything and the matching text is shown on the screen. From that output we can see that the simplified code is: void f ( ) { if ( p ) { free ( p ) ; } } Now that we know how the simplified code looks. We can create a regular expression that matches it properly: $ cppcheck --rule="if \( p \) { free \( p \) ; }" dealloc.cpp Checking dealloc.cpp... [dealloc.cpp:2]: (style) found 'if ( p ) { free ( p ) ; }'
Step 2 - Create rule file A rule file is a simple XML file that contains: a pattern to search for an error message that is reported when pattern is found Here is a simple example: <?xml version="1.0"?> <rule version="1"> <pattern>if \( p \) { free \( p \) ; }</pattern> <message> <id>redundantCondition</id> <severity>style</severity> <summary>Redundant condition. It is valid to free a NULL pointer.</summary> </message> </rule> If you save that xml data in dealloc.rule you can test this rule: $ cppcheck --rule-file=dealloc.rule dealloc.cpp Checking dealloc.cpp... [dealloc.cpp:2]: (style) Redundant condition. It is valid to free a NULL pointer.
cppcheck-1.90/man/writing-rules-2.docbook000066400000000000000000000201041357737443600203320ustar00rootroot00000000000000
Part 2 - The Cppcheck data representation
Introduction In this article I will discuss the data representation that Cppcheck uses. The data representation that Cppcheck uses is specifically designed for static analysis. It is not intended to be generic and useful for other tasks.
See the data There are two ways to look at the data representation at runtime. Using --rule=.+ is one way. All tokens are written on a line: int a ; int b ; Using --debug is another way. The tokens are line separated in the same way as the original code: 1: int a@1 ; 2: int b@2 ; In the --debug output there are "@1" and "@2" shown. These are the variable ids (Cppcheck gives each variable a unique id). You can ignore these if you only plan to write rules with regular expressions, you can't use variable ids with regular expressions. In general, I will use the --rule=.+ output in this article because it is more compact.
Some of the simplifications The data is simplified in many ways.
Preprocessing The Cppcheck data is preprocessed. There are no comments, #define, #include, etc. Original source code: #define SIZE 123 char a[SIZE]; The Cppcheck data for that is: char a [ 123 ] ;
typedef The typedefs are simplified. typedef char s8; s8 x; The Cppcheck data for that is: ; char x ;
Calculations Calculations are simplified. int a[10 + 4]; The Cppcheck data for that is: int a [ 14 ] ;
Variables
Variable declarations Variable declarations are simplified. Only one variable can be declared at a time. The initialization is also broken out into a separate statement. int *a=0, b=2; The Cppcheck data for that is: int * a ; a = 0 ; int b ; b = 2 ; This is even done in the global scope. Even though that is invalid in C/C++.
Known variable values Known variable values are simplified. void f() { int x = 0; x++; array[x + 2] = 0; } The --debug output for that is: 1: void f ( ) 2: { 3: ; ; 4: ; 5: array [ 3 ] = 0 ; 6: } The variable x is removed because it is not used after the simplification. It is therefore redundant. The "known values" doesn't have to be numeric. Variable aliases, pointer aliases, strings, etc should be handled too. Example code: void f() { char *a = strdup("hello"); char *b = a; free(b); } The --debug output for that is: 1: void f ( ) 2: { 3: char * a@1 ; a@1 = strdup ( "hello" ) ; 4: ; ; 5: free ( a@1 ) ; 6: }
if/for/while
Braces in if/for/while-body Cppcheck makes sure that there are always braces in if/for/while bodies. if (x) f1(); The Cppcheck data for that is: if ( x ) { f1 ( ) ; }
No else if The simplified data representation doesn't have "else if". void f(int x) { if (x == 1) f1(); else if (x == 2) f2(); } The --debug output: 1: void f ( int x@1 ) 2: { 3: if ( x@1 == 1 ) { 4: f1 ( ) ; } 5: else { if ( x@1 == 2 ) { 6: f2 ( ) ; } } 7: }
Condition is always true / false Conditions that are always true / false are simplified. void f() { if (true) { f1(); } } The Cppcheck data is: void f ( ) { { f1 ( ) ; } } Another example: void f() { if (false) { f1(); } } The debug output: void f ( ) { }
Assignments Assignments within conditions are broken out from the condition. void f() { int x; if ((x = f1()) == 12) { f2(); } } The x=f1() is broken out. The --debug output: 1: void f ( ) 2: { 3: int x@1 ; 4: x@1 = f1 ( ) ; if ( x@1 == 12 ) { 5: f2 ( ) ; 6: } 7: } Replacing the "if" with "while" in the above example: void f() { int x; while ((x = f1()) == 12) { f2(); } } The x=f1() is broken out twice. The --debug output: 1: void f ( ) 2: { 3: int x@1 ; 4: x@1 = f1 ( ) ; while ( x@1 == 12 ) { 5: f2 ( ) ; x@1 = f1 ( ) ; 5: 6: } 7: }
Comparison with > Comparisons are simplified. The two conditions in this example are logically the same: void f() { if (x < 2); if (2 > x); } Cppcheck data doesn't use > for comparisons. It is converted into < instead. In the Cppcheck data there is no difference for 2>x and x<2. 1: 2: void f ( ) 3: { 4: if ( x < 2 ) { ; } 5: if ( x < 2 ) { ; } 6: } A similar conversion happens when >= is used.
if (x) and if (!x) If possible a condition will be reduced to x or !x. Here is an example code: void f() { if (!x); if (NULL == x); if (x == 0); if (x); if (NULL != x); if (x != 0); } The --debug output is: 1: void f ( ) 2: { 3: if ( ! x ) { ; } 4: if ( ! x ) { ; } 5: if ( ! x ) { ; } 6: 7: if ( x ) { ; } 8: if ( x ) { ; } 9: if ( x ) { ; } 10: }
cppcheck-1.90/man/writing-rules-3.docbook000066400000000000000000000164751357737443600203530ustar00rootroot00000000000000
Part 3 - Introduction to writing rules with C++
Introduction The goal for this article is to introduce how Cppcheck rules are written with C++. With C++ it is possible to write more complex rules than is possible with regular expressions.
Basics A C++ rule is written in a C++ function. Rules are organized into Check classes. For instance there is a class with the name CheckStl that contains various stl rules. The CheckOther can always be used if no other class suits you. When you have added your rule you must recompile Cppcheck before you can test it.
Division by zero This simple regular expression will check for division by zero: cppcheck --rule="/ 0" Here is the corresponding C++ check: // Detect division by zero void CheckOther::divisionByZero() { // Loop through all tokens for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // check if there is a division by zero if (Token::Match(tok, "/ 0")) { // report error divisionByZeroError(tok); } } } // Report error void CheckOther::divisionByZeroError() { reportError(tok, // location Severity::error, // severity "divisionByZero", // id "Division by zero"); // message } The Token::Match matches tokens against expressions. A few rules about Token::Match expressions are: tokens are either completely matched or not matched at all. The token "abc" is not matched by "ab". Spaces are used as separators. With normal regular expressions there are special meanings for + * ? ( ). These are just normal characters in Token::Match patterns.
Condition before deallocation In the first Writing rules part I described a rule that looks for redundant conditions. Here is the regular expression that was shown: if \( p \) { free \( p \) ; } The corresponding Token::Match expression is: if ( %var% ) { free ( %var% ) ; } The %var% pattern match any variable name. Here is a C++ function: // Find redundant condition before deallocation void CheckOther::dealloc() { // Loop through all tokens for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Is there a condition and a deallocation? if (Token::Match(tok, "if ( %var% ) { free ( %var% ) ; }")) { // Get variable name used in condition: const std::string varname1 = tok->strAt(2); // Get variable name used in deallocation: const std::string varname2 = tok->strAt(7); // Is the same variable used? if (varname1 == varname2) { // report warning deallocWarning(tok); } } } } // Report warning void CheckOther::deallocWarning() { reportError(tok, // location Severity::warning, // severity "dealloc", // id "Redundant condition"); // message } The strAt function is used to fetch strings from the token list. The parameter specifies the token offset. The result for "tok->tokAt(1)" is the same as for "tok->next()".
Validate function parameters Sometimes it is known that a function can't handle certain parameters. Here is an example rule that checks that the parameters for strtol or strtoul are valid: //--------------------------------------------------------------------------- // strtol(str, 0, radix) <- radix must be 0 or 2-36 //--------------------------------------------------------------------------- void CheckOther::invalidFunctionUsage() { // Loop through all tokens for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // Is there a function call for strtol or strtoul? if (!Token::Match(tok, "strtol|strtoul (")) continue; // Locate the third parameter of the function call.. // Counter that counts the parameters. int param = 1; // Scan the function call tokens. The "tok->tokAt(2)" returns // the token after the "(" for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) { // If a "(" is found then jump to the corresponding ")" if (tok2->str() == "(") tok2 = tok2->link(); // End of function call. else if (tok2->str() == ")") break; // Found a ",". increment param counter else if (tok2->str() == ",") { ++param; // If the param is 3 then check if the parameter is valid if (param == 3) { if (Token::Match(tok2, ", %num% )")) { // convert next token into a number MathLib::bigint radix; radix = MathLib::toLongNumber(tok2->strAt(1)); // invalid radix? if (!(radix == 0 || (radix >= 2 && radix <= 36))) { dangerousUsageStrtolError(tok2); } } break; } } } } } void CheckOther::dangerousUsageStrtolError(const Token *tok) { reportError(tok, // location Severity::error, // severity "dangerousUsageStrtol", // id "Invalid radix"); // message } The link() member function is used to find the corresponding ( ) [ ] or { } token. The inner loop is not necessary if you just want to get the last parameter. This code will check if the last parameter is numerical.. .. // Is there a function call? if (!Token::Match(tok, "do_something (")) continue; if (Token::Match(tok->next()->link()->tokAt(-2), "(|, %num% )")) ... The pattern (|, can also be written as [(,].
cppcheck-1.90/naming.json000066400000000000000000000003011357737443600154040ustar00rootroot00000000000000{ "script": "addons/naming.py", "args": [ "--private-member-variable=m[A-Z].*", "--var=[_a-z].*", "--const=[_a-zA-Z].*", "--function=[a-zA-Z].*" ] } cppcheck-1.90/oss-fuzz/000077500000000000000000000000001357737443600150465ustar00rootroot00000000000000cppcheck-1.90/oss-fuzz/Makefile000066400000000000000000000016011357737443600165040ustar00rootroot00000000000000# # fuzzer clients # ==================== # # Local libfuzzer client: # make CXX=clang++-6.0 CXXFLAGS="-fsanitize=address" fuzz-client CPPCHECK_DIR=.. INCLUDE_DIR=-I ${CPPCHECK_DIR}/lib -I ${CPPCHECK_DIR}/externals/simplecpp -I ${CPPCHECK_DIR}/externals/tinyxml -I ${CPPCHECK_DIR}/externals SRC_FILES=main.cpp type2.cpp ${CPPCHECK_DIR}/externals/simplecpp/simplecpp.cpp ${CPPCHECK_DIR}/externals/tinyxml/tinyxml2.cpp ${CPPCHECK_DIR}/lib/*.cpp all: oss-fuzz-client translate oss-fuzz-client: main.cpp type2.cpp type2.h ${CXX} -std=c++11 -g ${CXXFLAGS} -o oss-fuzz-client ${INCLUDE_DIR} ${SRC_FILES} -lFuzzingEngine fuzz-client: main.cpp type2.cpp type2.h ${CXX} -std=c++11 -g -O1 ${CXXFLAGS} -fsanitize=fuzzer -o fuzz-client ${INCLUDE_DIR} ${SRC_FILES} translate: translate.cpp type2.cpp type2.h g++ -o translate type2.cpp translate.cpp clean: rm -f oss-fuzz-client fuzz-client translate cppcheck-1.90/oss-fuzz/main.cpp000066400000000000000000000020121357737443600164710ustar00rootroot00000000000000 #include "cppcheck.h" #include "type2.h" class CppcheckExecutor : public ErrorLogger { private: CppCheck cppcheck; public: CppcheckExecutor() : ErrorLogger() , cppcheck(*this, false) { cppcheck.settings().addEnabled("all"); cppcheck.settings().inconclusive = true; } void run(const std::string &code) { cppcheck.check("test.cpp", code); } void reportOut(const std::string &outmsg) { } void reportErr(const ErrorLogger::ErrorMessage &msg) {} void reportProgress(const std::string& filename, const char stage[], const unsigned int value) {} }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) { if (dataSize < 10000) { const std::string code = generateCode2(data, dataSize); //std::ofstream fout("code.cpp"); //fout << code; //fout.close(); CppcheckExecutor cppcheckExecutor; cppcheckExecutor.run(code); } return 0; } cppcheck-1.90/oss-fuzz/translate.cpp000066400000000000000000000011441357737443600175470ustar00rootroot00000000000000 #include #include #include "type2.h" int main(int argc, char **argv) { const char *filename = argc==2 ? argv[1] : nullptr; if (!filename) { std::cout << "Invalid args, no filename\n"; return 1; } std::ifstream f(filename); if (!f.is_open()) { std::cout << "failed to open file:" << filename << "\n"; return 1; } std::string str((std::istreambuf_iterator(f)), std::istreambuf_iterator()); std::cout << generateCode2((const uint8_t *)str.data(), str.size()) << std::endl; return 0; } cppcheck-1.90/oss-fuzz/type2.cpp000066400000000000000000000153601357737443600166220ustar00rootroot00000000000000 #include #include "type2.h" static int getValue(const uint8_t *data, size_t dataSize, uint8_t maxValue, bool *done = nullptr) { static size_t pos; // current "data" position static int dataValue; // value extracted from data static int ones; // ones. This variable tracks if we need to add more stuff in "dataValue". // Shift more bits from "data" into "dataValue" if needed while (pos < dataSize && ones < 0xFFFF) { ones = (ones << 8) | 0xff; dataValue = (dataValue << 8) | data[pos]; pos++; } if (done) *done = (ones == 0); if (maxValue == 0) return 0; // Shift out info from "dataValue" using % . Using & and >> would work but then we are limited to "power of 2" max value. const int ret = dataValue % maxValue; ones /= maxValue; dataValue /= maxValue; return ret; } static std::string generateExpression2_lvalue(const uint8_t *data, size_t dataSize) { return "var" + std::to_string(1 + getValue(data, dataSize, 5)); } static std::string generateExpression2_Op(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants) { std::ostringstream code; switch (getValue(data, dataSize, 3)) { case 0: code << generateExpression2_lvalue(data, dataSize); break; case 1: code << "globalconstant" << (1 + getValue(data, dataSize, numberOfGlobalConstants)); break; case 2: code << (getValue(data, dataSize, 0x80) * 0x80 + getValue(data, dataSize, 0x80)); break; }; return code.str(); } static std::string generateExpression2_Expr(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants, int depth=0) { ++depth; const unsigned int type = (depth > 3) ? 0 : getValue(data, dataSize, 3); const char binop[] = "=<>+-*/%&|^"; const char *unop[] = {"++","--","()","~"}; switch (type) { case 0: return generateExpression2_Op(data, dataSize, numberOfGlobalConstants); case 1: { const char op = binop[getValue(data,dataSize,sizeof(binop)-1)]; const std::string lhs = (op == '=') ? generateExpression2_lvalue(data, dataSize) : generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); std::string ret = lhs + op + rhs; if (depth > 1 && op == '=') ret = "(" + ret + ")"; return ret; } case 2: { const char *u = unop[getValue(data,dataSize,sizeof(unop)/sizeof(*unop))]; if (u == std::string("()")) return "(" + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth) + ")"; else if (u == std::string("++") || u == std::string("--")) return u + generateExpression2_lvalue(data, dataSize); return u + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); } default: break; }; return "0"; } static std::string generateExpression2_Cond(const uint8_t *data, size_t dataSize, int numberOfGlobalConstants) { const char *comp[] = {"==", "!=", "<", "<=", ">", ">="}; const int i = getValue(data, dataSize, 6); const std::string lhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); return lhs + comp[i] + rhs; } static std::string functionStart() { static int functionNumber; return "int f" + std::to_string(++functionNumber) + "()\n" "{\n"; } static std::string generateExpression2_conditionalCode(const std::string &indent, const uint8_t *data, size_t dataSize, int numberOfGlobalConstants) { std::ostringstream code; if (indent.empty()) code << functionStart(); else code << indent << "{\n"; for (int line = 0; line < 4 || indent.empty(); ++line) { bool done = false; const int type1 = getValue(data, dataSize, 8, &done); if (done) break; const int mostLikelyType = (line >= 2) ? 4 : 0; // should var assignment or return be more likely? const int type2 = (indent.size() >= 12) ? mostLikelyType : // max indentation, no inner conditions ((type1 >= 5) ? mostLikelyType : type1); if (type2 == 0) { code << indent << " var" << getValue(data, dataSize, 5) << "=" << generateExpression2_Expr(data, dataSize, numberOfGlobalConstants) << ";\n"; } else if (type2 == 1) { code << indent << " if (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); } else if (type2 == 2) { code << indent << " if (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); code << indent << " else\n"; code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); } else if (type2 == 3) { code << indent << " while (" << generateExpression2_Cond(data, dataSize, numberOfGlobalConstants) << ")\n"; code << generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); } else if (type2 == 4) { code << indent << " return " << generateExpression2_Expr(data, dataSize, numberOfGlobalConstants) << ";\n"; if (indent.empty()) code << "}\n\n" << functionStart(); else break; } } if (!indent.empty()) code << indent << "}\n"; else code << " return 0;\n}\n"; return code.str(); } std::string generateCode2(const uint8_t *data, size_t dataSize) { std::ostringstream code; // create global constants constexpr int numberOfGlobalConstants = 0; /* const int numberOfGlobalConstants = getValue(data, dataSize, 5); for (int nr = 1; nr <= numberOfGlobalConstants; nr++) { const char *types[4] = {"char", "int", "long long", "float"}; code << "const " << types[getValue(data, dataSize, 4)] << " globalconstant" << nr << " = " << generateExpression2_Expr(data, dataSize, nr - 1) << ";\n"; } */ code << "int var1 = 1;\n" "int var2 = 0;\n" "int var3 = 1;\n" "int var4 = 0;\n" "int var5 = -1;\n\n"; code << generateExpression2_conditionalCode("", data, dataSize, numberOfGlobalConstants); return code.str(); } cppcheck-1.90/oss-fuzz/type2.h000066400000000000000000000001671357737443600162660ustar00rootroot00000000000000 #pragma once #include #include std::string generateCode2(const uint8_t *data, size_t dataSize); cppcheck-1.90/philosophy.md000066400000000000000000000027551357737443600157770ustar00rootroot00000000000000 Cppcheck Philosophy =================== It is important that everybody in the Cppcheck team has a consistent idea about how this tool should work. This is a static analyser tool. When it comes to writing warnings, quality is much more important than quantity. No false positives ------------------ A fundamental goal is "no false positives". It is not possible to achieve "no false positives" completely. One case where false positives are OK is when the code is garbage. If the code is written as it is by design, then our goal is to not warn. If it is not known if there is a problem, then in general we need to bailout. We can only warn when we see that there is a problem. Stylistic checks are much more prone to false positives and therefore we should avoid writing stylistic checks mostly. No configuration ---------------- We want that a user can run Cppcheck without explicit -D and -I configuration. When this happens the false positives should be avoided. The user can reduce false negatives with configuration. Allow compiler extensions ------------------------- This is not just a tool for mainstream gcc/msvc c/c++ developers. If you can compile the code with a C/C++ compiler then our goal is that Cppcheck can check it. C++ language ------------ Our goal is to be highly portable. Users must be able to compile Cppcheck with GCC 4.6 or MSVS 2013. No C++14 is allowed. A subset of the C++11 is allowed. No dependencies --------------- We are very careful about dependencies. cppcheck-1.90/platforms/000077500000000000000000000000001357737443600152555ustar00rootroot00000000000000cppcheck-1.90/platforms/aix_ppc64.xml000066400000000000000000000005611357737443600175760ustar00rootroot00000000000000 8 unsigned 2 4 8 8 4 8 8 8 8 2 cppcheck-1.90/platforms/arm32-wchar_t2.xml000066400000000000000000000005611357737443600204340ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 8 4 4 2 cppcheck-1.90/platforms/arm32-wchar_t4.xml000066400000000000000000000005611357737443600204360ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 8 4 4 4 cppcheck-1.90/platforms/arm64-wchar_t2.xml000066400000000000000000000005611357737443600204410ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 8 8 4 2 cppcheck-1.90/platforms/arm64-wchar_t4.xml000066400000000000000000000005611357737443600204430ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 8 8 4 4 cppcheck-1.90/platforms/avr8.xml000066400000000000000000000006041357737443600166570ustar00rootroot00000000000000 8 unsigned 1 2 2 4 8 4 4 4 2 2 2 cppcheck-1.90/platforms/cppcheck-platforms.rng000066400000000000000000000024571357737443600215620ustar00rootroot00000000000000 cppcheck-1.90/platforms/cray_sv1.xml000066400000000000000000000005621357737443600175310ustar00rootroot00000000000000 8 unsigned 2 8 8 8 8 8 16 8 8 8 cppcheck-1.90/platforms/msp430_eabi_large_datamodel.xml000066400000000000000000000005571357737443600232000ustar00rootroot00000000000000 8 signed 2 2 4 8 4 8 8 4 4 2 cppcheck-1.90/platforms/unix32-unsigned.xml000066400000000000000000000005621357737443600207440ustar00rootroot00000000000000 8 unsigned 2 4 4 8 4 8 12 4 4 2 cppcheck-1.90/platforms/unix64-unsigned.xml000066400000000000000000000005621357737443600207510ustar00rootroot00000000000000 8 unsigned 2 4 8 8 4 8 16 8 8 4 cppcheck-1.90/pylintrc_travis000066400000000000000000000012561357737443600164310ustar00rootroot00000000000000[MESSAGES CONTROL] disable=C,R,W enable=mixed-indentation, trailing-whitespace, print-statement, literal-comparison, unnecessary-semicolon, mixed-line-endings, bad-open-mode, redundant-unittest-assert, boolean-datetime, deprecated-method, anomalous-unicode-escape-in-string, anomalous-backslash-in-string, bad-indentation [REPORTS] reports=no [TYPECHECK] # See https://stackoverflow.com/questions/10300082/how-to-prevent-python-pylint-complaining-about-socket-class-sendall-method ignored-classes=SQLObject,_socketobject [MASTER] init-hook='import sys; sys.path.append("./addons")' suggestion-mode=yes cppcheck-1.90/readme.md000066400000000000000000000115111357737443600150240ustar00rootroot00000000000000# **Cppcheck** |Linux Build Status|Windows Build Status|Coverity Scan Build Status|License| |:--:|:--:|:--:|:-:| |[![Linux Build Status](https://img.shields.io/travis/danmar/cppcheck/master.svg?label=Linux%20build)](https://travis-ci.org/danmar/cppcheck)|[![Windows Build Status](https://img.shields.io/appveyor/ci/danmar/cppcheck/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/danmar/cppcheck/branch/master)|[![Coverity Scan Build Status](https://img.shields.io/coverity/scan/512.svg)](https://scan.coverity.com/projects/512)|[![License](https://img.shields.io/badge/license-GPL3.0-blue.svg)](https://opensource.org/licenses/GPL-3.0) ## About the name The original name of this program was "C++check", but it was later changed to "Cppcheck". Despite the name, Cppcheck is designed for both C and C++. ## Manual A manual is available [online](http://cppcheck.sourceforge.net/manual.pdf). ## Donate CPU Cppcheck is a hobby project with limited resources. You can help us by donating CPU (1 core or as many as you like). It is simple: 1. Download (and extract) Cppcheck source code 2. Run script: python cppcheck/tools/donate-cpu.py The script will analyse debian source code and upload the results to a cppcheck server. We need these results both to improve Cppcheck and to detect regressions. You can stop the script whenever you like with Ctrl C. ## Compiling Any C++11 compiler should work. For compilers with partial C++11 support it may work. If your compiler has the C++11 features that are available in Visual Studio 2013 / GCC 4.6 then it will work. To build the GUI, you need Qt. When building the command line tool, [PCRE](http://www.pcre.org/) is optional. It is used if you build with rules. There are multiple compilation choices: * qmake - cross platform build tool * cmake - cross platform build tool * Windows: Visual Studio (VS 2013 and above) * Windows: Qt Creator + mingw * gnu make * g++ 4.6 (or later) * clang++ ### cmake Example, compiling Cppcheck with cmake: ```shell mkdir build cd build cmake .. cmake --build . ``` If you want to compile the GUI you can use the flag -DBUILD_GUI=ON For rules support (requires pcre) use the flag -DHAVE_RULES=ON For release builds it is recommended that you use: -DUSE_MATCHCOMPILER=ON ### qmake You can use the gui/gui.pro file to build the GUI. ```shell cd gui qmake make ``` ### Visual Studio Use the cppcheck.sln file. The file is configured for Visual Studio 2019, but the platform toolset can be changed easily to older or newer versions. The solution contains platform targets for both x86 and x64. To compile with rules, select "Release-PCRE" or "Debug-PCRE" configuration. pcre.lib (pcre64.lib for x64 builds) and pcre.h are expected to be in /externals then. A current version of PCRE for Visual Studio can be obtained using [vcpkg](https://github.com/microsoft/vcpkg). ### Qt Creator + MinGW The PCRE dll is needed to build the CLI. It can be downloaded here: http://software-download.name/pcre-library-windows/ ### GNU make Simple, unoptimized build (no dependencies): ```shell make ``` The recommended release build is: ```shell make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" ``` Flags: 1. `MATCHCOMPILER=yes` Python is used to optimise cppcheck. The Token::Match patterns are converted into C++ code at compile time. 2. `FILESDIR=/usr/share/cppcheck` Specify folder where cppcheck files are installed (addons, cfg, platform) 3. `HAVE_RULES=yes` Enable rules (PCRE is required if this is used) 4. `CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function"` Enables most compiler optimizations, disables cppcheck-internal debugging code and enables basic compiler warnings. ### g++ (for experts) If you just want to build Cppcheck without dependencies then you can use this command: ```shell g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml/*.cpp ``` If you want to use `--rule` and `--rule-file` then dependencies are needed: ```shell g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Ilib -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml/*.cpp ``` ### MinGW ```shell mingw32-make LDFLAGS=-lshlwapi ``` ### Other Compiler/IDE 1. Create an empty project file / makefile. 2. Add all cpp files in the cppcheck cli and lib folders to the project file / makefile. 3. Add all cpp files in the externals folders to the project file / makefile. 4. Compile. ### Cross compiling Win32 (CLI) version of Cppcheck in Linux ```shell sudo apt-get install mingw32 make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" RDYNAMIC="" mv cppcheck cppcheck.exe ``` ## Webpage http://cppcheck.sourceforge.net/ cppcheck-1.90/readme.txt000066400000000000000000000074641357737443600152570ustar00rootroot00000000000000========= Cppcheck ========= About The original name of this program is "C++check" but it was later changed to "cppcheck". Manual A manual is available online: http://cppcheck.sourceforge.net/manual.pdf Compiling Any C++11 compiler should work. For compilers with partial C++11 support it may work. If your compiler has the C++11 features that are available in Visual Studio 2013 / GCC 4.6 then it will work. To build the GUI, you need Qt. When building the command line tool, PCRE is optional. It is used if you build with rules. There are multiple compilation choices: * qmake - cross platform build tool * cmake - cross platform build tool * Windows: Visual Studio * Windows: Qt Creator + mingw * gnu make * g++ 4.6 (or later) * clang++ cmake ===== Example, compiling Cppcheck with cmake: mkdir build cd build cmake .. cmake --build . If you want to compile the GUI you can use the flag -DBUILD_GUI=ON For rules support (requires pcre) use the flag -DHAVE_RULES=ON For release builds it is recommended that you use: -DUSE_MATCHCOMPILER=ON qmake ===== You can use the gui/gui.pro file to build the GUI. cd gui qmake make Visual Studio ============= Use the cppcheck.sln file. The file is configured for Visual Studio 2019, but the platform toolset can be changed easily to older or newer versions. The solution contains platform targets for both x86 and x64. To compile with rules, select "Release-PCRE" or "Debug-PCRE" configuration. pcre.lib (pcre64.lib for x64 builds) and pcre.h are expected to be in /externals then. A current version of PCRE for Visual Studio can be obtained using vcpkg: https://github.com/microsoft/vcpkg Qt Creator + mingw ================== The PCRE dll is needed to build the CLI. It can be downloaded here: http://software-download.name/pcre-library-windows/ gnu make ======== Simple build (no dependencies): make The recommended release build is: make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes Flags: MATCHCOMPILER=yes : Python is used to optimise cppcheck at compile time FILESDIR=/usr/share/cppcheck : Specify folder where cppcheck files are installed HAVE_RULES=yes : Enable rules (pcre is required if this is used) g++ (for experts) ================= If you just want to build Cppcheck without dependencies then you can use this command: g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml/*.cpp If you want to use --rule and --rule-file then dependencies are needed: g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Ilib -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml/*.cpp mingw ===== The "LDFLAGS=-lshlwapi" is needed when building with mingw mingw32-make LDFLAGS=-lshlwapi other compilers/ide =================== 1. Create a empty project file / makefile. 2. Add all cpp files in the cppcheck cli and lib folders to the project file / makefile. 3. Add all cpp files in the externals folders to the project file / makefile. 4. Compile. Cross compiling Win32 (CLI) version of Cppcheck in Linux sudo apt-get install mingw32 make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" mv cppcheck cppcheck.exe Webpage http://cppcheck.sourceforge.net/ cppcheck-1.90/readmeja.md000066400000000000000000000130331357737443600153400ustar00rootroot00000000000000# Cppcheck | Linux ビルド状態 | Windows ビルド状態 | Coverity Scan Build 状態 | |:--:|:--:|:--:| | [![Linux ビルド状態](https://img.shields.io/travis/danmar/cppcheck/master.svg?label=Linux%20build)](https://travis-ci.org/danmar/cppcheck) | [![Windows ビルド状態](https://img.shields.io/appveyor/ci/danmar/cppcheck/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/danmar/cppcheck/branch/master) | [![Coverity Scan Build 状態](https://img.shields.io/coverity/scan/512.svg)](https://scan.coverity.com/projects/512) | ## 名前について このプログラムは元々、"C++check"という名前でしたが後に"Cppcheck"に変更されました。 このような名前ですが、Cppcheckは CとC++の両方に対して設計されています。 ## マニュアル マニュアルは[オンライン上に](http://cppcheck.sourceforge.net/manual.pdf)あります。 ## ビルド C++11に対応したコンパイラが利用できます。部分的にC++11にサポートしたコンパイラも利用できるかもしれません。もし、あなたのコンパイラがVisual Studio 2013や GCC 4.6で利用できるC++11機能がサポートされているなら、そのコンパイラが利用できます。 GUIも利用する場合、Qtライブラリが必要です。 コマンドラインツールをビルドする場合、[PCRE](http://www.pcre.org/)はオプションです。これはルールを作成するために利用します。 コンパイル上の選択肢がいくつかあります。 * qmake - クロスプラットフォームのビルドツール * cmake - クロスプラットフォームのビルドツール * Windows: Visual Studio (VS 2013 またはそれ以上) * Windows: Qt Creator + mingw * gnu make * g++ 4.6 (またはそれ以上) * clang++ ### cmake cmakeでCppcheckをコンパイルする例 ```shell mkdir build cd build cmake .. cmake --build . ``` C++標準を指定する必要がある場合次のオプションを指定します。 -DCMAKE_CXX_STANDARD=11 CppcheckのGUIが必要な場合次のフラグを指定します。 -DBUILD_GUI=ON pcreが必要になりますが、正規表現のルールサポートが必要な場合次のフラグを指定します。 -DHAVE_RULES=ON ### qmake GUIをビルドするには、gui/gui.proファイルが利用できます。 ```shell cd gui qmake make ``` ### Visual Studio cppcheck.slnファイルが利用できます。このファイルは、Visual Studio 2019向けです。しかし、このプラットフォームツールセットはこれより新しいバージョンまたは古いバージョン向けに変更できます。このソルーションには、プラットフォームターゲットとしてx86とx64があります。 ルールをコンパイルするためには、"Release-PCRE" または "Debug-PCRE" 設定を選択してください。pcre.lib (または pcre64.lib x64ビルド向け) と pcre.h を /externals にコピーしてください。Visual Studio のための PCRE の最新バージョンは [vcpkg](https://github.com/microsoft/vcpkg) から取得できます。 ### Qt Creator + MinGW コマンドラインツールをビルドするには、PCRE.dllが必要です。これは以下のURLからダウンロードできます。: http://software-download.name/pcre-library-windows/ ### GNU make 単純で最適化しないビルド(依存関係なし): ```shell make ``` 推奨するリリースビルド方法: ```shell make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" ``` フラグ: 1. `MATCHCOMPILER=yes` cppcheckの最適化にPythonを使用します。Token::Match パターンはコンパイル時にlC++コードに変換されます。 2. `FILESDIR=/usr/share/cppcheck` cppcheckの設定ファイル(addon や cfg や platform)を置くディレクトリを指定します。 3. `HAVE_RULES=yes` ルール機能の有効化 (ルール機能には PCRE が必要です)設定です。 4. `CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function"` ほとんどのコンパイラの最適化オプション、cppcheckの内部デバッグコードの無効化、基本的なコンパイラ警告の有効化 ### g++ (エキスパート向け) 依存関係なく Cppcheckをビルドしたい場合、次のコマンドを利用できます。 ```shell g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml/*.cpp ``` `--rule` や `--rule-file` を利用する場合、依存ライブラリが必要です。 ```shell g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml/*.cpp ``` ### MinGW ```shell mingw32-make LDFLAGS=-lshlwapi ``` ### その他のコンパイラ/IDE 1. 空のプロジェクトファイル /makefileの作成 2. cppcheck cli それに lib ディレクトリに含まれる全てのcppファイルをそのプロジェクトファイルまたはmakefileに加えます。 3. externalsフォルダの全てのcppファイルをプロジェクトファイル / makefileに追加します。 4. ビルド ### Linux で Win32 コマンドラインバージョンをクロスコンパイル ```shell sudo apt-get install mingw32 make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" RDYNAMIC="" mv cppcheck cppcheck.exe ``` ## Webページ http://cppcheck.sourceforge.net/ cppcheck-1.90/requirements.txt000066400000000000000000000001771357737443600165370ustar00rootroot00000000000000pcre,https://ftp.pcre.org/pub/pcre/pcre-8.43.tar.gz -H sha256:0b8e7465dc5e98c757cc3650a20a7843ee4c3edf50aaf60bb33fd879690d2c73 cppcheck-1.90/rules/000077500000000000000000000000001357737443600144005ustar00rootroot00000000000000cppcheck-1.90/rules/empty-catch-block.xml000066400000000000000000000003541357737443600204320ustar00rootroot00000000000000 normal style Empty catch block found. cppcheck-1.90/rules/error-reporting.xml000066400000000000000000000004631357737443600202650ustar00rootroot00000000000000 Severity :: fromString \( "\w+" \) ConstantSeverityFromString style Constant severity lookups should be done via Severity::constant. cppcheck-1.90/rules/show-all-defines.rule000066400000000000000000000003511357737443600204310ustar00rootroot00000000000000 define .* showalldefines information cppcheck-1.90/rules/stl.xml000066400000000000000000000005541357737443600157300ustar00rootroot00000000000000 \. find \( "[^"]+?" \) == \d+ UselessSTDStringFind performance When looking for a string at a fixed position compare is faster. cppcheck-1.90/rules/strlen-empty-str.xml000066400000000000000000000005001357737443600203660ustar00rootroot00000000000000 if \( ([!] )*?(strlen) \( \w+? \) ([>] [0] )*?\) { StrlenEmptyString performance Using strlen() to check if a string is empty is not efficient. cppcheck-1.90/rules/suggest_nullptr.xml000066400000000000000000000006111357737443600203610ustar00rootroot00000000000000 raw modernizeUseNullPtr style Prefer to use a 'nullptr' instead of initializing a pointer with 0. cppcheck-1.90/rules/token-matching.xml000066400000000000000000000023271357737443600200360ustar00rootroot00000000000000 Token :: (?:findm|(?:simple|)M)atch \([^,]+,\s+"(?:\s+|[^"]+?\s+") TokenMatchSpacing style Useless extra spacing for Token::*Match. (?U)Token :: Match \([^,]+,\s+"[^%|!\[\]]+" UseTokensimpleMatch error Token::simpleMatch should be used to match tokens without special pattern requirements. \b[\w_]+ \. tokAt \( 0 \) TokentokAt0 error tok->tokAt(0) is a slow way to say tok. \b[\w_]+ \. strAt \( 0 \) TokenstrAt0 error tok->strAt(0) is a slow way to say tok->str() cppcheck-1.90/rules/unused-deref.xml000066400000000000000000000004221357737443600175060ustar00rootroot00000000000000 [;{}] [*] \w+? (\+\+|\-\-) ; UnusedDeref style Redundant * found, "*p++" is the same as "*(p++)". cppcheck-1.90/runastyle000077500000000000000000000037351357737443600152320ustar00rootroot00000000000000#!/bin/bash # The version check in this script is used to avoid commit battles # between different developers that use different astyle versions as # different versions might have different output (this has happened in # the past). # If project management wishes to take a newer astyle version into use # just change this string to match the start of astyle version string. ASTYLE_VERSION="3.0.1" ASTYLE="${ASTYLE-astyle}" DETECTED_VERSION=$("$ASTYLE" --version 2>&1 | awk '{ print $NF; }') if [ "$DETECTED_VERSION" != "${ASTYLE_VERSION}" ]; then echo "You should use version: ${ASTYLE_VERSION}" echo "Detected version: ${DETECTED_VERSION}" exit 1 fi RCFILE=.astylerc # # Functions to format C/C++ source code # function formatCplusplus { "$ASTYLE" --options=$RCFILE "$1" } function formatCplusplusRecursive { RCFILE=.astylerc "$ASTYLE" --options=$RCFILE --recursive "$1" } # # Function to format XML files # function formatXML { xmllint --format -o "$1_" "$1" if cmp -s "$1_" "$1"; then rm -f "$1_" echo Unchanged $1 else mv -f "$1_" "$1" echo Formatted $1 fi } formatCplusplus "cli/*.cpp" formatCplusplus "cli/*.h" formatCplusplus "democlient/*.cpp" formatCplusplus "gui/*.cpp" formatCplusplus "gui/*.h" formatCplusplusRecursive "gui/test/*.cpp" formatCplusplusRecursive "gui/test/*.h" formatCplusplus "lib/*.cpp" formatCplusplus "lib/*.h" formatCplusplus "oss-fuzz/*.cpp" formatCplusplus "oss-fuzz/*.h" formatCplusplus "test/*.cpp" formatCplusplus "test/cfg/*.c" formatCplusplus "test/cfg/*.cpp" formatCplusplus "test/*.h" formatCplusplus "tools/*.cpp" formatCplusplusRecursive "tools/*.h" formatCplusplusRecursive "samples/*.c" formatCplusplusRecursive "samples/*.cpp" # format config files # TODO: use other tool than xmllint? use tabs instead of spaces? for CFGFILE in cfg/*.cfg do formatXML "$CFGFILE" done for PLATFORMFILE in platforms/*.xml do formatXML "$PLATFORMFILE" done formatXML man/cppcheck.1.xml formatXML cppcheck-errors.rng formatXML rules/*.xml cppcheck-1.90/runastyle.bat000066400000000000000000000040721357737443600157670ustar00rootroot00000000000000@REM Script to run AStyle on the sources @REM The version check in this script is used to avoid commit battles @REM between different developers that use different astyle versions as @REM different versions might have different output (this has happened in @REM the past). @REM If project management wishes to take a newer astyle version into use @REM just change this string to match the start of astyle version string. @SET ASTYLE_VERSION="Artistic Style Version 3.0.1" @SET ASTYLE="astyle" @SET DETECTED_VERSION="" @FOR /F "tokens=*" %%i IN ('%ASTYLE% --version') DO SET DETECTED_VERSION=%%i @ECHO %DETECTED_VERSION% | FINDSTR /B /C:%ASTYLE_VERSION% > nul && ( ECHO "%DETECTED_VERSION%" matches %ASTYLE_VERSION% ) || ( ECHO You should use: %ASTYLE_VERSION% ECHO Detected: "%DETECTED_VERSION%" GOTO EXIT_ERROR ) @SET RCFILE=.astylerc %ASTYLE% --options=%RCFILE% cli/*.cpp %ASTYLE% --options=%RCFILE% cli/*.h %ASTYLE% --options=%RCFILE% democlient/*.cpp %ASTYLE% --options=%RCFILE% gui/*.cpp %ASTYLE% --options=%RCFILE% gui/*.h %ASTYLE% --options=%RCFILE% -r gui/test/*.cpp %ASTYLE% --options=%RCFILE% -r gui/test/*.h %ASTYLE% --options=%RCFILE% lib/*.cpp %ASTYLE% --options=%RCFILE% lib/*.h %ASTYLE% --options=%RCFILE% test/*.cpp %ASTYLE% --options=%RCFILE% test/cfg/*.c %ASTYLE% --options=%RCFILE% test/cfg/*.cpp %ASTYLE% --options=%RCFILE% test/*.h %ASTYLE% --options=%RCFILE% -r tools/*.cpp %ASTYLE% --options=%RCFILE% -r tools/*.h %ASTYLE% --options=%RCFILE% -r samples/*.c %ASTYLE% --options=%RCFILE% -r samples/*.cpp @REM Format configuration files @SET XMLLINT=xmllint WHERE %XMLLINT% @IF %ERRORLEVEL% NEQ 0 ( ECHO WARNING: %XMLLINT% was not found. Skipping configuration file formatting! ) ELSE ( PUSHD "cfg" FOR /F "tokens=* delims=" %%f IN ('DIR /B *.cfg') DO @CALL :runxmllint "%%f" POPD ) @GOTO :EOF :EXIT_ERROR EXIT /B 1 @REM Function that formats one XML file @REM Argument: %1: XML-File to format :runxmllint xmllint --format -o "%~1_" "%~1" MOVE /Y "%~1_" "%~1" @GOTO :EOF cppcheck-1.90/samples/000077500000000000000000000000001357737443600147125ustar00rootroot00000000000000cppcheck-1.90/samples/AssignmentAddressToInteger/000077500000000000000000000000001357737443600221515ustar00rootroot00000000000000cppcheck-1.90/samples/AssignmentAddressToInteger/bad.c000066400000000000000000000001401357737443600230360ustar00rootroot00000000000000int foo(int *p) { int a = p; return a + 4; } int main() { int i[10]; foo(i); } cppcheck-1.90/samples/AssignmentAddressToInteger/good.c000066400000000000000000000001221357737443600232400ustar00rootroot00000000000000int* foo(int *p) { return p + 4; } int main() { int i[10]; foo(i); } cppcheck-1.90/samples/AssignmentAddressToInteger/out.txt000066400000000000000000000002471357737443600235240ustar00rootroot00000000000000samples\AssignmentAddressToInteger\bad.c:3:11: portability: Assigning a pointer to an integer is not portable. [AssignmentAddressToInteger] int a = p; ^ cppcheck-1.90/samples/arrayIndexOutOfBounds/000077500000000000000000000000001357737443600211505ustar00rootroot00000000000000cppcheck-1.90/samples/arrayIndexOutOfBounds/bad.c000066400000000000000000000001251357737443600220400ustar00rootroot00000000000000int a[2]; int main() { a[0] = 0; a[1] = 0; a[2] = 0; return a[0]; } cppcheck-1.90/samples/arrayIndexOutOfBounds/good.c000066400000000000000000000001221357737443600222370ustar00rootroot00000000000000int a[3]; int main() { a[0] = 0; a[1] = 0; a[2] = 0; return 0; } cppcheck-1.90/samples/arrayIndexOutOfBounds/out.txt000066400000000000000000000002271357737443600225210ustar00rootroot00000000000000samples\arrayIndexOutOfBounds\bad.c:7:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] a[2] = 0; ^ cppcheck-1.90/samples/autoVariables/000077500000000000000000000000001357737443600175135ustar00rootroot00000000000000cppcheck-1.90/samples/autoVariables/bad.c000066400000000000000000000001331357737443600204020ustar00rootroot00000000000000void foo(int **a) { int b = 1; *a = &b; } int main() { int *c; foo(&c); } cppcheck-1.90/samples/autoVariables/good.c000066400000000000000000000001531357737443600206060ustar00rootroot00000000000000void foo(int **a) { int b = 1; **a = b; } int main() { int b; int *c = &b; foo(&c); } cppcheck-1.90/samples/autoVariables/out.txt000066400000000000000000000002141357737443600210600ustar00rootroot00000000000000samples\autoVariables\bad.c:4:5: error: Address of local auto-variable assigned to a function parameter. [autoVariables] *a = &b; ^ cppcheck-1.90/samples/bufferAccessOutOfBounds/000077500000000000000000000000001357737443600214355ustar00rootroot00000000000000cppcheck-1.90/samples/bufferAccessOutOfBounds/bad.c000066400000000000000000000001471357737443600223310ustar00rootroot00000000000000int main() { int a[2]; int i; for (i = 0; i < 3; i++) a[i] = 0; return a[0]; } cppcheck-1.90/samples/bufferAccessOutOfBounds/good.c000066400000000000000000000001471357737443600225330ustar00rootroot00000000000000int main() { int a[3]; int i; for (i = 0; i < 3; i++) a[i] = 0; return a[0]; } cppcheck-1.90/samples/bufferAccessOutOfBounds/out.txt000066400000000000000000000002421357737443600230030ustar00rootroot00000000000000samples\bufferAccessOutOfBounds\bad.c:6:10: error: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] a[i] = 0; ^ cppcheck-1.90/samples/erase/000077500000000000000000000000001357737443600160115ustar00rootroot00000000000000cppcheck-1.90/samples/erase/bad.cpp000066400000000000000000000004611357737443600172440ustar00rootroot00000000000000#include int main() { std::vector items; items.push_back(1); items.push_back(2); items.push_back(3); std::vector::iterator iter; for (iter = items.begin(); iter != items.end(); ++iter) { if (*iter == 2) { items.erase(iter); } } } cppcheck-1.90/samples/erase/good.cpp000066400000000000000000000005261357737443600174500ustar00rootroot00000000000000#include int main() { std::vector items; items.push_back(1); items.push_back(2); items.push_back(3); std::vector::iterator iter; for (iter = items.begin(); iter != items.end();) { if (*iter == 2) { iter = items.erase(iter); } else { ++iter; } } } cppcheck-1.90/samples/erase/out.txt000066400000000000000000000022751357737443600173670ustar00rootroot00000000000000samples\erase\bad.cpp:9:32: error: Using iterator to local container 'items' that may be invalid. [invalidContainer] for (iter = items.begin(); iter != items.end(); ++iter) { ^ samples\erase\bad.cpp:9:17: note: Iterator to container is created here. for (iter = items.begin(); iter != items.end(); ++iter) { ^ samples\erase\bad.cpp:10:19: note: Assuming condition is true. if (*iter == 2) { ^ samples\erase\bad.cpp:10:19: note: Assuming condition is true. if (*iter == 2) { ^ samples\erase\bad.cpp:9:37: note: Assuming condition is true. for (iter = items.begin(); iter != items.end(); ++iter) { ^ samples\erase\bad.cpp:11:13: note: After calling 'erase', iterators or references to the container's data may be invalid . items.erase(iter); ^ samples\erase\bad.cpp:4:22: note: Variable created here. std::vector items; ^ samples\erase\bad.cpp:9:32: note: Using iterator to local container 'items' that may be invalid. for (iter = items.begin(); iter != items.end(); ++iter) { ^ cppcheck-1.90/samples/memleak/000077500000000000000000000000001357737443600163255ustar00rootroot00000000000000cppcheck-1.90/samples/memleak/bad.c000066400000000000000000000002011357737443600172100ustar00rootroot00000000000000#include int main() { int result; char *a = malloc(10); a[0] = 0; result = a[0]; return result; } cppcheck-1.90/samples/memleak/good.c000066400000000000000000000002161357737443600174200ustar00rootroot00000000000000#include int main() { int result; char *a = malloc(10); a[0] = 0; result = a[0]; free(a); return result; } cppcheck-1.90/samples/memleak/out.txt000066400000000000000000000001241357737443600176720ustar00rootroot00000000000000samples/memleak/bad.c:8:5: error: Memory leak: a [memleak] return result; ^ cppcheck-1.90/samples/outOfBounds/000077500000000000000000000000001357737443600171615ustar00rootroot00000000000000cppcheck-1.90/samples/outOfBounds/bad.c000066400000000000000000000001301357737443600200450ustar00rootroot00000000000000#include int main() { char str[5]; strcpy(str, "0123456789abcdef"); } cppcheck-1.90/samples/outOfBounds/good.c000066400000000000000000000001301357737443600202470ustar00rootroot00000000000000#include int main() { char str[10]; snprintf(str, 10, "%s", "abc"); } cppcheck-1.90/samples/outOfBounds/out.txt000066400000000000000000000002311357737443600205250ustar00rootroot00000000000000samples\outOfBounds\bad.c:5:12: error: Buffer is accessed out of bounds: str [bufferAccessOutOfBounds] strcpy(str, "0123456789abcdef"); ^ cppcheck-1.90/samples/resourceLeak/000077500000000000000000000000001357737443600173365ustar00rootroot00000000000000cppcheck-1.90/samples/resourceLeak/bad.c000066400000000000000000000001631357737443600202300ustar00rootroot00000000000000#include int main() { FILE *a = fopen("good.c", "r"); if (!a) return 0; return 0; } cppcheck-1.90/samples/resourceLeak/good.c000066400000000000000000000002011357737443600204230ustar00rootroot00000000000000#include int main() { FILE *a = fopen("good.c", "r"); if (!a) return 0; fclose(a); return 0; } cppcheck-1.90/samples/resourceLeak/out.txt000066400000000000000000000001331357737443600207030ustar00rootroot00000000000000samples\resourceLeak\bad.c:8:5: error: Resource leak: a [resourceLeak] return 0; ^ cppcheck-1.90/samples/syntaxError/000077500000000000000000000000001357737443600172525ustar00rootroot00000000000000cppcheck-1.90/samples/syntaxError/bad.c000066400000000000000000000000371357737443600201440ustar00rootroot00000000000000int main() { #ifdef A } #endif cppcheck-1.90/samples/syntaxError/good.c000066400000000000000000000000401357737443600203400ustar00rootroot00000000000000int main() { #ifndef A #endif } cppcheck-1.90/samples/syntaxError/out.txt000066400000000000000000000001321357737443600206160ustar00rootroot00000000000000samples\syntaxError\bad.c:2:1: error: Unmatched '{'. Configuration: ''. [syntaxError] { ^ cppcheck-1.90/snap/000077500000000000000000000000001357737443600142075ustar00rootroot00000000000000cppcheck-1.90/snap/gui/000077500000000000000000000000001357737443600147735ustar00rootroot00000000000000cppcheck-1.90/snap/gui/cppcheck-gui.desktop000066400000000000000000000003331357737443600207270ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=Cppcheck Comment=A tool for static C/C++ code analysis Exec=cppcheck-gui Icon=${SNAP}/meta/gui/cppcheck-gui.png Terminal=true StartupNotify=true Categories=Development;Debugger;Qt; cppcheck-1.90/snap/gui/cppcheck-gui.png000066400000000000000000000047571357737443600200600ustar00rootroot00000000000000PNG  IHDRAAE IDATxt$񎝬m۶mڶm\۶m۾kS{*'әAMwuUX^V x/1ovb :n*m⃟ `.A 11V>2`vIJzFfH/S6o>#S=oW2iҫj-n[Ixx.1B<$((X/g";w^]c۷_3ޗWH2Mh3(m 'QSAX Ԭ[. ΧҲ)QYo=Z Iɒ s煲l>X?wA4C@T0;]uGj.npaUTl3W!_Leow8Wo2G"N` ZP]C'!8Fg;ZLTFQN@zZi\EsE\ͻzBȍ.]ޣ:[ȓntƍ{L(P/\n? @6j`e9!|Ѩ1m;~Y!,Xdyvb3d o,jdxVP ,tU!Gg!n^ǔH5}u مЬYԯ?4]ХPE!t 5Hmo͚`=q˒{=Hdd<3nCHJzZ7< ~n #f6ȰނzO. +QN+=^A#$6}8BFߣ<ҥ qwim:iwG4Uk*a읖~ĒY'>Gh QqUjƺ'd;\4F+wmJH,W`X~+#&ԁ/P=0NrSijrȨt_iےUnK$"*5xᶪJJ'%4'5)mVc G`ǎR׍řuXn d},ݒ]- K=ƫ EᲚ$27.ѣ\ siQ""f}wH|.K&'9,ך/#&GU_Ԫ՗0vCanݖLr~FEhOC]u)HѲrzAKj65Q.+q|YG^mUNO|mS,q1kUgsS7YRd_:DqIlԚ?RZy5DX: { ] pK:Uu' `(_~6TuBHD%gݖf݈_71K0jH|L{&3I=荀8)Uɒv2;?\ ?k!CQO]d !~D T(KPiN< /OAC O0>g,-I>z#~6d=vbFߡ;V}-tzoNC, M=_P~pjQc|x t:!jf`:^]_<>jVQsLmï{!Ӽ?X=eJN͂=U[a{%)Am˜ƬU/[V0 AY\xwTٶoP  PťjIƚzD ˪4@OЏL5^-0_BRWr[-XA1  jta3_X Pc==W,cTP7UC==YyGԓ?#u`4vaj}ʧ(lQA!Ju4\.e r{h,9VѰpc_پ \@ۍOU脀WϥIENDB`cppcheck-1.90/snap/snapcraft.yaml000066400000000000000000000030671357737443600170620ustar00rootroot00000000000000name: cppcheckgui version: '1.81.99' summary: A tool for static C/C++ code analysis description: | A tool for static C/C++ code analysis grade: stable confinement: strict icon: snap/gui/cppcheck-gui.png type: app apps: cppcheckgui: command: desktop-launch ${SNAP}/bin/cppcheck-gui plugs: [home, unity7, x11, network-bind, network-control] parts: cppcheckgui: source-type: git plugin: cmake configflags: - -DBUILD_GUI=ON after: [desktop-qt5] build-packages: # A list of Ubuntu packages to be installed on the host to aid in building the part. # These packages will not go into the final snap. - build-essential - qt5-default - qtbase5-dev - dpkg-dev # For Qt5LinguistTools - qttools5-dev - qttools5-dev-tools desktop-qt5: stage-packages: # A set of Ubuntu packages to be downloaded and unpacked to join the part before it’s built. # Note that these packages are not installed on the host. # Like the rest of the part, all files from these packages will make it into the final snap unless filtered out via the prime keyword. - libqt5gui5 - libqt5svg5 # for loading icon themes which are svg - libtiff5-dev - libjpeg8-dev - libxkbcommon0 - ttf-ubuntu-font-family - dmz-cursor-theme - light-themes - shared-mime-info - libgdk-pixbuf2.0-0 - locales-all - xcb - libxcb1 cppcheck-1.90/test/000077500000000000000000000000001357737443600142255ustar00rootroot00000000000000cppcheck-1.90/test/CMakeLists.txt000066400000000000000000000104671357737443600167750ustar00rootroot00000000000000if (BUILD_TESTS) cmake_policy(SET CMP0064 NEW) cmake_policy(SET CMP0057 NEW) include(CTest) find_package(Threads REQUIRED) include(ProcessorCount) ProcessorCount(N) set(CTEST_PARALLEL_LEVEL ${N} CACHE STRING "CTest parallel level") add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -j ${CTEST_PARALLEL_LEVEL} -C ${CMAKE_CFG_INTDIR} --timeout 90) include_directories(${PROJECT_SOURCE_DIR}/lib/ ${PROJECT_SOURCE_DIR}/cli/) include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/externals/tinyxml) include_directories(${PROJECT_SOURCE_DIR}/externals/simplecpp/) file(GLOB hdrs "*.h") file(GLOB srcs "*.cpp") add_executable(testrunner ${hdrs} ${srcs} $ $ $ $) if (HAVE_RULES) target_link_libraries(testrunner pcre) endif() add_custom_target(copy_cfg ALL COMMENT "Copying cfg files") add_custom_command( TARGET copy_cfg COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/cfg" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/cfg") if (WIN32 AND NOT BORLAND) target_link_libraries(testrunner Shlwapi.lib) endif() add_dependencies(testrunner copy_cfg) add_dependencies(check testrunner cppcheck) set(SKIP_TESTS "" CACHE STRING "A list of tests to skip") function(add_fixture NAME) if (${NAME} IN_LIST SKIP_TESTS) else() add_test(NAME ${NAME} COMMAND testrunner ${NAME} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) endif() endfunction() function(fixture_cost NAME COST) if(TEST ${NAME}) set_tests_properties(${NAME} PROPERTIES COST ${COST}) endif() endfunction() foreach(SRC ${srcs}) file(STRINGS ${SRC} FIXTURE_LINE REGEX "TestFixture\\(" LIMIT_COUNT 1) if(FIXTURE_LINE MATCHES "TestFixture\\(\"([a-zA-z0-9]+)\"\\)") add_fixture(${CMAKE_MATCH_1}) endif() endforeach() function(add_cfg CFG_TEST) set(options INCONCLUSIVE) set(oneValueArgs PLATFORM NAME) set(multiValueArgs LIBRARY) cmake_parse_arguments(PARSE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(PARSE_LIBRARY) string(REPLACE ";" "," LIBRARY "${PARSE_LIBRARY}") else() get_filename_component(LIBRARY ${CFG_TEST} NAME_WE) endif() set(PLATFORM unix64) if(PARSE_PLATFORM) set(PLATFORM ${PARSE_PLATFORM}) endif() if(PARSE_NAME) set(TEST_NAME ${PARSE_NAME}) else() string(MAKE_C_IDENTIFIER ${CFG_TEST} TEST_NAME) endif() set(INCONCLUSIVE) if(PARSE_INCONCLUSIVE) set(INCONCLUSIVE "--inconclusive") endif() if (${TEST_NAME} IN_LIST SKIP_TESTS) else() add_test(NAME cfg-${TEST_NAME} COMMAND $ --check-library --platform=${PLATFORM} --library=${LIBRARY} --enable=information --enable=style --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr ${INCONCLUSIVE} ${CMAKE_CURRENT_SOURCE_DIR}/cfg/${CFG_TEST} ) endif() endfunction() add_cfg(boost.cpp INCONCLUSIVE) add_cfg(bsd.c) add_cfg(gnu.c LIBRARY posix gnu) add_cfg(googletest.cpp) add_cfg(gtk.c) add_cfg(libcurl.c) add_cfg(lua.c) add_cfg(openmp.c) add_cfg(posix.c) add_cfg(python.c) add_cfg(qt.cpp) add_cfg(sqlite3.c INCONCLUSIVE) add_cfg(std.c INCONCLUSIVE) add_cfg(std.cpp INCONCLUSIVE) add_cfg(windows.cpp INCONCLUSIVE NAME windows32A PLATFORM win32A) add_cfg(windows.cpp INCONCLUSIVE NAME windows32W PLATFORM win32W) add_cfg(windows.cpp INCONCLUSIVE NAME windows64 PLATFORM win64) add_cfg(wxwidgets.cpp INCONCLUSIVE) # Set cost of the more expensive tests to help improve parallel scheduling # of tests fixture_cost(TestIO 20) fixture_cost(cfg-std_c 8) fixture_cost(TestThreadExecutor 5) fixture_cost(TestLeakAutoVar 4) fixture_cost(TestTokenizer 4) endif() cppcheck-1.90/test/cfg/000077500000000000000000000000001357737443600147645ustar00rootroot00000000000000cppcheck-1.90/test/cfg/boost.cpp000066400000000000000000000030631357737443600166200ustar00rootroot00000000000000 // Test library configuration for boost.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/boost.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include BOOST_FORCEINLINE void boost_forceinline_test() { } BOOST_NOINLINE void boost_noinline_test() { } BOOST_NORETURN void boost_noreturn_test() { } void print_hello() { printf("hello"); } void valid_code(boost::function &pf_print_hello) { if (BOOST_LIKELY(1)) { } if (BOOST_UNLIKELY(0)) { } int int1 = 5; boost::endian::endian_reverse_inplace(int1); boost::bind(print_hello)(); pf_print_hello = boost::bind(print_hello); } void ignoredReturnValue(char * buf) { // cppcheck-suppress ignoredReturnValue boost::math::round(1.5); // cppcheck-suppress ignoredReturnValue boost::math::iround(1.5); // cppcheck-suppress ignoredReturnValue boost::math::lround(1.5); // cppcheck-suppress ignoredReturnValue boost::math::llround(1.5); // cppcheck-suppress ignoredReturnValue boost::endian::endian_reverse(1); } void uninitvar() { int intUninit1; int intUninit2; // cppcheck-suppress uninitvar boost::endian::endian_reverse_inplace(intUninit1); // cppcheck-suppress uninitvar (void)boost::math::round(intUninit2); } cppcheck-1.90/test/cfg/bsd.c000066400000000000000000000044601357737443600157040ustar00rootroot00000000000000// Test library configuration for bsd.cfg // // Usage: // $ cppcheck --library=bsd --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/bsd.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include // #9323, #9331 void verify_timercmp(struct timeval t) { (void)timercmp(&t, &t, <); (void)timercmp(&t, &t, <=); (void)timercmp(&t, &t, ==); (void)timercmp(&t, &t, !=); (void)timercmp(&t, &t, >=); (void)timercmp(&t, &t, >); } // False negative: #9346 void uninitvar_timercmp(struct timeval t) { struct timeval uninit; (void)timercmp(&t, &uninit, <); (void)timercmp(&uninit, &t, <=); (void)timercmp(&uninit, &uninit, ==); } void nullPointer_timercmp(struct timeval t) { struct timeval *p=0; // cppcheck-suppress nullPointer (void)timercmp(&t, p, <); // cppcheck-suppress nullPointer (void)timercmp(p, &t, <=); // cppcheck-suppress nullPointer (void)timercmp(p, p, ==); } // size_t strlcat(char *dst, const char *src, size_t size); void uninitvar_strlcat(char *Ct, const char *S, size_t N) { char *ct; char *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)strlcat(ct,s,n1); // cppcheck-suppress uninitvar (void)strlcat(ct,S,N); // cppcheck-suppress uninitvar (void)strlcat(Ct,s,N); // cppcheck-suppress uninitvar (void)strlcat(Ct,S,n2); // no warning is expected for (void)strlcat(Ct,S,N); } void bufferAccessOutOfBounds(void) { uint16_t uint16Buf[4]; // cppcheck-suppress bufferAccessOutOfBounds arc4random_buf(uint16Buf, 9); // valid arc4random_buf(uint16Buf, 8); } void ignoredReturnValue(void) { // cppcheck-suppress ignoredReturnValue arc4random(); // cppcheck-suppress ignoredReturnValue arc4random_uniform(10); } void invalidFunctionArg() { // cppcheck-suppress invalidFunctionArg (void) arc4random_uniform(1); // valid (void) arc4random_uniform(2); } void nullPointer(void) { // cppcheck-suppress nullPointer arc4random_buf(NULL, 5); } void uninitvar(void) { uint32_t uint32Uninit; // cppcheck-suppress uninitvar (void) arc4random_uniform(uint32Uninit); } cppcheck-1.90/test/cfg/cairo.c000066400000000000000000000013411357737443600162240ustar00rootroot00000000000000 // Test library configuration for cairo.cfg // // Usage: // $ cppcheck --check-library --library=cairo --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/cairo.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include void validCode(cairo_surface_t *target) { cairo_t * cairo1 = cairo_create(target); cairo_move_to(cairo1, 1.0, 2.0); cairo_line_to(cairo1, 5.0, 6.0); cairo_destroy(cairo1); } void ignoredReturnValue(cairo_surface_t *target) { // cppcheck-suppress ignoredReturnValue cairo_create(target); // cppcheck-suppress ignoredReturnValue cairo_status_to_string(CAIRO_STATUS_READ_ERROR); } cppcheck-1.90/test/cfg/generate-cfg-tests.sh000077500000000000000000000015461357737443600210200ustar00rootroot00000000000000#!/bin/bash cd ~/cppcheck make generate_cfg_tests ./generate_cfg_tests cfg/avr.cfg > test/cfg/generated-cfg-tests-avr.cpp ./generate_cfg_tests cfg/bsd.cfg > test/cfg/generated-cfg-tests-bsd.cpp ./generate_cfg_tests cfg/gnu.cfg > test/cfg/generated-cfg-tests-gnu.cpp ./generate_cfg_tests cfg/motif.cfg > test/cfg/generated-cfg-tests-motif.cpp ./generate_cfg_tests cfg/posix.cfg > test/cfg/generated-cfg-tests-posix.cpp ./generate_cfg_tests cfg/qt.cfg > test/cfg/generated-cfg-tests-qt.cpp ./generate_cfg_tests cfg/sdl.cfg > test/cfg/generated-cfg-tests-sdl.cpp ./generate_cfg_tests cfg/sfml.cfg > test/cfg/generated-cfg-tests-sfml.cpp ./generate_cfg_tests cfg/std.cfg > test/cfg/generated-cfg-tests-std.cpp ./generate_cfg_tests cfg/windows.cfg > test/cfg/generated-cfg-tests-windows.cpp ./generate_cfg_tests cfg/wxwidgets.cfg > test/cfg/generated-cfg-tests-wxwidgets.cpp cppcheck-1.90/test/cfg/gnu.c000066400000000000000000000150741357737443600157300ustar00rootroot00000000000000 // Test library configuration for gnu.cfg // // Usage: // $ cppcheck --check-library --library=gnu --enable=information --enable=style --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/gnu.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include #include #ifndef __CYGWIN__ #include #endif // #9323, #9331 void syntaxError_timercmp(struct timeval t) { (void)timercmp(&t, &t, <); (void)timercmp(&t, &t, <=); (void)timercmp(&t, &t, ==); (void)timercmp(&t, &t, !=); (void)timercmp(&t, &t, >=); (void)timercmp(&t, &t, >); } // False negative: #9346 void uninitvar_timercmp(struct timeval t) { struct timeval uninit; (void)timercmp(&t, &uninit, <); (void)timercmp(&uninit, &t, <=); (void)timercmp(&uninit, &uninit, ==); } void nullPointer_timercmp(struct timeval t) { struct timeval *p=0; // cppcheck-suppress nullPointer (void)timercmp(&t, p, <); // cppcheck-suppress nullPointer (void)timercmp(p, &t, <=); // cppcheck-suppress nullPointer (void)timercmp(p, p, ==); } // Declaration necessary because there is no specific / portable header. extern void *xcalloc(size_t nmemb, size_t size); extern void *xmalloc(size_t size); extern void *xrealloc(void *block, size_t newsize); extern void xfree(void *ptr); void resourceLeak_mkostemps(char *template, int suffixlen, int flags) { // cppcheck-suppress unreadVariable int fp = mkostemps(template, suffixlen, flags); // cppcheck-suppress resourceLeak } void no_resourceLeak_mkostemps_01(char *template, int suffixlen, int flags) { int fp = mkostemps(template, suffixlen, flags); close(fp); } int no_resourceLeak_mkostemps_02(char *template, int suffixlen, int flags) { return mkostemps(template, suffixlen, flags); } void resourceLeak_mkstemps(char *template, int suffixlen) { // cppcheck-suppress unreadVariable int fp = mkstemps(template, suffixlen); // cppcheck-suppress resourceLeak } void no_resourceLeak_mkstemps_01(char *template, int suffixlen) { int fp = mkstemps(template, suffixlen); close(fp); } int no_resourceLeak_mkstemps_02(char *template, int suffixlen) { return mkstemps(template, suffixlen); } void resourceLeak_mkostemp(char *template, int flags) { // cppcheck-suppress unreadVariable int fp = mkostemp(template, flags); // cppcheck-suppress resourceLeak } void no_resourceLeak_mkostemp_01(char *template, int flags) { int fp = mkostemp(template, flags); close(fp); } int no_resourceLeak_mkostemp_02(char *template, int flags) { return mkostemp(template, flags); } void valid_code(int argInt1, va_list valist_arg, int * parg) { char *p; if (__builtin_expect(argInt1, 0)) {} if (__builtin_expect_with_probability(argInt1 + 1, 2, 0.5)) {} if (__glibc_unlikely(argInt1 != 0)) {} if (__glibc_likely(parg != NULL)) {} void *ax1 = __builtin_assume_aligned(parg, 16); printf("%p", ax1); void *ax2 = __builtin_assume_aligned(parg, 32, 8); printf("%p", ax2); p = (char *)malloc(10); free(p); p = (char *)malloc(5); xfree(p); p = (char *)xmalloc(10); free(p); p = (char *)xmalloc(5); xfree(p); // cppcheck-suppress allocaCalled p = __builtin_alloca(5); p[0] = 1; // TODO cppcheck-suppress arrayIndexOutOfBounds p[5] = 1; __builtin_prefetch(p, 0, 1); if (__builtin_types_compatible_p(int, char)) {} char * pStr = NULL; if (vasprintf(&pStr, "%d %d", valist_arg) != -1) { free(pStr); } printf("%d", 0b010); printf("%d", __extension__ 0b10001000); if (__alignof__(int) == 4) {} void * p_mmap = mmap(NULL, 1, PROT_NONE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); printf("%p", p_mmap); munmap(p_mmap, 1); } void ignoreleak(void) { char *p = (char *)malloc(10); __builtin_memset(&(p[0]), 0, 10); // cppcheck-suppress memleak } void memleak_asprintf(char **ptr, const char *fmt, const int arg) { // No warning is expected for if (-1 != asprintf(ptr,fmt,arg)) { free(ptr); } if (-1 != asprintf(ptr,fmt,arg)) { // TODO: Related to #8980 cppcheck-suppress memleak } } void memleak_xmalloc() { char *p = (char*)xmalloc(10); p[9] = 0; // cppcheck-suppress memleak } void memleak_mmap() { void * p_mmap = mmap(NULL, 1, PROT_NONE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); printf("%p", p_mmap); // cppcheck-suppress memleak } void uninitvar__builtin_memset(void) { void *s; int c; size_t n; // cppcheck-suppress uninitvar (void)__builtin_memset(s,c,n); } void bufferAccessOutOfBounds__builtin_memset(void) { uint8_t buf[42]; // cppcheck-suppress bufferAccessOutOfBounds (void)__builtin_memset(buf,0,1000); } void bufferAccessOutOfBounds() { char buf[2] = "a"; // This is valid sethostname(buf, 2); // cppcheck-suppress bufferAccessOutOfBounds sethostname(buf, 4); char * pAlloc1 = xcalloc(2, 4); memset(pAlloc1, 0, 8); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc1, 0, 9); free(pAlloc1); char * pAlloc2 = xmalloc(4); memset(pAlloc2, 0, 4); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc2, 0, 5); pAlloc2 = xrealloc(pAlloc2, 10); memset(pAlloc2, 0, 10); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc2, 0, 11); free(pAlloc2); } void leakReturnValNotUsed() { // cppcheck-suppress unreadVariable char* ptr = (char*)strdupa("test"); // cppcheck-suppress ignoredReturnValue strdupa("test"); // cppcheck-suppress unreadVariable char* ptr2 = (char*)strndupa("test", 1); // cppcheck-suppress ignoredReturnValue strndupa("test", 1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcasestr("test", NULL); // FIXME cppcheck-suppress knownConditionTrueFalse // cppcheck-suppress duplicateExpression if (42 == __builtin_expect(42, 0)) return; } #ifndef __CYGWIN__ int nullPointer_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) { // no warning is expected (void)epoll_ctl(epfd, op, fd, event); // No nullpointer warning is expected in case op is set to EPOLL_CTL_DEL // EPOLL_CTL_DEL // Remove (deregister) the target file descriptor fd from the // epoll instance referred to by epfd. The event is ignored and // can be NULL. return epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); } #endif cppcheck-1.90/test/cfg/googletest.cpp000066400000000000000000000014261357737443600176470ustar00rootroot00000000000000 // Test library configuration for googletest.cfg // // Usage: // $ cppcheck --check-library --library=googletest --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/googletest.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include // #9397 syntaxError when MATCHER_P is not known namespace ExampleNamespace { constexpr long long TOLERANCE = 10; MATCHER_P(ExampleMatcherTest, expected, "") { return ((arg <= (expected + TOLERANCE)) && (arg >= (expected - TOLERANCE))); } } TEST(ASSERT, ASSERT) { int *a = (int*)calloc(10,sizeof(int)); ASSERT_TRUE(a != nullptr); a[0] = 10; free(a); } cppcheck-1.90/test/cfg/gtk.c000066400000000000000000000212371357737443600157220ustar00rootroot00000000000000 // Test library configuration for gtk.cfg // // Usage: // $ cppcheck --check-library --enable=information --inconclusive --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr --library=gtk test/cfg/gtk.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode(int argInt) { // if G_UNLIKELY is not defined this results in a syntax error if G_UNLIKELY(argInt == 1) { } else if (G_UNLIKELY(argInt == 2)) { } if G_LIKELY(argInt == 0) { } else if (G_LIKELY(argInt == -1)) { } printf("%s", _("test")); printf("%s", Q_("a|test")); printf("%s", N_("test")); gpointer gpt = g_malloc(4); printf("%p", gpt); g_free(gpt); g_assert(gpt); if (!gpt) { // cppcheck-suppress checkLibraryNoReturn g_assert_not_reached(); } gpointer p = GINT_TO_POINTER(1); int i = GPOINTER_TO_INT(p); // cppcheck-suppress knownConditionTrueFalse if (i == 1) {} g_print("test"); g_print("%d", 1); g_printerr("err"); GString * pGStr1 = g_string_new("test"); g_string_append(pGStr1, "a"); g_string_free(pGStr1, TRUE); gchar * pGchar1 = g_strconcat("a", "b", NULL); printf("%s", pGchar1); g_free(pGchar1); GError * pGerror = g_error_new(1, -2, "a %d", 1); g_error_free(pGerror); static gsize init_val = 0; if (g_once_init_enter(&init_val)) { gsize result_val = 1; g_once_init_leave(&init_val, result_val); } } void g_malloc_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_malloc(8); gpointer gpt = g_malloc(1); printf("%p", gpt); // cppcheck-suppress memleak } void g_malloc0_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_malloc0(8); gpointer gpt = g_malloc0(1); printf("%p", gpt); // cppcheck-suppress memleak } void g_malloc_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_malloc_n(8, 1); gpointer gpt = g_malloc_n(1, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_malloc0_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_malloc0_n(8, 1); gpointer gpt = g_malloc0_n(1, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_malloc_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_malloc(8); gpointer gpt = g_try_malloc(1); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_malloc0_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_malloc0(8); gpointer gpt = g_try_malloc0(1); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_malloc_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_malloc_n(8, 1); gpointer gpt = g_try_malloc_n(1, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_malloc0_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_malloc0_n(8, 1); gpointer gpt = g_try_malloc0_n(1, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_realloc_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_realloc(NULL, 1); gpointer gpt = g_malloc(1); gpt = g_realloc(gpt, 2); // No memleakOnRealloc since g_realloc aborts if it fails printf("%p", gpt); // cppcheck-suppress memleak } void g_realloc_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_realloc_n(NULL, 1, 2); gpointer gpt = g_malloc_n(1, 2); gpt = g_realloc_n(gpt, 2, 3); // No memleakOnRealloc since g_realloc_n aborts if it fails printf("%p", gpt); // cppcheck-suppress memleak } void g_try_realloc_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_realloc(NULL, 1); gpointer gpt = g_try_malloc(1); // cppcheck-suppress memleakOnRealloc gpt = g_try_realloc(gpt, 2); printf("%p", gpt); // cppcheck-suppress memleak } void g_try_realloc_n_test() { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_try_realloc_n(NULL, 1, 2); gpointer gpt = g_try_malloc_n(1, 2); // cppcheck-suppress memleakOnRealloc gpt = g_try_realloc_n(gpt, 2, 3); printf("%p", gpt); // cppcheck-suppress memleak } void g_assert_test() { int a; // cppcheck-suppress checkLibraryNoReturn // cppcheck-suppress assignmentInAssert g_assert(a = 5); } void g_print_test() { // cppcheck-suppress invalidPrintfArgType_uint g_print("%u", -1); // cppcheck-suppress invalidPrintfArgType_uint g_printerr("%x", "a"); } void g_alloca_test() { // cppcheck-suppress allocaCalled char * pBuf1 = g_alloca(5); pBuf1[0] = '\0'; } void g_new_test() { struct a { int b; }; // valid struct a * pNew1 = g_new(struct a, 5); printf("%p", pNew1); g_free(pNew1); // cppcheck-suppress leakReturnValNotUsed g_new(struct a, 1); struct a * pNew2 = g_new(struct a, 2); printf("%p", pNew2); // cppcheck-suppress memleak } void g_new_if_test() { struct a { int b; }; struct a * pNew3; if (pNew3 = g_new(struct a, 6)) { printf("%p", pNew3); } // cppcheck-suppress memleak } void g_new0_test() { struct a { int b; }; // valid struct a * pNew1 = g_new0(struct a, 5); printf("%p", pNew1); g_free(pNew1); // cppcheck-suppress leakReturnValNotUsed g_new0(struct a, 1); struct a * pNew2 = g_new0(struct a, 2); printf("%p", pNew2); // cppcheck-suppress memleak } void g_try_new_test() { struct a { int b; }; // valid struct a * pNew1 = g_try_new(struct a, 5); printf("%p", pNew1); g_free(pNew1); // cppcheck-suppress leakReturnValNotUsed g_try_new(struct a, 1); struct a * pNew2 = g_try_new(struct a, 2); printf("%p", pNew2); // cppcheck-suppress memleak } void g_try_new0_test() { struct a { int b; }; // valid struct a * pNew1 = g_try_new0(struct a, 5); printf("%p", pNew1); g_free(pNew1); // cppcheck-suppress leakReturnValNotUsed g_try_new0(struct a, 1); struct a * pNew2 = g_try_new0(struct a, 2); printf("%p", pNew2); // cppcheck-suppress memleak } void g_renew_test() { struct a { int b; }; // cppcheck-suppress leakReturnValNotUsed g_renew(struct a, NULL, 1); struct a * pNew = g_new(struct a, 1); pNew = g_renew(struct a, pNew, 2); // No memleakOnRealloc since g_renew aborts if it fails printf("%p", pNew); // cppcheck-suppress memleak } void g_try_renew_test() { struct a { int b; }; // cppcheck-suppress leakReturnValNotUsed g_try_renew(struct a, NULL, 1); struct a * pNew = g_try_new(struct a, 1); // cppcheck-suppress memleakOnRealloc pNew = g_try_renew(struct a, pNew, 2); printf("%p", pNew); // cppcheck-suppress memleak } void g_error_new_test() { // valid GError * pNew1 = g_error_new(1, -2, "a %d", 1); printf("%p", pNew1); g_error_free(pNew1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed g_error_new(1, -2, "a %d", 1); GError * pNew2 = g_error_new(1, -2, "a %d", 1); printf("%p", pNew2); // cppcheck-suppress memleak } void g_once_init_enter_leave_test() { static gsize init_val; if (g_once_init_enter(&init_val)) { gsize result_val = 0; // cppcheck-suppress invalidFunctionArg g_once_init_leave(&init_val, result_val); } gsize init_val2; // cppcheck-suppress uninitvar // cppcheck-suppress ignoredReturnValue g_once_init_enter(&init_val2); gsize * init_val3 = NULL; // cppcheck-suppress nullPointer if (g_once_init_enter(init_val3)) { // cppcheck-suppress nullPointer g_once_init_leave(init_val3, 1); } gsize * init_val4; // cppcheck-suppress uninitvar if (g_once_init_enter(init_val4)) { // cppcheck-suppress uninitvar g_once_init_leave(init_val4, 1); } } void g_strchug_g_strchomp_test(gchar * str1) { g_strchug(str1); g_strchomp(str1); g_strchug(g_strchomp(str1)); gchar * str2; // cppcheck-suppress uninitvar g_strchug(str2); gchar * str3; // cppcheck-suppress uninitvar g_strchomp(str3); } cppcheck-1.90/test/cfg/kde.cpp000066400000000000000000000016711357737443600162400ustar00rootroot00000000000000 // Test library configuration for kde.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/kde.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include class k_global_static_testclass1 {}; K_GLOBAL_STATIC(k_global_static_testclass1, k_global_static_testinstance1); void valid_code(KConfigGroup &cfgGroup) { k_global_static_testclass1 * pk_global_static_testclass1 = k_global_static_testinstance1; printf("%p", pk_global_static_testclass1); bool entryTest = cfgGroup.readEntry("test", false); if (entryTest) {} } void ignoredReturnValue(KConfigGroup & cfgGroup) { // cppcheck-suppress ignoredReturnValue cfgGroup.readEntry("test", "default"); // cppcheck-suppress ignoredReturnValue cfgGroup.readEntry("test"); } cppcheck-1.90/test/cfg/libcurl.c000066400000000000000000000041701357737443600165660ustar00rootroot00000000000000 // Test library configuration for libcurl.cfg // // Usage: // $ cppcheck --check-library --library=libcurl --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/libcurl.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode() { CURL *curl = curl_easy_init(); if (curl) { CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); res = curl_easy_perform(curl); if (res != CURLE_OK) { printf("error"); } else { long response_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); printf("%ld", response_code); char * pStr = curl_easy_escape(curl, "a", 1); if (pStr) printf("%s", pStr); curl_free(pStr); curl_easy_reset(curl); } curl_easy_cleanup(curl); } } void ignoredReturnValue(CURL * handle) { // cppcheck-suppress ignoredReturnValue curl_easy_strerror(1); } void resourceLeak_curl_easy_init() { CURL *curl = curl_easy_init(); printf("%p", curl); // cppcheck-suppress resourceLeak } void resourceLeak_curl_easy_duphandle(CURL * handle) { CURL *curl = curl_easy_duphandle(handle); printf("%p", curl); // cppcheck-suppress resourceLeak } void memleak_curl_easy_escape(CURL * handle) { char * pStr = curl_easy_escape(handle, "a", 1); if (pStr) printf("%s", pStr); // cppcheck-suppress memleak } void nullPointer(CURL * handle) { char * buf[10] = {0}; size_t len; curl_easy_recv(handle, buf, 10, &len); // cppcheck-suppress nullPointer curl_easy_recv(handle, buf, 10, NULL); curl_easy_send(handle, buf, 10, &len); // cppcheck-suppress nullPointer curl_easy_send(handle, buf, 10, NULL); } void uninitvar(CURL * handle) { char * bufInit[10] = {0}; char * bufUninit; size_t len; curl_easy_send(handle, bufInit, 10, &len); // cppcheck-suppress uninitvar curl_easy_send(handle, bufUninit, 10, &len); } cppcheck-1.90/test/cfg/libsigc++.cpp000066400000000000000000000012661357737443600172370ustar00rootroot00000000000000 // Test library configuration for libsigc++.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/libsigc++.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include struct struct1 : public sigc::trackable { void func1(int) const {} }; void valid_code() { const struct1 my_sruct1; sigc::slot sl = sigc::mem_fun(my_sruct1, &struct1::func1); if (sl) {} } void ignoredReturnValue() { const struct1 my_sruct1; // cppcheck-suppress ignoredReturnValue sigc::mem_fun(my_sruct1, &struct1::func1); } cppcheck-1.90/test/cfg/lua.c000066400000000000000000000014021357737443600157060ustar00rootroot00000000000000 // Test library configuration for lua.cfg // // Usage: // $ cppcheck --check-library --library=lua --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/lua.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode(lua_State *L) { int a = lua_gettop(L); printf("%d", a); lua_pushnil(L); lua_pop(L, 1); } void ignoredReturnValue(lua_State *L) { // cppcheck-suppress ignoredReturnValue lua_tonumber(L, 1); // cppcheck-suppress ignoredReturnValue lua_tostring(L, 1); // cppcheck-suppress ignoredReturnValue lua_isboolean(L, 1); // cppcheck-suppress ignoredReturnValue lua_isnil(L, 1); } cppcheck-1.90/test/cfg/opencv2.cpp000066400000000000000000000015161357737443600170470ustar00rootroot00000000000000 // Test library configuration for opencv2.cfg // // Usage: // $ cppcheck --check-library --library=cairo --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/opencv2.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode(char* argStr) { cv::Mat image; image = cv::imread(argStr, cv::IMREAD_COLOR); if (!image.data) { printf("No image data \n"); return; } cv::namedWindow("Display Image", cv::WINDOW_AUTOSIZE); cv::imshow("Display Image", image); cv::waitKey(0); cv::String cvStr("Hello"); cvStr += " World"; std::cout << cvStr; } void ignoredReturnValue() { // cppcheck-suppress ignoredReturnValue cv::imread("42.png"); } cppcheck-1.90/test/cfg/openmp.c000066400000000000000000000013731357737443600164320ustar00rootroot00000000000000 // Test library configuration for openmp.cfg // // Usage: // $ cppcheck --check-library --library=openmp --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/openmp.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode() { int arr[20] = { 0 }; #pragma omp parallel for for (int i = 0; i < 20; ++i) { arr[i] = i * i; } char * pChars = (char *) omp_target_alloc(4, 1); printf("pChars: %p", pChars); omp_target_free(pChars, 1); } void memleak_omp_target_alloc() { char * pChars = (char *) omp_target_alloc(2, 0); printf("pChars: %p", pChars); // cppcheck-suppress memleak } cppcheck-1.90/test/cfg/openssl.c000066400000000000000000000033211357737443600166120ustar00rootroot00000000000000 // Test library configuration for openssl.cfg // // Usage: // $ cppcheck --check-library --library=openssl --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/openssl.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include void valid_code(BIO * bio) { BIO_printf(bio, "%d\n", 1); } // Example for encrypting a string using IDEA (from https://www.openssl.org/docs/man1.1.1/man3/EVP_CIPHER_CTX_new.html) int valid_code_do_crypt(char *outfile) { unsigned char outbuf[1024]; int outlen, tmplen; unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; unsigned char iv[] = {1,2,3,4,5,6,7,8}; char intext[] = "Some Crypto Text"; EVP_CIPHER_CTX *ctx; FILE *out; ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); if (!EVP_EncryptUpdate(ctx, outbuf, &outlen, intext, strlen(intext))) { /* Error */ EVP_CIPHER_CTX_free(ctx); return 0; } if (!EVP_EncryptFinal_ex(ctx, outbuf + outlen, &tmplen)) { /* Error */ EVP_CIPHER_CTX_free(ctx); return 0; } outlen += tmplen; EVP_CIPHER_CTX_free(ctx); out = fopen(outfile, "wb"); if (out == NULL) { /* Error */ return 0; } fwrite(outbuf, 1, outlen, out); fclose(out); return 1; } void invalidPrintfArgType_test(BIO * bio) { // cppcheck-suppress invalidPrintfArgType_sint BIO_printf(bio, "%d\n", 5U); } void EVP_CIPHER_CTX_new_test() { EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new(); printf("%p", ctx); // cppcheck-suppress resourceLeak } cppcheck-1.90/test/cfg/posix.c000066400000000000000000000322211357737443600162720ustar00rootroot00000000000000 // Test library configuration for posix.cfg // // Usage: // $ cppcheck --check-library --library=posix --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/posix.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include // <- FILE #include #include #include #include #include // unavailable on some linux systems #include #include #include #include #include #include #include #include void uninitvar_putenv(char * envstr) { // No warning is expected (void)putenv(envstr); char * p; // cppcheck-suppress uninitvar (void)putenv(p); } void nullPointer_putenv(char * envstr) { // No warning is expected (void)putenv(envstr); char * p=NULL; // cppcheck-suppress nullPointer (void)putenv(p); } void memleak_scandir(void) { struct dirent **namelist; int n = scandir(".", &namelist, NULL, alphasort); if (n == -1) { return; } // http://man7.org/linux/man-pages/man3/scandir.3.html /* The scandir() function scans the directory dirp, calling filter() on each directory entry. Entries for which filter() returns nonzero are stored in strings allocated via malloc(3), sorted using qsort(3) with the comparison function compar(), and collected in array namelist which is allocated via malloc(3). If filter is NULL, all entries are selected.*/ // TODO: cppcheck-suppress memleak } void no_memleak_scandir(void) { struct dirent **namelist; int n = scandir(".", &namelist, NULL, alphasort); if (n == -1) { return; } while (n--) { free(namelist[n]); } free(namelist); } void validCode(va_list valist_arg1, va_list valist_arg2) { void *ptr; if (posix_memalign(&ptr, sizeof(void *), sizeof(void *)) == 0) free(ptr); syslog(LOG_ERR, "err %u", 0U); syslog(LOG_WARNING, "warn %d %d", 5, 1); vsyslog(LOG_EMERG, "emerg %d", valist_arg1); vsyslog(LOG_INFO, "test %s %d %p", valist_arg2); void* handle = dlopen("/lib.so", RTLD_NOW); if (handle) { dlclose(handle); } } void bufferAccessOutOfBounds(int fd) { char a[5]; read(fd,a,5); // cppcheck-suppress bufferAccessOutOfBounds read(fd,a,6); write(fd,a,5); // cppcheck-suppress bufferAccessOutOfBounds write(fd,a,6); recv(fd,a,5,0); // cppcheck-suppress bufferAccessOutOfBounds recv(fd,a,6,0); recvfrom(fd,a,5,0,0x0,0x0); // cppcheck-suppress bufferAccessOutOfBounds recvfrom(fd,a,6,0,0x0,0x0); send(fd,a,5,0); // cppcheck-suppress bufferAccessOutOfBounds send(fd,a,6,0); sendto(fd,a,5,0,0x0,0x0); // cppcheck-suppress bufferAccessOutOfBounds sendto(fd,a,6,0,0x0,0x0); // cppcheck-suppress constStatement 0; readlink("path", a, 5); // cppcheck-suppress bufferAccessOutOfBounds readlink("path", a, 6); readlinkat(1, "path", a, 5); // cppcheck-suppress bufferAccessOutOfBounds readlinkat(1, "path", a, 6); // This is valid gethostname(a, 5); // cppcheck-suppress bufferAccessOutOfBounds gethostname(a, 6); } void nullPointer(char *p, int fd, pthread_mutex_t mutex) { // cppcheck-suppress ignoredReturnValue isatty(0); mkdir(p, 0); getcwd(0, 0); // cppcheck-suppress nullPointer // cppcheck-suppress readdirCalled readdir(0); // cppcheck-suppress nullPointer // cppcheck-suppress utimeCalled utime(NULL, NULL); // not implemented yet: cppcheck-suppress nullPointer read(fd,NULL,42); read(fd,NULL,0); // not implemented yet: cppcheck-suppress nullPointer write(fd,NULL,42); write(fd,NULL,0); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer open(NULL, 0); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer open(NULL, 0, 0); // cppcheck-suppress unreadVariable // cppcheck-suppress nullPointer int ret = access(NULL, 0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer fdopen(fd, NULL); // cppcheck-suppress strtokCalled // cppcheck-suppress nullPointer strtok(p, NULL); // cppcheck-suppress nullPointer pthread_mutex_init(NULL, NULL); // Second argument can be NULL pthread_mutex_init(&mutex, NULL); // cppcheck-suppress nullPointer pthread_mutex_destroy(NULL); // cppcheck-suppress nullPointer pthread_mutex_lock(NULL); // cppcheck-suppress nullPointer (void)pthread_mutex_trylock(NULL); // cppcheck-suppress nullPointer pthread_mutex_unlock(NULL); } void memleak_getaddrinfo() { //TODO: nothing to report yet, see http://sourceforge.net/p/cppcheck/discussion/general/thread/d9737d5d/ struct addrinfo * res=NULL; getaddrinfo("node", NULL, NULL, &res); freeaddrinfo(res); } void memleak_mmap(int fd) { // cppcheck-suppress unreadVariable void *addr = mmap(NULL, 255, PROT_NONE, MAP_PRIVATE, fd, 0); // cppcheck-suppress memleak } void resourceLeak_fdopen(int fd) { // cppcheck-suppress unreadVariable FILE *f = fdopen(fd, "r"); // cppcheck-suppress resourceLeak } void resourceLeak_mkstemp(char *template) { // cppcheck-suppress unreadVariable int fp = mkstemp(template); // cppcheck-suppress resourceLeak } void no_resourceLeak_mkstemp_01(char *template) { int fp = mkstemp(template); close(fp); } int no_resourceLeak_mkstemp_02(char *template) { return mkstemp(template); } void resourceLeak_fdopendir(int fd) { // cppcheck-suppress unreadVariable DIR* leak1 = fdopendir(fd); // cppcheck-suppress resourceLeak } void resourceLeak_opendir(void) { // cppcheck-suppress unreadVariable DIR* leak1 = opendir("abc"); // cppcheck-suppress resourceLeak } void resourceLeak_socket(void) { // cppcheck-suppress unreadVariable int s = socket(AF_INET, SOCK_STREAM, 0); // cppcheck-suppress resourceLeak } void resourceLeak_open1(void) { // cppcheck-suppress unreadVariable int fd = open("file", O_RDWR | O_CREAT); // cppcheck-suppress resourceLeak } void resourceLeak_open2(void) { // cppcheck-suppress unreadVariable int fd = open("file", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); // cppcheck-suppress resourceLeak } void noleak(int x, int y, int z) { DIR *p1 = fdopendir(x); closedir(p1); DIR *p2 = opendir("abc"); closedir(p2); int s = socket(AF_INET,SOCK_STREAM,0); close(s); int fd1 = open("a", O_RDWR | O_CREAT); close(fd1); int fd2 = open("a", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); close(fd2); /* TODO: add configuration for open/fdopen // #2830 int fd = open("path", O_RDONLY); FILE *f = fdopen(fd, "rt"); fclose(f); */ } // unused return value void ignoredReturnValue(void *addr, int fd) { // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed mmap(addr, 255, PROT_NONE, MAP_PRIVATE, fd, 0); // cppcheck-suppress ignoredReturnValue setuid(42); // cppcheck-suppress ignoredReturnValue getuid(); // cppcheck-suppress ignoredReturnValue access("filename", 1); } // valid range void invalidFunctionArg() { // cppcheck-suppress invalidFunctionArg // cppcheck-suppress usleepCalled usleep(-1); // cppcheck-suppress usleepCalled usleep(0); // cppcheck-suppress usleepCalled usleep(999999); // cppcheck-suppress invalidFunctionArg // cppcheck-suppress usleepCalled usleep(1000000); } void invalidFunctionArg_close(int fd) { if (fd < 0) { // cppcheck-suppress invalidFunctionArg (void)close(fd); } } void uninitvar(int fd) { int x1, x2, x3, x4; char buf[2]; int decimal, sign; double d; void *p; pthread_mutex_t mutex, mutex1, mutex2, mutex3; // cppcheck-suppress uninitvar write(x1,"ab",2); // TODO cppcheck-suppress uninitvar write(fd,buf,2); // #6325 // cppcheck-suppress uninitvar write(fd,"ab",x2); // cppcheck-suppress uninitvar write(fd,p,2); /* int regcomp(regex_t *restrict preg, const char *restrict pattern, int cflags); */ regex_t reg; const char * pattern; int cflags1, cflags2; // cppcheck-suppress uninitvar regcomp(®, pattern, cflags1); pattern=""; // cppcheck-suppress uninitvar regcomp(®, pattern, cflags2); regerror(0, ®, 0, 0); #ifndef __CYGWIN__ // cppcheck-suppress uninitvar // cppcheck-suppress unreadVariable // cppcheck-suppress ecvtCalled char *buffer = ecvt(d, 11, &decimal, &sign); #endif // cppcheck-suppress gcvtCalled gcvt(3.141, 2, buf); char *filename; struct utimbuf *times; // cppcheck-suppress uninitvar // cppcheck-suppress utimeCalled utime(filename, times); struct timeval times1[2]; // cppcheck-suppress uninitvar // cppcheck-suppress utimeCalled utime(filename, times1); // cppcheck-suppress unreadVariable // cppcheck-suppress uninitvar int access_ret = access("file", x3); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress uninitvar fdopen(x4, "rw"); char *strtok_arg1; // cppcheck-suppress strtokCalled // cppcheck-suppress uninitvar strtok(strtok_arg1, ";"); // cppcheck-suppress uninitvar pthread_mutex_lock(&mutex1); // cppcheck-suppress uninitvar (void)pthread_mutex_trylock(&mutex2); // cppcheck-suppress uninitvar pthread_mutex_unlock(&mutex3); // after initialization it must be OK to call lock, trylock and unlock for this mutex pthread_mutex_init(&mutex, NULL); pthread_mutex_lock(&mutex); (void)pthread_mutex_trylock(&mutex); pthread_mutex_unlock(&mutex); } void uninitvar_getcwd(void) { char *buf; size_t size; // cppcheck-suppress uninitvar (void)getcwd(buf,size); } void uninitvar_types(void) { // cppcheck-suppress unassignedVariable blkcnt_t b; // cppcheck-suppress uninitvar b + 1; struct dirent d; // TODO cppcheck-suppress uninitvar d.d_ino + 1; } void timet_h(struct timespec* ptp1) { clockid_t clk_id1, clk_id2, clk_id3; struct timespec* ptp; // cppcheck-suppress uninitvar clock_settime(CLOCK_REALTIME, ptp); // cppcheck-suppress uninitvar clock_settime(clk_id1, ptp); // cppcheck-suppress uninitvar clock_settime(clk_id2, ptp1); struct timespec tp; // TODO cppcheck-suppress uninitvar clock_settime(CLOCK_REALTIME, &tp); // #6577 - false negative // cppcheck-suppress uninitvar clock_settime(clk_id3, &tp); time_t clock = time(0); char buf[26]; // cppcheck-suppress ctime_rCalled ctime_r(&clock, buf); } void dl(const char* libname, const char* func) { void* lib = dlopen(libname, RTLD_NOW); // cppcheck-suppress redundantInitialization // cppcheck-suppress resourceLeak lib = dlopen(libname, RTLD_LAZY); const char* funcname; // cppcheck-suppress uninitvar // cppcheck-suppress unreadVariable void* sym = dlsym(lib, funcname); // cppcheck-suppress ignoredReturnValue dlsym(lib, "foo"); void* uninit; // cppcheck-suppress uninitvar dlclose(uninit); // cppcheck-suppress resourceLeak } void asctime_r_test(struct tm * tm, char * bufSizeUnknown) { struct tm tm_uninit_data; struct tm * tm_uninit_pointer; char bufSize5[5]; char bufSize25[25]; char bufSize26[26]; char bufSize100[100]; // cppcheck-suppress asctime_rCalled // cppcheck-suppress bufferAccessOutOfBounds asctime_r(tm, bufSize5); // cppcheck-suppress asctime_rCalled // cppcheck-suppress bufferAccessOutOfBounds asctime_r(tm, bufSize25); // cppcheck-suppress asctime_rCalled asctime_r(tm, bufSize26); // cppcheck-suppress asctime_rCalled asctime_r(tm, bufSize100); // cppcheck-suppress asctime_rCalled // cppcheck-suppress uninitvar asctime_r(&tm_uninit_data, bufSize100); // cppcheck-suppress asctime_rCalled // cppcheck-suppress uninitvar asctime_r(tm_uninit_pointer, bufSize100); // cppcheck-suppress asctime_rCalled asctime_r(tm, bufSizeUnknown); } void ctime_r_test(time_t * timep, char * bufSizeUnknown) { time_t time_t_uninit_data; time_t * time_t_uninit_pointer; char bufSize5[5]; char bufSize25[25]; char bufSize26[26]; char bufSize100[100]; // cppcheck-suppress ctime_rCalled // cppcheck-suppress bufferAccessOutOfBounds ctime_r(timep, bufSize5); // cppcheck-suppress ctime_rCalled // cppcheck-suppress bufferAccessOutOfBounds ctime_r(timep, bufSize25); // cppcheck-suppress ctime_rCalled ctime_r(timep, bufSize26); // cppcheck-suppress ctime_rCalled ctime_r(timep, bufSize100); // cppcheck-suppress ctime_rCalled // cppcheck-suppress uninitvar ctime_r(&time_t_uninit_data, bufSize100); // cppcheck-suppress ctime_rCalled // cppcheck-suppress uninitvar ctime_r(time_t_uninit_pointer, bufSize100); // cppcheck-suppress ctime_rCalled ctime_r(timep, bufSizeUnknown); } cppcheck-1.90/test/cfg/python.c000066400000000000000000000026271357737443600164600ustar00rootroot00000000000000 // Test library configuration for python.cfg // // Usage: // $ cppcheck --check-library --library=python --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/python.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #define PY_SSIZE_T_CLEAN #include // should be the first include #include void validCode(PyObject * pPyObjArg) { PyObject * pPyObjNULL = NULL; Py_Initialize(); Py_INCREF(pPyObjArg); Py_DECREF(pPyObjArg); Py_XINCREF(pPyObjArg); Py_XINCREF(pPyObjNULL); Py_XDECREF(pPyObjArg); Py_XDECREF(pPyObjNULL); Py_CLEAR(pPyObjArg); Py_CLEAR(pPyObjNULL); (void)PyErr_NewException("text", NULL, NULL); char * pBuf1 = PyMem_Malloc(5); PyMem_Free(pBuf1); int * pIntBuf1 = PyMem_New(int, 10); PyMem_Free(pIntBuf1); } void nullPointer() { // cppcheck-suppress nullPointer Py_INCREF(NULL); // cppcheck-suppress nullPointer Py_DECREF(NULL); } void PyMem_Malloc_memleak() { char * pBuf1 = PyMem_Malloc(1); printf("%p", pBuf1); // cppcheck-suppress memleak } void PyMem_Malloc_mismatchAllocDealloc() { char * pBuf1 = PyMem_Malloc(10); // cppcheck-suppress mismatchAllocDealloc free(pBuf1); } void PyMem_New_memleak() { char * pBuf1 = PyMem_New(char, 5); printf("%p", pBuf1); // cppcheck-suppress memleak } cppcheck-1.90/test/cfg/qt.cpp000066400000000000000000000304531357737443600161210ustar00rootroot00000000000000 // Test library configuration for qt.cfg // // Usage: // $ cppcheck --check-library --enable=information --inconclusive --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr --library=qt test/cfg/qt.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include #include #include #include #include #include #include void QString1(QString s) { for (int i = 0; i <= s.size(); ++i) { // cppcheck-suppress stlOutOfBounds s[i] = 'x'; } } int QString2() { QString s; // FIXME cppcheck-suppress reademptycontainer return s.size(); } QString::iterator QString3() { QString qstring1; QString qstring2; // cppcheck-suppress iterators2 for (QString::iterator it = qstring1.begin(); it != qstring2.end(); ++it) {} QString::iterator it = qstring1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } void QByteArray1(QByteArray byteArrayArg) { for (int i = 0; i <= byteArrayArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds byteArrayArg[i] = 'x'; } // cppcheck-suppress containerOutOfBoundsIndexExpression byteArrayArg[byteArrayArg.length()] = 'a'; // cppcheck-suppress containerOutOfBoundsIndexExpression byteArrayArg[byteArrayArg.count()] = 'b'; // cppcheck-suppress containerOutOfBoundsIndexExpression printf("val: %c\n", byteArrayArg[byteArrayArg.size()]); QByteArray byteArray1{'a', 'b'}; (void)byteArray1[1]; // cppcheck-suppress ignoredReturnValue byteArray1.at(1); } void QList1(QList intListArg) { for (int i = 0; i <= intListArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds intListArg[i] = 1; } // cppcheck-suppress containerOutOfBoundsIndexExpression intListArg[intListArg.length()] = 5; // cppcheck-suppress containerOutOfBoundsIndexExpression intListArg[intListArg.count()] = 10; // cppcheck-suppress containerOutOfBoundsIndexExpression printf("val: %d\n", intListArg[intListArg.size()]); QList qstringList1{"one", "two"}; (void)qstringList1[1]; QList qstringList2 = {"one", "two"}; (void)qstringList2[1]; qstringList2.clear(); // TODO: cppcheck-suppress containerOutOfBounds #9243 (void)qstringList2[1]; QList qstringList3; qstringList3 << "one" << "two"; // FIXME: The following containerOutOfBounds suppression is wrong #9242 // Please remove the suppression as soon as this is fixed // cppcheck-suppress containerOutOfBounds (void)qstringList3[1]; // cppcheck-suppress ignoredReturnValue qstringList3.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringList3.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringList3.count(); // cppcheck-suppress ignoredReturnValue qstringList3.length(); // cppcheck-suppress ignoredReturnValue qstringList3.size(); // cppcheck-suppress ignoredReturnValue qstringList3.at(5); // cppcheck-suppress invalidFunctionArg (void)qstringList3.at(-5); QList qstringList4; // cppcheck-suppress containerOutOfBounds (void)qstringList4[0]; qstringList4.append("a"); (void)qstringList4[0]; qstringList4.clear(); // TODO: cppcheck-suppress containerOutOfBounds #9243 (void)qstringList4[0]; } QList::iterator QList3() { QList qlist1; QList qlist2; // cppcheck-suppress iterators2 for (QList::iterator it = qlist1.begin(); it != qlist2.end(); ++it) {} QList::iterator it = qlist1.begin(); // TODO: cppcheck-suppress returnDanglingLifetime return it; } void QLinkedList1() { QLinkedList qstringLinkedList1{"one", "two"}; QLinkedList qstringLinkedList2 = {"one", "two"}; qstringLinkedList2.clear(); QLinkedList qstringLinkedList3; qstringLinkedList3 << "one" << "two"; // cppcheck-suppress ignoredReturnValue qstringLinkedList3.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringLinkedList3.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringLinkedList3.count(); // cppcheck-suppress ignoredReturnValue qstringLinkedList3.size(); QLinkedList qstringLinkedList4; qstringLinkedList4.append("a"); qstringLinkedList4.clear(); } QLinkedList::iterator QLinkedList3() { QLinkedList intQLinkedList1; QLinkedList intQLinkedList2; // cppcheck-suppress iterators2 for (QLinkedList::iterator it = intQLinkedList1.begin(); it != intQLinkedList2.end(); ++it) {} QLinkedList::iterator it = intQLinkedList1.begin(); // TODO: cppcheck-suppress returnDanglingLifetime return it; } void QStringList1(QStringList stringlistArg) { for (int i = 0; i <= stringlistArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds stringlistArg[i] = "abc"; } // cppcheck-suppress containerOutOfBoundsIndexExpression stringlistArg[stringlistArg.length()] = "ab"; stringlistArg[stringlistArg.length() - 1] = "ab"; // could be valid // cppcheck-suppress containerOutOfBoundsIndexExpression stringlistArg[stringlistArg.count()] = "12"; stringlistArg[stringlistArg.count() - 1] = "12"; // could be valid // cppcheck-suppress containerOutOfBoundsIndexExpression (void)stringlistArg[stringlistArg.size()]; (void)stringlistArg[stringlistArg.size() - 1]; // could be valid QStringList qstringlist1{"one", "two"}; (void)qstringlist1[1]; QStringList qstringlist2 = {"one", "two"}; (void)qstringlist2[1]; QStringList qstringlist3; qstringlist3 << "one" << "two"; // FIXME: The following containerOutOfBounds suppression is wrong #9242 // Please remove the suppression as soon as this is fixed // cppcheck-suppress containerOutOfBounds (void)qstringlist3[1]; // cppcheck-suppress ignoredReturnValue qstringlist3.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringlist3.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringlist3.count(); // cppcheck-suppress ignoredReturnValue qstringlist3.length(); // cppcheck-suppress ignoredReturnValue qstringlist3.size(); // cppcheck-suppress ignoredReturnValue qstringlist3.at(5); // cppcheck-suppress invalidFunctionArg (void)qstringlist3.at(-5); } QStringList::iterator QStringList2() { QStringList qstringlist1; QStringList qstringlist2; // cppcheck-suppress iterators2 for (QStringList::iterator it = qstringlist1.begin(); it != qstringlist2.end(); ++it) {} QStringList::iterator it = qstringlist1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } void QVector1(QVector intVectorArg) { for (int i = 0; i <= intVectorArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds intVectorArg[i] = 1; } // cppcheck-suppress containerOutOfBoundsIndexExpression intVectorArg[intVectorArg.length()] = 5; // cppcheck-suppress containerOutOfBoundsIndexExpression intVectorArg[intVectorArg.count()] = 10; // cppcheck-suppress containerOutOfBoundsIndexExpression printf("val: %d\n", intVectorArg[intVectorArg.size()]); QVector qstringVector1{"one", "two"}; (void)qstringVector1[1]; QVector qstringVector2 = {"one", "two"}; (void)qstringVector2[1]; QVector qstringVector3; qstringVector3 << "one" << "two"; // FIXME: The following containerOutOfBounds suppression is wrong #9242 // Please remove the suppression as soon as this is fixed // cppcheck-suppress containerOutOfBounds (void)qstringVector3[1]; // cppcheck-suppress ignoredReturnValue qstringVector3.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringVector3.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringVector3.count(); // cppcheck-suppress ignoredReturnValue qstringVector3.length(); // cppcheck-suppress ignoredReturnValue qstringVector3.size(); // cppcheck-suppress ignoredReturnValue qstringVector3.at(5); // cppcheck-suppress invalidFunctionArg (void)qstringVector3.at(-5); } QVector::iterator QVector2() { QVector qvector1; QVector qvector2; // cppcheck-suppress iterators2 for (QVector::iterator it = qvector1.begin(); it != qvector2.end(); ++it) {} QVector::iterator it = qvector1.begin(); // TODO cppcheck-suppress returnDanglingLifetime return it; } void QStack1(QStack intStackArg) { for (int i = 0; i <= intStackArg.size(); ++i) { // cppcheck-suppress stlOutOfBounds intStackArg[i] = 1; } // cppcheck-suppress containerOutOfBoundsIndexExpression intStackArg[intStackArg.length()] = 5; // cppcheck-suppress containerOutOfBoundsIndexExpression intStackArg[intStackArg.count()] = 10; // cppcheck-suppress containerOutOfBoundsIndexExpression printf("val: %d\n", intStackArg[intStackArg.size()]); QStack qstringStack1; qstringStack1.push("one"); qstringStack1.push("two"); (void)qstringStack1[1]; QStack qstringStack2; qstringStack2 << "one" << "two"; // FIXME: The following containerOutOfBounds suppression is wrong #9242 // Please remove the suppression as soon as this is fixed // cppcheck-suppress containerOutOfBounds (void)qstringStack2[1]; // cppcheck-suppress ignoredReturnValue qstringStack2.startsWith("one"); // cppcheck-suppress ignoredReturnValue qstringStack2.endsWith("one"); // cppcheck-suppress ignoredReturnValue qstringStack2.count(); // cppcheck-suppress ignoredReturnValue qstringStack2.length(); // cppcheck-suppress ignoredReturnValue qstringStack2.size(); // cppcheck-suppress ignoredReturnValue qstringStack2.at(5); // cppcheck-suppress invalidFunctionArg (void)qstringStack2.at(-5); } QStack::iterator QStack2() { QStack qstack1; QStack qstack2; // cppcheck-suppress iterators2 for (QStack::iterator it = qstack1.begin(); it != qstack2.end(); ++it) {} QStack::iterator it = qstack1.begin(); // TODO cppcheck-suppress returnDanglingLifetime return it; } void QStack3() { QStack intStack; intStack.push(1); // cppcheck-suppress ignoredReturnValue intStack.top(); intStack.pop(); } // Verify that Qt macros do not result in syntax errors, false positives or other issues. class MacroTest1: public QObject { Q_OBJECT Q_PLUGIN_METADATA(IID "com.foo.bar" FILE "test.json") public: explicit MacroTest1(QObject *parent = 0); ~MacroTest1(); }; class MacroTest2 { Q_DECLARE_TR_FUNCTIONS(MacroTest2) public: MacroTest2(); ~MacroTest2(); }; void MacroTest2_test() { // TODO: remove suppression when #9002 is fixed // cppcheck-suppress checkLibraryFunction QString str = MacroTest2::tr("hello"); QByteArray ba = str.toLatin1(); printf(ba.data()); #ifndef QT_NO_DEPRECATED // TODO: remove suppression when #9002 is fixed // cppcheck-suppress checkLibraryFunction str = MacroTest2::trUtf8("test2"); ba = str.toLatin1(); printf(ba.data()); #endif } void validCode(int * pIntPtr) { if (QFile::exists("test")) { } if (pIntPtr != Q_NULLPTR) { *pIntPtr = 5; } if (pIntPtr && *pIntPtr == 1) { forever { } } else if (pIntPtr && *pIntPtr == 2) { Q_FOREVER { } } if (Q_LIKELY(pIntPtr)) {} if (Q_UNLIKELY(!pIntPtr)) {} printf(QT_TR_NOOP("Hi")); Q_DECLARE_LOGGING_CATEGORY(logging_category_test); QT_FORWARD_DECLARE_CLASS(forwardDeclaredClass); QT_FORWARD_DECLARE_STRUCT(forwardDeclaredStruct); } void ignoredReturnValue() { // cppcheck-suppress ignoredReturnValue QFile::exists("test"); QFile file1("test"); // cppcheck-suppress ignoredReturnValue file1.exists(); } void nullPointer(int * pIntPtr) { int * pNullPtr = Q_NULLPTR; // cppcheck-suppress nullPointer *pNullPtr = 1; if (pIntPtr != Q_NULLPTR) { *pIntPtr = 2; } else { // cppcheck-suppress nullPointerRedundantCheck *pIntPtr = 3; } } cppcheck-1.90/test/cfg/runtests.sh000077500000000000000000000336511357737443600172220ustar00rootroot00000000000000#!/bin/bash set -e # abort on error #set -x # be verbose if [[ $(pwd) == */test/cfg ]] ; then # we are in test/cfg CPPCHECK="../../cppcheck" DIR="" CFG="../../cfg/" else # assume we are in repo root CPPCHECK="./cppcheck" DIR=test/cfg/ CFG="cfg/" fi # Cppcheck options CPPCHECK_OPT='--check-library --enable=information --enable=style --error-exitcode=-1 --suppress=missingIncludeSystem --inline-suppr --template="{file}:{line}:{severity}:{id}:{message}"' # Compiler settings CXX=g++ CXX_OPT='-fsyntax-only -std=c++0x -Wno-format -Wno-format-security' CC=gcc CC_OPT='-Wno-format -Wno-nonnull -Wno-implicit-function-declaration -Wno-deprecated-declarations -Wno-format-security -Wno-nonnull -fsyntax-only' # posix.c ${CC} ${CC_OPT} ${DIR}posix.c ${CPPCHECK} ${CPPCHECK_OPT} --library=posix ${DIR}posix.c # gnu.c ${CC} ${CC_OPT} -D_GNU_SOURCE ${DIR}gnu.c ${CPPCHECK} ${CPPCHECK_OPT} --library=posix,gnu ${DIR}gnu.c # qt.cpp set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve Qt configuration is not available, skipping syntax check." else set +e QTCONFIG=$(pkg-config --cflags Qt5Core) QTCONFIG_RETURNCODE=$? set -e if [ $QTCONFIG_RETURNCODE -eq 0 ]; then QTBUILDCONFIG=$(pkg-config --variable=qt_config Qt5Core) [[ $QTBUILDCONFIG =~ (^|[[:space:]])reduce_relocations($|[[:space:]]) ]] && QTCONFIG="${QTCONFIG} -fPIC" set +e echo -e "#include " | ${CXX} ${CXX_OPT} ${QTCONFIG} -x c++ - QTCHECK_RETURNCODE=$? set -e if [ $QTCHECK_RETURNCODE -ne 0 ]; then echo "Qt not completely present or not working, skipping syntax check with ${CXX}." else echo "Qt found and working, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${QTCONFIG} ${DIR}qt.cpp fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=qt ${DIR}qt.cpp # bsd.c ${CPPCHECK} ${CPPCHECK_OPT} --library=bsd ${DIR}bsd.c # std.c ${CC} ${CC_OPT} ${DIR}std.c ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive ${DIR}std.c ${CXX} ${CXX_OPT} ${DIR}std.cpp ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive ${DIR}std.cpp # windows.cpp # Syntax check via g++ does not work because it can not find a valid windows.h #${CXX} ${CXX_OPT} ${DIR}windows.cpp ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --platform=win32A ${DIR}windows.cpp ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --platform=win32W ${DIR}windows.cpp ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --platform=win64 ${DIR}windows.cpp # wxwidgets.cpp set +e WXCONFIG=$(wx-config --cxxflags) WXCONFIG_RETURNCODE=$? set -e if [ $WXCONFIG_RETURNCODE -ne 0 ]; then echo "wx-config does not work, skipping syntax check for wxWidgets tests." else set +e echo -e "#include \n#include \n#include \n#include \n#if wxVERSION_NUMBER<2950\n#error \"Old version\"\n#endif" | ${CXX} ${CXX_OPT} ${WXCONFIG} -x c++ - WXCHECK_RETURNCODE=$? set -e if [ $WXCHECK_RETURNCODE -ne 0 ]; then echo "wxWidgets not completely present (with GUI classes) or not working, skipping syntax check with ${CXX}." else echo "wxWidgets found, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${WXCONFIG} -Wno-deprecated-declarations ${DIR}wxwidgets.cpp fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=wxwidgets -f ${DIR}wxwidgets.cpp # gtk.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve GTK+ configuration is not available, skipping syntax check." else set +e GTKCONFIG=$(pkg-config --cflags gtk+-3.0) GTKCONFIG_RETURNCODE=$? set -e if [ $GTKCONFIG_RETURNCODE -ne 0 ]; then set +e GTKCONFIG=$(pkg-config --cflags gtk+-2.0) GTKCONFIG_RETURNCODE=$? set -e fi if [ $GTKCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${GTKCONFIG} -x c - GTKCHECK_RETURNCODE=$? set -e if [ $GTKCHECK_RETURNCODE -ne 0 ]; then echo "GTK+ not completely present or not working, skipping syntax check with ${CXX}." else echo "GTK+ found and working, checking syntax with ${CXX} now." ${CC} ${CC_OPT} ${GTKCONFIG} ${DIR}gtk.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=gtk -f ${DIR}gtk.c # boost.cpp set +e echo -e "#include " | ${CXX} ${CXX_OPT} -x c++ - BOOSTCHECK_RETURNCODE=$? set -e if [ ${BOOSTCHECK_RETURNCODE} -ne 0 ]; then echo "Boost not completely present or not working, skipping syntax check with ${CXX}." else echo "Boost found and working, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${DIR}boost.cpp fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=boost ${DIR}boost.cpp # sqlite3.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve SQLite3 configuration is not available, skipping syntax check." else set +e SQLITE3CONFIG=$(pkg-config --cflags sqlite3) SQLITE3CONFIG_RETURNCODE=$? set -e if [ $SQLITE3CONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${SQLITE3CONFIG} -x c - SQLITE3CHECK_RETURNCODE=$? set -e if [ $SQLITE3CHECK_RETURNCODE -ne 0 ]; then echo "SQLite3 not completely present or not working, skipping syntax check with ${CC}." else echo "SQLite3 found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${SQLITE3CONFIG} ${DIR}sqlite3.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=sqlite3 ${DIR}sqlite3.c # openmp.c ${CC} ${CC_OPT} -fopenmp ${DIR}openmp.c ${CPPCHECK} ${CPPCHECK_OPT} --library=openmp ${DIR}openmp.c # python.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve Python 3 configuration is not available, skipping syntax check." else set +e PYTHON3CONFIG=$(pkg-config --cflags python3) PYTHON3CONFIG_RETURNCODE=$? set -e if [ $PYTHON3CONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${PYTHON3CONFIG} -x c - PYTHON3CONFIG_RETURNCODE=$? set -e if [ $PYTHON3CONFIG_RETURNCODE -ne 0 ]; then echo "Python 3 not completely present or not working, skipping syntax check with ${CC}." else echo "Python 3 found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${PYTHON3CONFIG} ${DIR}python.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=python ${DIR}python.c # lua.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve Lua configuration is not available, skipping syntax check." else set +e LUACONFIG=$(pkg-config --cflags lua-5.3) LUACONFIG_RETURNCODE=$? set -e if [ $LUACONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${LUACONFIG} -x c - LUACONFIG_RETURNCODE=$? set -e if [ $LUACONFIG_RETURNCODE -ne 0 ]; then echo "Lua not completely present or not working, skipping syntax check with ${CC}." else echo "Lua found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${LUACONFIG} ${DIR}lua.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=lua ${DIR}lua.c # libcurl.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve libcurl configuration is not available, skipping syntax check." else set +e LIBCURLCONFIG=$(pkg-config --cflags libcurl) LIBCURLCONFIG_RETURNCODE=$? set -e if [ $LIBCURLCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${LIBCURLCONFIG} -x c - LIBCURLCONFIG_RETURNCODE=$? set -e if [ $LIBCURLCONFIG_RETURNCODE -ne 0 ]; then echo "libcurl not completely present or not working, skipping syntax check with ${CC}." else echo "libcurl found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${LIBCURLCONFIG} ${DIR}libcurl.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=libcurl ${DIR}libcurl.c # cairo.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve cairo configuration is not available, skipping syntax check." else set +e CAIROCONFIG=$(pkg-config --cflags cairo) CAIROCONFIG_RETURNCODE=$? set -e if [ $CAIROCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${CAIROCONFIG} -x c - CAIROCONFIG_RETURNCODE=$? set -e if [ $CAIROCONFIG_RETURNCODE -ne 0 ]; then echo "cairo not completely present or not working, skipping syntax check with ${CC}." else echo "cairo found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${CAIROCONFIG} ${DIR}cairo.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=cairo ${DIR}cairo.c ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=googletest ${DIR}googletest.cpp # kde.cpp set +e KDECONFIG=$(kde4-config --path include) KDECONFIG_RETURNCODE=$? set -e if [ $KDECONFIG_RETURNCODE -ne 0 ]; then echo "kde4-config does not work, skipping syntax check." else set +e KDEQTCONFIG=$(pkg-config --cflags QtCore) KDEQTCONFIG_RETURNCODE=$? set -e if [ $KDEQTCONFIG_RETURNCODE -ne 0 ]; then echo "Suitable Qt not present, Qt is necessary for KDE. Skipping syntax check." else set +e echo -e "#include \n" | ${CXX} ${CXX_OPT} -I${KDECONFIG} ${KDEQTCONFIG} -x c++ - KDECHECK_RETURNCODE=$? set -e if [ $KDECHECK_RETURNCODE -ne 0 ]; then echo "KDE headers not completely present or not working, skipping syntax check with ${CXX}." else echo "KDE found, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} -I${KDECONFIG} ${KDEQTCONFIG} ${DIR}kde.cpp fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --inconclusive --library=kde ${DIR}kde.cpp # libsigc++.cpp set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve libsigc++ configuration is not available, skipping syntax check." else set +e LIBSIGCPPCONFIG=$(pkg-config --cflags sigc++-2.0) LIBSIGCPPCONFIG_RETURNCODE=$? set -e if [ $LIBSIGCPPCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include \n" | ${CXX} ${CXX_OPT} ${LIBSIGCPPCONFIG} -x c++ - LIBSIGCPPCONFIG_RETURNCODE=$? set -e if [ $LIBSIGCPPCONFIG_RETURNCODE -ne 0 ]; then echo "libsigc++ not completely present or not working, skipping syntax check with ${CXX}." else echo "libsigc++ found and working, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${LIBSIGCPPCONFIG} ${DIR}libsigc++.cpp fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=libsigc++ ${DIR}libsigc++.cpp # openssl.c set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve OpenSSL configuration is not available, skipping syntax check." else set +e OPENSSLCONFIG=$(pkg-config --cflags libssl) OPENSSLCONFIG_RETURNCODE=$? set -e if [ $OPENSSLCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include " | ${CC} ${CC_OPT} ${OPENSSLCONFIG} -x c - OPENSSLCONFIG_RETURNCODE=$? set -e if [ $OPENSSLCONFIG_RETURNCODE -ne 0 ]; then echo "OpenSSL not completely present or not working, skipping syntax check with ${CC}." else echo "OpenSSL found and working, checking syntax with ${CC} now." ${CC} ${CC_OPT} ${OPENSSLCONFIG} ${DIR}openssl.c fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=openssl ${DIR}openssl.c # opencv2.cpp set +e pkg-config --version PKGCONFIG_RETURNCODE=$? set -e if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then echo "pkg-config needed to retrieve OpenCV configuration is not available, skipping syntax check." else set +e OPENCVCONFIG=$(pkg-config --cflags opencv) OPENCVCONFIG_RETURNCODE=$? set -e if [ $OPENCVCONFIG_RETURNCODE -eq 0 ]; then set +e echo -e "#include \n" | ${CXX} ${CXX_OPT} ${OPENCVCONFIG} -x c++ - OPENCVCONFIG_RETURNCODE=$? set -e if [ $OPENCVCONFIG_RETURNCODE -ne 0 ]; then echo "OpenCV not completely present or not working, skipping syntax check with ${CXX}." else echo "OpenCV found and working, checking syntax with ${CXX} now." ${CXX} ${CXX_OPT} ${OPENCVCONFIG} ${DIR}opencv2.cpp fi fi fi ${CPPCHECK} ${CPPCHECK_OPT} --library=opencv2 ${DIR}opencv2.cpp # Check the syntax of the defines in the configuration files set +e xmlstarlet --version XMLSTARLET_RETURNCODE=$? set -e if [ $XMLSTARLET_RETURNCODE -ne 0 ]; then echo "xmlstarlet needed to extract defines, skipping defines check." else for configfile in ${CFG}*.cfg; do echo "Checking defines in $configfile" # Disable debugging output temporarily since there could be many defines set +x # XMLStarlet returns 1 if no elements were found which is no problem here set +e EXTRACTED_DEFINES=$(xmlstarlet sel -t -m '//define' -c . -n <$configfile) set -e EXTRACTED_DEFINES=$(echo "$EXTRACTED_DEFINES" | sed 's///g') echo "$EXTRACTED_DEFINES" | gcc -fsyntax-only -xc -Werror - done fi echo SUCCESS cppcheck-1.90/test/cfg/sqlite3.c000066400000000000000000000024561357737443600165230ustar00rootroot00000000000000 // Test library configuration for sqlite3.cfg // // Usage: // $ cppcheck --check-library --library=sqlite3 --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/sqlite3.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include void validCode() { sqlite3 * db; int rc = sqlite3_open("/db", &db); if (rc != SQLITE_OK) { fprintf(stderr, "Error opening sqlite3 db: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); } else { sqlite3_close(db); } { char * buf = sqlite3_malloc(10); // cppcheck-suppress invalidPrintfArgType_uint printf("size: %ull\n", sqlite3_msize(buf)); sqlite3_free(buf); } } void memleak_sqlite3_malloc() { char * buf = sqlite3_malloc(10); if (buf) { buf[0] = 0; } // cppcheck-suppress memleak } void resourceLeak_sqlite3_open() { sqlite3 * db; sqlite3_open("/db", &db); // TODO: cppcheck-suppress resourceLeak } void ignoredReturnValue(char * buf) { // cppcheck-suppress leakReturnValNotUsed sqlite3_malloc(10); // cppcheck-suppress leakReturnValNotUsed sqlite3_malloc64(5); // cppcheck-suppress ignoredReturnValue sqlite3_msize(buf); } cppcheck-1.90/test/cfg/std.c000066400000000000000000002462361357737443600157370ustar00rootroot00000000000000 // Test library configuration for std.cfg // // Usage: // $ cppcheck --check-library --enable=information --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr test/cfg/std.c // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include // frexp #include #if defined(__STD_UTF_16__) || defined(__STD_UTF_32__) #include #endif #include #include #include #include #include #include #include #include void bufferAccessOutOfBounds(void) { char a[5]; fgets(a,5,stdin); // cppcheck-suppress bufferAccessOutOfBounds fgets(a,6,stdin); sprintf(a, "ab%s", "cd"); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy sprintf(a, "ab%s", "cde"); // TODO cppcheck-suppress redundantCopy snprintf(a, 5, "abcde%i", 1); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds snprintf(a, 6, "abcde%i", 1); // TODO cppcheck-suppress redundantCopy strcpy(a,"abcd"); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy strcpy(a, "abcde"); // cppcheck-suppress bufferAccessOutOfBounds strcpy_s(a, 10, "abcdefghij"); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress terminateStrncpy strncpy(a,"abcde",5); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy strncpy(a,"abcde",6); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy strncpy(a,"a",6); // TODO cppcheck-suppress redundantCopy strncpy(a,"abcdefgh",4); // valid call strncpy_s(a,5,"abcd",5); // string will be truncated, error is returned, but no buffer overflow strncpy_s(a,5,"abcde",6); // TODO cppcheck-suppress bufferAccessOutOfBounds strncpy_s(a,5,"a",6); strncpy_s(a,5,"abcdefgh",4); // valid call strncat_s(a,5,"1",2); // cppcheck-suppress bufferAccessOutOfBounds strncat_s(a,10,"1",2); // TODO cppcheck-suppress bufferAccessOutOfBounds strncat_s(a,5,"1",5); fread(a,1,5,stdin); // cppcheck-suppress bufferAccessOutOfBounds fread(a,1,6,stdin); fwrite(a,1,5,stdout); // cppcheck-suppress bufferAccessOutOfBounds fread(a,1,6,stdout); char * pAlloc1 = aligned_alloc(8, 16); memset(pAlloc1, 0, 16); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc1, 0, 17); free(pAlloc1); } void bufferAccessOutOfBounds_libraryDirectionConfiguration(void) { // This tests whether the argument to isdigit() is configured with direction "in". This allows // Cppcheck to report the error without marking it as inconclusive. char arr[10]; char c = 'A'; (void)isdigit(c); // cppcheck-suppress arrayIndexOutOfBounds // cppcheck-suppress unreadVariable arr[c] = 'x'; } void arrayIndexOutOfBounds() { char * pAlloc1 = aligned_alloc(8, 16); pAlloc1[15] = '\0'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc1[16] = '1'; free(pAlloc1); char * pAlloc2 = malloc(9); pAlloc2[8] = 'a'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc2[9] = 'a'; // #1379 // cppcheck-suppress memleakOnRealloc pAlloc2 = realloc(pAlloc2, 8); pAlloc2[7] = 'b'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc2[8] = 0; // cppcheck-suppress memleakOnRealloc pAlloc2 = realloc(pAlloc2, 20); pAlloc2[19] = 'b'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc2[20] = 0; free(pAlloc2); char * pAlloc3 = calloc(2,3); pAlloc3[5] = 'a'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc3[6] = 1; // cppcheck-suppress memleakOnRealloc pAlloc3 = reallocarray(pAlloc3, 3,3); pAlloc3[8] = 'a'; // cppcheck-suppress arrayIndexOutOfBounds pAlloc3[9] = 1; free(pAlloc3); } void resourceLeak_tmpfile(void) { // cppcheck-suppress unreadVariable FILE * fp = tmpfile(); // cppcheck-suppress resourceLeak } // memory leak void ignoreleak(void) { char *p = (char *)malloc(10); memset(&(p[0]), 0, 10); // cppcheck-suppress memleak } // null pointer void nullpointer(int value) { int res = 0; FILE *fp; wchar_t *pWcsUninit; #ifndef __CYGWIN__ // cppcheck-suppress nullPointer clearerr(0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer feof(0); #endif // cppcheck-suppress nullPointer (void)fgetc(0); // cppcheck-suppress nullPointer fclose(0); #ifndef __CYGWIN__ // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer ferror(0); #endif // cppcheck-suppress nullPointer (void)ftell(0); // cppcheck-suppress nullPointer puts(0); // cppcheck-suppress nullPointer fp=fopen(0,0); fclose(fp); fp = 0; // No FP fflush(0); fp = freopen(0,"abc",stdin); fclose(fp); fp = NULL; // cppcheck-suppress nullPointer fputc(0,0); // cppcheck-suppress nullPointer fputs(0,0); // cppcheck-suppress nullPointer fgetpos(0,0); // cppcheck-suppress nullPointer frexp(1.0,0); // cppcheck-suppress nullPointer fsetpos(0,0); // cppcheck-suppress nullPointer itoa(123,0,10); putchar(0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strchr(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcschr(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strlen(0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcslen(0); // cppcheck-suppress nullPointer strcpy(0,0); // cppcheck-suppress nullPointer wcscpy(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcsspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcscspn(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcoll(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcscoll(0,0); // cppcheck-suppress nullPointer strcat(0,0); // cppcheck-suppress nullPointer wcscat(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strcmp(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcscmp(0,0); // cppcheck-suppress nullPointer strcpy_s(0,1,1); // cppcheck-suppress nullPointer strcpy_s(1,1,0); // cppcheck-suppress nullPointer strncpy(0,0,1); // cppcheck-suppress nullPointer strncpy_s(0,1,1,1); // cppcheck-suppress nullPointer strncpy_s(1,1,0,1); // cppcheck-suppress nullPointer wcsncpy(0,0,1); // cppcheck-suppress nullPointer strncat(0,0,1); // cppcheck-suppress nullPointer strncat_s(0,1,1,1); // cppcheck-suppress nullPointer strncat_s(1,1,0,1); // cppcheck-suppress nullPointer wcsncat(0,0,1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strncmp(0,0,1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcsncmp(0,0,1); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer strstr(0,0); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress nullPointer wcsstr(0,0); // cppcheck-suppress nullPointer strtoul(0,0,0); // cppcheck-suppress nullPointer wcstoul(0,0,0); // cppcheck-suppress nullPointer strtoull(0,0,0); // cppcheck-suppress nullPointer wcstoull(0,0,0); // cppcheck-suppress nullPointer strtol(0,0,0); // cppcheck-suppress nullPointer wcstol(0,0,0); // #6100 False positive nullPointer - calling mbstowcs(NULL,) res += mbstowcs(0,"",0); res += wcstombs(0,L"",0); strtok(NULL,"xyz"); wcstok(NULL,L"xyz",&pWcsUninit); strxfrm(0,"foo",0); // TODO: error message (#6306 and http://trac.cppcheck.net/changeset/d11eb4931aea51cf2cb74faccdcd2a3289b818d6/) strxfrm(0,"foo",42); wcsxfrm(0,L"foo",0); // TODO: error message when arg1==NULL and arg3!=0 #6306: https://trac.cppcheck.net/ticket/6306#comment:2 wcsxfrm(0,L"foo",42); snprintf(NULL, 0, "someformatstring"); // legal // cppcheck-suppress nullPointer snprintf(NULL, 42, "someformatstring"); // not legal scanf("%i", &res); // cppcheck-suppress nullPointer scanf("%i", NULL); wscanf(L"%i", &res); // cppcheck-suppress nullPointer wscanf(L"%i", NULL); } void nullpointerMemchr1(char *p, char *s) { p = memchr(s, 'p', strlen(s)); (void)p; } void nullpointerMemchr2(char *p, char *s) { p = memchr(s, 0, strlen(s)); (void)p; } void nullPointer_memchr(char *p) { char *s = 0; // cppcheck-suppress nullPointer p = memchr(s, 0, strlen(s)); (void)p; } void nullPointer_memcmp(char *p) { // cppcheck-suppress nullPointer (void)memcmp(p, 0, 123); } void nullPointer_wmemcmp(wchar_t *p) { // cppcheck-suppress nullPointer (void)wmemcmp(p, 0, 123); } void nullPointer_vsnprintf(const char * format, ...) { va_list args; // valid char buffer[256]; va_start(args, format); vsnprintf(buffer, 256, format, args); printf("%s", buffer); va_end(args); // valid va_start(args, format); vsnprintf(NULL, 0, format, args); va_end(args); // invalid va_start(args, format); // TODO #9410 cppcheck-suppress nullPointer vsnprintf(NULL, 10, format, args); va_end(args); } // uninit pointers void uninitvar_abs(void) { int i; // cppcheck-suppress uninitvar (void)abs(i); } void uninitvar_clearerr(void) { FILE *fp; // cppcheck-suppress uninitvar clearerr(fp); } void uninitvar_fclose(void) { FILE *fp; // cppcheck-suppress uninitvar fclose(fp); } void uninitvar_fopen(void) { const char *filename, *mode; FILE *fp; // cppcheck-suppress uninitvar fp = fopen(filename, "rt"); fclose(fp); // cppcheck-suppress uninitvar fp = fopen("filename.txt", mode); fclose(fp); } void uninitvar_feof(void) { FILE *fp; // cppcheck-suppress ignoredReturnValue // cppcheck-suppress uninitvar feof(fp); // cppcheck-suppress uninitvar (void)feof(fp); } void uninitvar_ferror(void) { FILE *fp; // cppcheck-suppress ignoredReturnValue // cppcheck-suppress uninitvar ferror(fp); // cppcheck-suppress uninitvar (void)ferror(fp); } void uninitvar_fflush(void) { FILE *fp; // cppcheck-suppress uninitvar fflush(fp); } void uninitvar_fgetc(void) { FILE *fp; // cppcheck-suppress uninitvar (void)fgetc(fp); } void uninitvar_fgetpos(void) { FILE *fp; fpos_t pos; fpos_t *ppos; // cppcheck-suppress uninitvar fgetpos(fp,&pos); fp = fopen("filename","rt"); // cppcheck-suppress uninitvar fgetpos(fp,ppos); fclose(fp); } void uninitvar_fsetpos(void) { FILE *fp; fpos_t pos; fpos_t *ppos; // cppcheck-suppress uninitvar fsetpos(fp,&pos); fp = fopen("filename","rt"); // cppcheck-suppress uninitvar fsetpos(fp,ppos); fclose(fp); } void uninitvar_fgets(void) { FILE *fp; char buf[10]; char *str; int n; fgets(buf,10,stdin); // cppcheck-suppress uninitvar fgets(str,10,stdin); // cppcheck-suppress uninitvar fgets(buf,10,fp); // cppcheck-suppress uninitvar fgets(buf,n,stdin); } void uninitvar_fputc(void) { int i; FILE *fp; fputc('a', stdout); // cppcheck-suppress uninitvar fputc(i, stdout); // cppcheck-suppress uninitvar fputc('a', fp); } void uninitvar_fputs(void) { const char *s; FILE *fp; fputs("a", stdout); // cppcheck-suppress uninitvar fputs(s, stdout); // cppcheck-suppress uninitvar fputs("a", fp); } void uninitvar_ftell(void) { FILE *fp; // cppcheck-suppress uninitvar (void)ftell(fp); } void uninitvar_puts(void) { const char *s; // cppcheck-suppress uninitvar puts(s); } void uninitvar_putchar(void) { char c; // cppcheck-suppress uninitvar putchar(c); } void uninitvar_cproj(void) // #6939 { float complex fc; // cppcheck-suppress uninitvar (void)cprojf(fc); double complex dc; // cppcheck-suppress uninitvar (void)cproj(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cprojl(ldc); } void uninitvar_creal(void) { float complex fc; // cppcheck-suppress uninitvar (void)crealf(fc); double complex dc; // cppcheck-suppress uninitvar (void)creal(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)creall(ldc); } void uninitvar_acos(void) { float f; // cppcheck-suppress uninitvar (void)acosf(f); double d; // cppcheck-suppress uninitvar (void)acos(d); long double ld; // cppcheck-suppress uninitvar (void)acosl(ld); } void uninitvar_acosh(void) { float f; // cppcheck-suppress uninitvar (void)acoshf(f); double d; // cppcheck-suppress uninitvar (void)acosh(d); long double ld; // cppcheck-suppress uninitvar (void)acoshl(ld); } void uninitvar_asctime(void) { const struct tm *tm; // cppcheck-suppress uninitvar // cppcheck-suppress asctimeCalled (void)asctime(tm); } void uninitvar_asctime_s(void) { const struct tm *tm; char buf[26]; // cppcheck-suppress uninitvar // cppcheck-suppress asctime_sCalled asctime_s(buf, sizeof(buf), tm); } void uninitvar_assert(void) { int i; // cppcheck-suppress checkLibraryNoReturn // cppcheck-suppress uninitvar assert(i); } void uninitvar_sqrt(void) { float f; // cppcheck-suppress uninitvar (void)sqrtf(f); double d; // cppcheck-suppress uninitvar (void)sqrt(d); long double ld; // cppcheck-suppress uninitvar (void)sqrtl(ld); } void uninitvar_csqrt(void) { float complex fc; // cppcheck-suppress uninitvar (void)csqrtf(fc); double complex dc; // cppcheck-suppress uninitvar (void)csqrt(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)csqrtl(ldc); } void uninitvar_sinh(void) { float f; // cppcheck-suppress uninitvar (void)sinhf(f); double d; // cppcheck-suppress uninitvar (void)sinh(d); long double ld; // cppcheck-suppress uninitvar (void)sinhl(ld); } void uninitvar_sin(void) { float f; // cppcheck-suppress uninitvar (void)sinf(f); double d; // cppcheck-suppress uninitvar (void)sin(d); long double ld; // cppcheck-suppress uninitvar (void)sinl(ld); } void uninitvar_csin(void) { float complex fd; // cppcheck-suppress uninitvar (void)csinf(fd); double complex dc; // cppcheck-suppress uninitvar (void)csin(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)csinl(ldc); } void uninitvar_csinh(void) { float complex fd; // cppcheck-suppress uninitvar (void)csinhf(fd); double complex dc; // cppcheck-suppress uninitvar (void)csinh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)csinhl(ldc); } void uninitvar_asin(void) { float f; // cppcheck-suppress uninitvar (void)asinf(f); double d; // cppcheck-suppress uninitvar (void)asin(d); long double ld; // cppcheck-suppress uninitvar (void)asinl(ld); } void uninitvar_casin(void) { float complex fd; // cppcheck-suppress uninitvar (void)casinf(fd); double complex dc; // cppcheck-suppress uninitvar (void)casin(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)casinl(ldc); } void uninitvar_asinh(void) { float f; // cppcheck-suppress uninitvar (void)asinhf(f); double d; // cppcheck-suppress uninitvar (void)asinh(d); long double ld; // cppcheck-suppress uninitvar (void)asinhl(ld); } void uninitvar_casinh(void) { float complex fd; // cppcheck-suppress uninitvar (void)casinhf(fd); double complex dc; // cppcheck-suppress uninitvar (void)casinh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)casinhl(ldc); } void uninitvar_wcsftime(wchar_t* ptr) { size_t maxsize; wchar_t* format; struct tm* timeptr; // cppcheck-suppress uninitvar (void)wcsftime(ptr, maxsize, format, timeptr); } void uninitvar_tan(void) { float f; // cppcheck-suppress uninitvar (void)tanf(f); double d; // cppcheck-suppress uninitvar (void)tan(d); long double ld; // cppcheck-suppress uninitvar (void)tanl(ld); } void uninitvar_ctan(void) { float complex fd; // cppcheck-suppress uninitvar (void)ctanf(fd); double complex dc; // cppcheck-suppress uninitvar (void)ctan(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)ctanl(ldc); } void uninitvar_tanh(void) { float f; // cppcheck-suppress uninitvar (void)tanhf(f); double d; // cppcheck-suppress uninitvar (void)tanh(d); long double ld; // cppcheck-suppress uninitvar (void)tanhl(ld); } void uninitvar_ctanh(void) { float complex fd; // cppcheck-suppress uninitvar (void)ctanhf(fd); double complex dc; // cppcheck-suppress uninitvar (void)ctanh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)ctanhl(ldc); } void uninitvar_feclearexcept(void) { int i; // cppcheck-suppress uninitvar (void)feclearexcept(i); } void uninitvar_fegetexceptflag(fexcept_t* flagp) { int excepts; // cppcheck-suppress uninitvar (void)fegetexceptflag(flagp, excepts); } void uninitvar_feraiseexcept(void) { int excepts; // cppcheck-suppress uninitvar (void)feraiseexcept(excepts); } void uninitvar_fesetenv(void) { fenv_t* envp; // cppcheck-suppress uninitvar (void)fesetenv(envp); } void uninitvar_fesetround(void) { int i; // cppcheck-suppress uninitvar (void)fesetround(i); } void uninitvar_fetestexcept(void) { int i; // cppcheck-suppress uninitvar (void)fetestexcept(i); } void uninitvar_feupdateenv(void) { fenv_t* envp; // cppcheck-suppress uninitvar (void)feupdateenv(envp); } void uninitvar_atan(void) { float f; // cppcheck-suppress uninitvar (void)atanf(f); double d; // cppcheck-suppress uninitvar (void)atan(d); long double ld; // cppcheck-suppress uninitvar (void)atanl(ld); } void uninitvar_catan(void) { float complex fd; // cppcheck-suppress uninitvar (void)catanf(fd); double complex dc; // cppcheck-suppress uninitvar (void)catan(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)catanl(ldc); } void uninitvar_tgamma(void) { float f; // cppcheck-suppress uninitvar (void)tgammaf(f); double d; // cppcheck-suppress uninitvar (void)tgamma(d); long double ld; // cppcheck-suppress uninitvar (void)tgammal(ld); } void uninitvar_trunc(void) { float f; // cppcheck-suppress uninitvar (void)truncf(f); double d; // cppcheck-suppress uninitvar (void)trunc(d); long double ld; // cppcheck-suppress uninitvar (void)truncl(ld); } void uninitvar_atanh(void) { float f; // cppcheck-suppress uninitvar (void)atanhf(f); double d; // cppcheck-suppress uninitvar (void)atanh(d); long double ld; // cppcheck-suppress uninitvar (void)atanhl(ld); } void uninitvar_catanh(void) { float complex fd; // cppcheck-suppress uninitvar (void)catanhf(fd); double complex dc; // cppcheck-suppress uninitvar (void)catanh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)catanhl(ldc); } void uninitvar_atan2(void) { float f1,f2; // cppcheck-suppress uninitvar (void)atan2f(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)atan2(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)atan2l(ld1,ld2); } void uninitvar_atof(void) { char * c; // cppcheck-suppress uninitvar (void)atof(c); } void uninitvar_atol(void) { char * c; // cppcheck-suppress uninitvar (void)atoi(c); // cppcheck-suppress uninitvar (void)atol(c); // cppcheck-suppress uninitvar (void)atoll(c); } void uninitvar_calloc(void) { size_t nitems; size_t size; // cppcheck-suppress uninitvar int * p = (int*) calloc(nitems, size); free(p); } void uninitvar_ceil(void) { float f; // cppcheck-suppress uninitvar (void)ceilf(f); double d; // cppcheck-suppress uninitvar (void)ceil(d); long double ld; // cppcheck-suppress uninitvar (void)ceill(ld); } void uninitvar_copysign(void) { float f1, f2; // cppcheck-suppress uninitvar (void)copysignf(f1, f2); double d1, d2; // cppcheck-suppress uninitvar (void)copysign(d1, d2); long double ld1, ld2; // cppcheck-suppress uninitvar (void)copysignl(ld1, ld2); } void uninitvar_cbrt(void) { float f; // cppcheck-suppress uninitvar (void)cbrtf(f); double d; // cppcheck-suppress uninitvar (void)cbrt(d); long double ld; // cppcheck-suppress uninitvar (void)cbrtl(ld); } void uninitvar_cos(void) { float f; // cppcheck-suppress uninitvar (void)cosf(f); double d; // cppcheck-suppress uninitvar (void)cos(d); long double ld; // cppcheck-suppress uninitvar (void)cosl(ld); } void uninitvar_ccos(void) { float complex fd; // cppcheck-suppress uninitvar (void)ccosf(fd); double complex dc; // cppcheck-suppress uninitvar (void)ccos(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)ccosl(ldc); } void uninitvar_cosh(void) { float f; // cppcheck-suppress uninitvar (void)coshf(f); double d; // cppcheck-suppress uninitvar (void)cosh(d); long double ld; // cppcheck-suppress uninitvar (void)coshl(ld); } void uninitvar_ccosh(void) { float complex fd; // cppcheck-suppress uninitvar (void)ccoshf(fd); double complex dc; // cppcheck-suppress uninitvar (void)ccosh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)ccoshl(ldc); } void uninitvar_ctime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)ctime(tp); } void uninitvar_difftime(void) { time_t t1,t2; // cppcheck-suppress uninitvar (void)difftime(t1, t2); } void uninitvar_div(void) { int num; int denom; // cppcheck-suppress uninitvar (void)div(num,denom); } void uninitvar_exit(void) { int i; // cppcheck-suppress uninitvar exit(i); } void uninitvar_erf(void) { float f; // cppcheck-suppress uninitvar (void)erff(f); double d; // cppcheck-suppress uninitvar (void)erf(d); long double ld; // cppcheck-suppress uninitvar (void)erfl(ld); } void uninitvar_erfc(void) { float f; // cppcheck-suppress uninitvar (void)erfcf(f); double d; // cppcheck-suppress uninitvar (void)erfc(d); long double ld; // cppcheck-suppress uninitvar (void)erfcl(ld); } void uninitvar_carg(void) { float complex fd; // cppcheck-suppress uninitvar (void)cargf(fd); double complex dc; // cppcheck-suppress uninitvar (void)carg(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cargl(ldc); } void uninitvar_exp(void) { float f; // cppcheck-suppress uninitvar (void)expf(f); double d; // cppcheck-suppress uninitvar (void)exp(d); long double ld; // cppcheck-suppress uninitvar (void)expl(ld); } void uninitvar_cexp(void) { float complex fd; // cppcheck-suppress uninitvar (void)cexpf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cexp(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cexpl(ldc); } void uninitvar_cimag(void) { float complex fd; // cppcheck-suppress uninitvar (void)cimagf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cimag(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cimagl(ldc); } void uninitvar_exp2(void) { float f; // cppcheck-suppress uninitvar (void)exp2f(f); double d; // cppcheck-suppress uninitvar (void)exp2(d); long double ld; // cppcheck-suppress uninitvar (void)exp2l(ld); } void uninitvar_expm1(void) { float f; // cppcheck-suppress uninitvar (void)expm1f(f); double d; // cppcheck-suppress uninitvar (void)expm1(d); long double ld; // cppcheck-suppress uninitvar (void)expm1l(ld); } void uninitvar_fabs(void) { float f; // cppcheck-suppress uninitvar (void)fabsf(f); double d; // cppcheck-suppress uninitvar (void)fabs(d); long double ld; // cppcheck-suppress uninitvar (void)fabsl(ld); } void uninitvar_fdim(void) { float f1,f2; // cppcheck-suppress uninitvar (void)fdimf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)fdim(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)fdiml(ld1,ld2); } void uninitvar_fgetwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)fgetwc(stream); } void uninitvar_floor(void) { float f; // cppcheck-suppress uninitvar (void)floorf(f); double d; // cppcheck-suppress uninitvar (void)floor(d); long double ld; // cppcheck-suppress uninitvar (void)floorl(ld); } void uninitvar_fma(void) { float f1,f2,f3; // cppcheck-suppress uninitvar (void)fmaf(f1,f2,f3); double d1,d2,d3; // cppcheck-suppress uninitvar (void)fma(d1,d2,d3); long double ld1,ld2,ld3; // cppcheck-suppress uninitvar (void)fmal(ld1,ld2,ld3); } void uninitvar_fmax(void) { float f1,f2; // cppcheck-suppress uninitvar (void)fmaxf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)fmax(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)fmaxl(ld1,ld2); } void uninitvar_fmin(void) { float f1,f2; // cppcheck-suppress uninitvar (void)fminf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)fmin(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)fminl(ld1,ld2); } void uninitvar_fmod(void) { float f1,f2; // cppcheck-suppress uninitvar (void)fmodf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)fmod(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)fmodl(ld1,ld2); } void uninitvar_fprintf(FILE *Stream, char *Format, int Argument) { FILE *stream; char *format1, *format2; int argument1, argument2; // cppcheck-suppress uninitvar (void)fprintf(stream, format1, argument1); // cppcheck-suppress uninitvar (void)fprintf(stream, Format, Argument); // cppcheck-suppress uninitvar (void)fprintf(Stream, format2, Argument); // cppcheck-suppress uninitvar (void)fprintf(Stream, Format, argument2); // no warning is expected (void)fprintf(Stream, Format, Argument); } void uninitvar_vfprintf(FILE *Stream, const char *Format, va_list Arg) { FILE *stream; char *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfprintf(stream, format1, arg); // cppcheck-suppress uninitvar (void)vfprintf(stream, Format, Arg); // cppcheck-suppress uninitvar (void)vfprintf(Stream, format2, Arg); // no warning is expected (void)vfprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vfprintf(Stream, Format, arg); } void uninitvar_vfwprintf(FILE *Stream, wchar_t *Format, va_list Arg) { FILE *stream; wchar_t *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfwprintf(stream, format1, arg); // cppcheck-suppress uninitvar (void)vfwprintf(stream, Format, Arg); // cppcheck-suppress uninitvar (void)vfwprintf(Stream, format2, Arg); // no warning is expected (void)vfwprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vfwprintf(Stream, Format, arg); } void uninitvar_fputwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)fputwc(c,stream); } void uninitvar_fputws(void) { wchar_t *string; FILE *stream; // cppcheck-suppress uninitvar (void)fputws(string,stream); } void uninitvar_fread(void) { void *ptr; size_t size; size_t nobj; FILE *stream; // cppcheck-suppress uninitvar (void)fread(ptr,size,nobj,stream); } void uninitvar_free(void) { // cppcheck-suppress unassignedVariable void *block; // cppcheck-suppress uninitvar free(block); } void uninitvar_freopen(void) { char *filename; char *mode; FILE *stream; // cppcheck-suppress uninitvar FILE * p = freopen(filename,mode,stream); fclose(p); } void uninitvar_frexp(void) { float f1; int *i1; // cppcheck-suppress uninitvar (void)frexpf(f1,i1); double d1; int *i2; // cppcheck-suppress uninitvar (void)frexp(d1,i2); long double ld1; int *i3; // cppcheck-suppress uninitvar (void)frexpl(ld1,i3); } void uninitvar_hypot(void) { float f1,f2; // cppcheck-suppress uninitvar (void)hypotf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)hypot(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)hypotl(ld1,ld2); } void uninitvar_fscanf(void) { FILE *stream; char *format; int i; // cppcheck-suppress uninitvar (void)fscanf(stream,format,i); } void uninitvar_vfscanf(void) { FILE *stream; char * format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfscanf(stream,format,arg); } void uninitvar_vfwscanf(void) { FILE *stream; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vfwscanf(stream,format,arg); } void uninitvar_fseek(void) { FILE* stream; long int offset; int origin; // cppcheck-suppress uninitvar (void)fseek(stream,offset,origin); } void uninitvar_fgetws(void) { wchar_t *buffer; int n; FILE *stream; // cppcheck-suppress uninitvar (void)fgetws(buffer,n,stream); } void uninitvar_fwide(void) { FILE *stream; int mode; // cppcheck-suppress uninitvar (void)fwide(stream,mode); } void uninitvar_fwrite(void) { void *ptr; size_t size; size_t nobj; FILE *stream; // cppcheck-suppress uninitvar (void)fwrite(ptr,size,nobj,stream); } void uninitvar_mblen(void) { char *string; size_t size; // cppcheck-suppress uninitvar (void)mblen(string,size); } void uninitvar_mbtowc(void) { wchar_t* pwc; char* pmb; size_t max; // cppcheck-suppress uninitvar (void)mbtowc(pwc,pmb,max); } void uninitvar_mbrlen(const char* p, size_t m, mbstate_t* s) { char* pmb; size_t max1, max2; mbstate_t* ps1, *ps2; // cppcheck-suppress uninitvar (void)mbrlen(pmb,max1,ps1); // cppcheck-suppress uninitvar (void)mbrlen(pmb,m,s); // cppcheck-suppress uninitvar (void)mbrlen(p,max2,s); // cppcheck-suppress uninitvar (void)mbrlen(p,m,ps2); // no warning is expected (void)mbrlen(p,m,s); } void nullPointer_mbrlen(const char* p, size_t m, mbstate_t* s) { /* no warning is expected: A call to the function with a null pointer as pmb resets the shift state (and ignores parameter max). */ (void)mbrlen(NULL,m,s); (void)mbrlen(NULL,0,s); /* cppcheck-suppress nullPointer */ (void)mbrlen(p,m,NULL); } void uninitvar_btowc(void) { int c; // cppcheck-suppress uninitvar (void)btowc(c); } void uninitvar_mbsinit(void) { mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbsinit(ps); } void uninitvar_mbstowcs(wchar_t* d, const char* s, size_t m) { wchar_t *dest; char *src; size_t max; // cppcheck-suppress uninitvar (void)mbstowcs(dest,s,m); // cppcheck-suppress uninitvar (void)mbstowcs(d,src,m); // cppcheck-suppress uninitvar (void)mbstowcs(d,s,max); // No warning is expected (void)mbstowcs(d,s,m); } void uninitvar_mbsrtowcs(wchar_t* d, const char** s, size_t m, mbstate_t *p) { wchar_t* dest; const char* src; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbsrtowcs(dest,s,m,p); // cppcheck-suppress uninitvar (void)mbsrtowcs(d,&src,m,p); // cppcheck-suppress uninitvar (void)mbsrtowcs(d,s,max,p); // cppcheck-suppress uninitvar (void)mbsrtowcs(d,s,m,ps); // No warning is expected (void)mbsrtowcs(d,s,m,p); } void uninitvar_wctob(void) { wint_t wc; // cppcheck-suppress uninitvar (void)wctob(wc); } void uninitvar_wctomb(void) { char *s; wchar_t wc; // cppcheck-suppress uninitvar (void)wctomb(s,wc); } void uninitvar_wcstombs(void) { char *mbstr; wchar_t *wcstr; size_t n; // cppcheck-suppress uninitvar (void)wcstombs(mbstr,wcstr,n); } void uninitvar_getc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)getc(stream); } void uninitvar_getwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)getwc(stream); } void uninitvar_ungetc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)ungetc(c,stream); } void uninitvar_ungetwc(void) { wint_t c; FILE *stream; // cppcheck-suppress uninitvar (void)ungetwc(c,stream); } void uninitvar_getenv(void) { char *name; // cppcheck-suppress uninitvar (void)getenv(name); } void uninitvar_gets(void) { char *buffer; // cppcheck-suppress getsCalled // cppcheck-suppress uninitvar (void)gets(buffer); } void uninitvar_gmtime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)gmtime(tp); } void uninitvar_isalnum(void) { int i; // cppcheck-suppress uninitvar (void)isalnum(i); } void uninitvar_iswalnum(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswalnum(i); } void uninitvar_isalpha(void) { int i; // cppcheck-suppress uninitvar (void)isalpha(i); } void uninitvar_iswalpha(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswalpha(i); } void uninitvar_isblank(void) { int i; // cppcheck-suppress uninitvar (void)isblank(i); } void uninitvar_iswblank(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswblank(i); } void uninitvar_iscntrl(void) { int i; // cppcheck-suppress uninitvar (void)iscntrl(i); } void uninitvar_iswcntrl(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswcntrl(i); } void uninitvar_iswctype(void) { wint_t c; wctype_t desc; // cppcheck-suppress uninitvar (void)iswctype(c,desc); } void uninitvar_isdigit(void) { int i; // cppcheck-suppress uninitvar (void)isdigit(i); } void uninitvar_iswdigit(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswdigit(i); } void uninitvar_isgraph(void) { int i; // cppcheck-suppress uninitvar (void)isgraph(i); } void uninitvar_iswgraph(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswgraph(i); } void uninitvar_islower(void) { int i; // cppcheck-suppress uninitvar (void)islower(i); } void uninitvar_iswlower(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswlower(i); } void uninitvar_isprint(void) { int i; // cppcheck-suppress uninitvar (void)isprint(i); } void uninitvar_iswprint(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswprint(i); } void uninitvar_ispunct(void) { int i; // cppcheck-suppress uninitvar (void)ispunct(i); } void uninitvar_iswpunct(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswpunct(i); } void uninitvar_isspace(void) { int i; // cppcheck-suppress uninitvar (void)isspace(i); } void uninitvar_iswspace(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswspace(i); } void uninitvar_isupper(void) { int i; // cppcheck-suppress uninitvar (void)isupper(i); } void uninitvar_iswupper(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswupper(i); } void uninitvar_isxdigit(void) { int i; // cppcheck-suppress uninitvar (void)isxdigit(i); } void uninitvar_iswxdigit(void) { wint_t i; // cppcheck-suppress uninitvar (void)iswxdigit(i); } void uninitvar_towctrans(void) { wint_t c; wctrans_t desc; // cppcheck-suppress uninitvar (void)towctrans(c,desc); } void uninitvar_towlower(void) { wint_t i; // cppcheck-suppress uninitvar (void)towlower(i); } void uninitvar_towupper(void) { wint_t i; // cppcheck-suppress uninitvar (void)towupper(i); } void uninitvar_wctrans(void) { char* property; // cppcheck-suppress uninitvar (void)wctrans(property); } void uninitvar_wctype(void) { char* property; // cppcheck-suppress uninitvar (void)wctype(property); } void ignorereturn(void) { char szNumbers[] = "2001 60c0c0 -1101110100110100100000 0x6fffff"; char * pEnd; strtol(szNumbers,&pEnd,10); } void uninitvar_cabs(void) { float complex fd; // cppcheck-suppress uninitvar (void)cabsf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cabs(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cabsl(ldc); } void uninitvar_cacos(void) { float complex fd; // cppcheck-suppress uninitvar (void)cacosf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cacos(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cacosl(ldc); } void uninitvar_cacosh(void) { float complex fd; // cppcheck-suppress uninitvar (void)cacoshf(fd); double complex dc; // cppcheck-suppress uninitvar (void)cacosh(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)cacoshl(ldc); } void uninitvar_labs(void) { long int li; // cppcheck-suppress uninitvar (void)labs(li); long long int lli; // cppcheck-suppress uninitvar (void)llabs(lli); } void uninitvar_ldexp(void) { float f; int e1; // cppcheck-suppress uninitvar (void)ldexpf(f,e1); double d; int e2; // cppcheck-suppress uninitvar (void)ldexp(d,e2); long double ld; int e3; // cppcheck-suppress uninitvar (void)ldexpl(ld,e3); } void uninitvar_lgamma(void) { float f; // cppcheck-suppress uninitvar (void)lgammaf(f); double d; // cppcheck-suppress uninitvar (void)lgamma(d); long double ld; // cppcheck-suppress uninitvar (void)lgammal(ld); } void uninitvar_rint(void) { float f; // cppcheck-suppress uninitvar (void)rintf(f); double d; // cppcheck-suppress uninitvar (void)rint(d); long double ld; // cppcheck-suppress uninitvar (void)rintl(ld); } void uninitvar_lrint(void) { float f; // cppcheck-suppress uninitvar (void)lrintf(f); double d; // cppcheck-suppress uninitvar (void)lrint(d); long double ld; // cppcheck-suppress uninitvar (void)lrintl(ld); } void uninitvar_llrint(void) { float f; // cppcheck-suppress uninitvar (void)llrintf(f); double d; // cppcheck-suppress uninitvar (void)llrint(d); long double ld; // cppcheck-suppress uninitvar (void)llrintl(ld); } void uninitvar_lround(void) { float f; // cppcheck-suppress uninitvar (void)lroundf(f); double d; // cppcheck-suppress uninitvar (void)lround(d); long double ld; // cppcheck-suppress uninitvar (void)lroundl(ld); } void uninitvar_llround(void) { float f; // cppcheck-suppress uninitvar (void)llroundf(f); double d; // cppcheck-suppress uninitvar (void)llround(d); long double ld; // cppcheck-suppress uninitvar (void)llroundl(ld); } void uninitvar_srand(void) { unsigned int seed; // cppcheck-suppress uninitvar (void)srand(seed); } void uninitvar_ldiv(void) { long int l1; long int l2; // cppcheck-suppress uninitvar (void)ldiv(l1,l2); long long int ll1; long long int ll2; // cppcheck-suppress uninitvar (void)lldiv(ll1,ll2); } void uninitvar_localtime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)localtime(tp); } void uninitvar_log(void) { float f; // cppcheck-suppress uninitvar (void)logf(f); double d; // cppcheck-suppress uninitvar (void)log(d); long double ld; // cppcheck-suppress uninitvar (void)logl(ld); } void uninitvar_clog(void) { float complex fc; // cppcheck-suppress uninitvar (void)clogf(fc); double complex dc; // cppcheck-suppress uninitvar (void)clog(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)clogl(ldc); } void uninitvar_conj(void) { float complex fc; // cppcheck-suppress uninitvar (void)conjf(fc); double complex dc; // cppcheck-suppress uninitvar (void)conj(dc); long double complex ldc; // cppcheck-suppress uninitvar (void)conjl(ldc); } void uninitvar_fpclassify(void) { float f; // cppcheck-suppress uninitvar (void)fpclassify(f); double d; // cppcheck-suppress uninitvar (void)fpclassify(d); long double ld; // cppcheck-suppress uninitvar (void)fpclassify(ld); } void uninitvar_isfinite(void) { float f; // cppcheck-suppress uninitvar (void)isfinite(f); double d; // cppcheck-suppress uninitvar (void)isfinite(d); long double ld; // cppcheck-suppress uninitvar (void)isfinite(ld); } void uninitvar_isgreater(void) { float f1,f2; // cppcheck-suppress uninitvar (void)isgreater(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)isgreater(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)isgreater(ld1,ld2); } void uninitvar_isgreaterequal(void) { float f1,f2; // cppcheck-suppress uninitvar (void)isgreaterequal(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)isgreaterequal(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)isgreaterequal(ld1,ld2); } void uninitvar_isinf(void) { float f; // cppcheck-suppress uninitvar (void)isinf(f); double d; // cppcheck-suppress uninitvar (void)isinf(d); long double ld; // cppcheck-suppress uninitvar (void)isinf(ld); } void uninitvar_logb(void) { float f; // cppcheck-suppress uninitvar (void)logbf(f); double d; // cppcheck-suppress uninitvar (void)logb(d); long double ld; // cppcheck-suppress uninitvar (void)logbl(ld); } void uninitvar_isless(void) { float f1,f2; // cppcheck-suppress uninitvar (void)isless(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)isless(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)isless(ld1,ld2); } void uninitvar_islessequal(void) { float f1,f2; // cppcheck-suppress uninitvar (void)islessequal(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)islessequal(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)islessequal(ld1,ld2); } void uninitvar_islessgreater(void) { float f1,f2; // cppcheck-suppress uninitvar (void)islessgreater(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)islessgreater(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)islessgreater(ld1,ld2); } void uninitvar_nan(void) { char *tagp; // cppcheck-suppress uninitvar (void)nanf(tagp); // cppcheck-suppress uninitvar (void)nan(tagp); // cppcheck-suppress uninitvar (void)nanl(tagp); } void uninitvar_isnan(void) { double d; // cppcheck-suppress uninitvar (void)isnan(d); } void uninitvar_isnormal(void) { double d; // cppcheck-suppress uninitvar (void)isnormal(d); } void uninitvar_isunordered(void) { double d1,d2; // cppcheck-suppress uninitvar (void)isunordered(d1,d2); } void uninitvar_ilogb(void) { float f; // cppcheck-suppress uninitvar (void)ilogbf(f); double d; // cppcheck-suppress uninitvar (void)ilogb(d); long double ld; // cppcheck-suppress uninitvar (void)ilogbl(ld); } void uninitvar_log10(void) { float f; // cppcheck-suppress uninitvar (void)log10f(f); double d; // cppcheck-suppress uninitvar (void)log10(d); long double ld; // cppcheck-suppress uninitvar (void)log10l(ld); } void uninitvar_log1p(void) { float f; // cppcheck-suppress uninitvar (void)log1pf(f); double d; // cppcheck-suppress uninitvar (void)log1p(d); long double ld; // cppcheck-suppress uninitvar (void)log1pl(ld); } void uninitvar_log2(void) { float f; // cppcheck-suppress uninitvar (void)log2f(f); double d; // cppcheck-suppress uninitvar (void)log2(d); long double ld; // cppcheck-suppress uninitvar (void)log2l(ld); } void uninitvar_nearbyint(void) { float f; // cppcheck-suppress uninitvar (void)nearbyintf(f); double d; // cppcheck-suppress uninitvar (void)nearbyint(d); long double ld; // cppcheck-suppress uninitvar (void)nearbyintl(ld); } void uninitvar_nextafter(void) { float f1,f2; // cppcheck-suppress uninitvar (void)nextafterf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)nextafter(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)nextafterl(ld1,ld2); } void uninitvar_nexttoward(void) { float f1,f2; // cppcheck-suppress uninitvar (void)nexttowardf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)nexttoward(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)nexttowardl(ld1,ld2); } void uninitvar_longjmp(void) { jmp_buf env; int val; // cppcheck-suppress uninitvar (void)longjmp(env,val); } void uninitvar_malloc(void) { size_t size; // cppcheck-suppress uninitvar int *p = (int*)malloc(size); free(p); } void uninitvar_alloca(void) { size_t size; // cppcheck-suppress allocaCalled // cppcheck-suppress uninitvar (void)alloca(size); } void uninitvar_memchr(void) { void *cs; int c; size_t n; // cppcheck-suppress uninitvar (void)memchr(cs,c,n); } void uninitvar_wmemchr(void) { wchar_t *cs; wchar_t c; size_t n; // cppcheck-suppress uninitvar (void)wmemchr(cs,c,n); } void uninitvar_memcmp(void) { void *s1; void *s2; size_t n; // cppcheck-suppress uninitvar (void)memcmp(s1,s2,n); } void uninitvar_wmemcmp(void) { wchar_t *s1; wchar_t *s2; size_t n; // cppcheck-suppress uninitvar (void)wmemcmp(s1,s2,n); } void uninitvar_memcpy(void) { void *ct; void *cs; size_t n; // cppcheck-suppress uninitvar (void)memcpy(ct,cs,n); } void uninitvar_wmemcpy(void) { wchar_t *cs; wchar_t *c; size_t n; // cppcheck-suppress uninitvar (void)wmemcpy(cs,c,n); } void uninitvar_memmove(void) { void *ct; void *cs; size_t n; // cppcheck-suppress uninitvar (void)memmove(ct,cs,n); } void uninitvar_wmemmove(void) { wchar_t *cs; wchar_t *c; size_t n; // cppcheck-suppress uninitvar (void)wmemmove(cs,c,n); } void uninitvar_memset(void) { void *s; int c; size_t n; // cppcheck-suppress uninitvar (void)memset(s,c,n); } void uninitvar_wmemset(void) { wchar_t *cs; wchar_t c; size_t n; // cppcheck-suppress uninitvar (void)wmemset(cs,c,n); } void uninitvar_mktime(void) { struct tm *tp; // cppcheck-suppress uninitvar (void)mktime(tp); struct tmx *tpx; // cppcheck-suppress uninitvar (void)mkxtime(tpx); } void uninitvar_modf(void) { float f1; float *f2; // cppcheck-suppress uninitvar (void)modff(f1,f2); double d1; double *d2; // cppcheck-suppress uninitvar (void)modf(d1,d2); long double ld1; long double *ld2; // cppcheck-suppress uninitvar (void)modfl(ld1,ld2); } void uninitvar_perror(void) { char *string; // cppcheck-suppress uninitvar (void)perror(string); } void uninitvar_pow(void) { float f1,f2; // cppcheck-suppress uninitvar (void)powf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)pow(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)powl(ld1,ld2); } void uninitvar_cpow(void) { float complex f1,f2; // cppcheck-suppress uninitvar (void)cpowf(f1,f2); double complex d1,d2; // cppcheck-suppress uninitvar (void)cpow(d1,d2); long double complex ld1,ld2; // cppcheck-suppress uninitvar (void)cpowl(ld1,ld2); } void uninitvar_remainder(void) { float f1,f2; // cppcheck-suppress uninitvar (void)remainderf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)remainder(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)remainderl(ld1,ld2); } void uninitvar_remquo(void) { float f1,f2; int *i1; // cppcheck-suppress uninitvar (void)remquof(f1,f2,i1); double d1,d2; int *i2; // cppcheck-suppress uninitvar (void)remquo(d1,d2,i2); long double ld1,ld2; int *i3; // cppcheck-suppress uninitvar (void)remquol(ld1,ld2,i3); } void uninitvar_printf(char *Format, int Argument) { char * format_1, * format_2, * format_3; int argument1, argument2; // no warning is expected (void)printf("x"); // cppcheck-suppress uninitvar (void)printf(format_1,argument1); // cppcheck-suppress uninitvar (void)printf(Format,argument2); // cppcheck-suppress uninitvar (void)printf(format_2,Argument); // cppcheck-suppress uninitvar (void)printf(format_3,1); // no warning is expected (void)printf(Format,Argument); } void uninitvar_vprintf(char *Format, va_list Arg) { char * format1, *format2; va_list arg1, arg2; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vprintf(format1,arg1); // cppcheck-suppress uninitvar (void)vprintf(format2,Arg); // no warning is expected (void)vprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vprintf(Format,arg2); } void uninitvar_vwprintf(wchar_t *Format, va_list Arg) { wchar_t * format1, * format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vwprintf(format1,arg); // cppcheck-suppress uninitvar (void)vwprintf(format2,Arg); // no warning is expected (void)vwprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vwprintf(Format,arg); } void uninitvar_bsearch(void) { void* key; void* base; size_t num; size_t size; // cppcheck-suppress uninitvar (void)bsearch(key,base,num,size,(int(*)(const void*,const void*)) strcmp); } void uninitvar_qsort(void) { void *base; size_t n; size_t size; // cppcheck-suppress uninitvar (void)qsort(base,n,size, (int(*)(const void*,const void*)) strcmp); } void uninitvar_putc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)putc(c,stream); } void uninitvar_putwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)putc(c,stream); } void uninitvar_putwchar(void) { wchar_t c; // cppcheck-suppress uninitvar (void)putwchar(c); } void uninitvar_realloc(void) { void *block; size_t newsize; // cppcheck-suppress uninitvar void *p = realloc(block, newsize); free(p); } void uninitvar_remove(void) { char *s; // cppcheck-suppress uninitvar (void)remove(s); } void uninitvar_rename(void) { char *s1; char *s2; // cppcheck-suppress uninitvar (void)rename(s1,s2); } void uninitvar_rewind(void) { FILE *f; // cppcheck-suppress uninitvar (void)rewind(f); } void uninitvar_round(void) { float f; // cppcheck-suppress uninitvar (void)roundf(f); double d; // cppcheck-suppress uninitvar (void)round(d); long double ld; // cppcheck-suppress uninitvar (void)roundl(ld); } void uninitvar_scalbn(void) { float f; int i1; // cppcheck-suppress uninitvar (void)scalbnf(f,i1); double d; int i2; // cppcheck-suppress uninitvar (void)scalbn(d,i2); long double ld; int i3; // cppcheck-suppress uninitvar (void)scalbnl(ld,i3); } void uninitvar_scalbln(void) { float f; long int i1; // cppcheck-suppress uninitvar (void)scalblnf(f,i1); double d; long int i2; // cppcheck-suppress uninitvar (void)scalbln(d,i2); long double ld; long int i3; // cppcheck-suppress uninitvar (void)scalblnl(ld,i3); } void uninitvar_signbit(void) { double d; // cppcheck-suppress uninitvar (void)signbit(d); } void uninitvar_signal(void) { int i; // cppcheck-suppress uninitvar signal(i, exit); } void uninitvar_raise(void) { int i; // cppcheck-suppress uninitvar (void)raise(i); } void uninitvar_scanf(void) { char *format; char str[42]; // cppcheck-suppress uninitvar (void)scanf(format, str); // no warning is expected (#9347) int i; sscanf("0", "%d", &i); } void uninitvar_vsscanf(void) { char *s; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vsscanf(s,format,arg); } void uninitvar_vswscanf(void) { wchar_t *s; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vswscanf(s,format,arg); } void uninitvar_vscanf(void) { char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vscanf(format,arg); } void uninitvar_vwscanf(void) { wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vwscanf(format,arg); } void uninitvar_setbuf(void) { FILE *stream; char *buf; // cppcheck-suppress uninitvar (void)setbuf(stream,buf); } void uninitvar_setvbuf(void) { FILE *stream; char *buf; int mode; size_t size; // cppcheck-suppress uninitvar (void)setvbuf(stream,buf,mode,size); } void uninitvar_strcat(char *dest, const char * const source) { char *deststr1, *deststr2; char *srcstr; // cppcheck-suppress uninitvar (void)strcat(deststr1,srcstr); // cppcheck-suppress uninitvar (void)strcat(dest,srcstr); // cppcheck-suppress uninitvar (void)strcat(deststr2,source); // no warning shall be shown for (void)strcat(dest,source); } void bufferAccessOutOfBounds_strcat(char *dest, const char * const source) { char buf4[4] = {0}; const char * const srcstr3 = "123"; const char * const srcstr4 = "1234"; // @todo #8599 cppcheck-suppress bufferAccessOutOfBounds (void)strcat(buf4,srcstr4); // off by one issue: strcat is appends \0' at the end // no warning shall be shown for (void)strcat(dest,source); (void)strcat(buf4,srcstr3); // strcat appends '\0' at the end (void)strcat(dest,srcstr4); // Cppcheck does not know the length of 'dest' } void uninitvar_wcscat(wchar_t *dest, const wchar_t * const source) { wchar_t *deststr_1, *deststr_2; wchar_t *srcstr_1, *srcstr_2; // cppcheck-suppress uninitvar (void)wcscat(deststr_1,srcstr_1); // cppcheck-suppress uninitvar (void)wcscat(dest,srcstr_2); // cppcheck-suppress uninitvar (void)wcscat(deststr_2,source); // no warning shall be shown for (void)wcscat(dest,source); } void uninitvar_wcrtomb(void) { char *s; wchar_t wc; mbstate_t *ps; // cppcheck-suppress uninitvar (void)wcrtomb(s,wc,ps); } void uninitvar_strchr(void) { char *cs; int c; // cppcheck-suppress uninitvar (void)strchr(cs,c); } void invalidFunctionArg_strchr(char *cs, int c) { // cppcheck-suppress invalidFunctionArg (void)strchr(cs,-1); // No warning shall be issued for (void)strchr(cs, 0); (void)strchr(cs, 255); // cppcheck-suppress invalidFunctionArg (void)strchr(cs, 256); } void uninitvar_wcschr(void) { wchar_t *cs; wchar_t c; // cppcheck-suppress uninitvar (void)wcschr(cs,c); } void uninitvar_strcmp(char *s1, char *s2) { char *str1; char *str2; // cppcheck-suppress uninitvar (void)strcmp(str1,s2); // cppcheck-suppress uninitvar (void)strcmp(s1,str2); // cppcheck-suppress uninitvar (void)strcmp(str1,str2); // No warning is expected (void)strcmp(s1,s2); } void uninitvar_wcscmp(wchar_t *s1, wchar_t *s2) { wchar_t *str1; wchar_t *str2; // cppcheck-suppress uninitvar (void)wcscmp(str1,s2); // cppcheck-suppress uninitvar (void)wcscmp(s1,str2); // cppcheck-suppress uninitvar (void)wcscmp(str1,str2); // No warning is expected (void)wcscmp(s1,s2); } void uninitvar_strcpy(char *d, char *s) { char *dest; char *src; // cppcheck-suppress uninitvar (void)strcpy(dest,s); // cppcheck-suppress uninitvar (void)strcpy(d,src); // cppcheck-suppress uninitvar (void)strcpy(dest,src); // No warning is expected (void)strcpy(d,s); } void uninitvar_strcpy_s(char * strDest, ssize_t s, char *source) { char *strUninit1; char *strUninit2; ssize_t size; // cppcheck-suppress uninitvar (void)strcpy_s(strUninit1, 1, "a"); // cppcheck-suppress uninitvar (void)strcpy_s(strDest, 1, strUninit2); // cppcheck-suppress uninitvar (void)strcpy_s(strDest, size, "a"); // No warning is expected (void)strcpy_s(strDest, s, source); } void uninitvar_wcscpy(wchar_t *d, wchar_t*s) { wchar_t *dest; wchar_t *src; // cppcheck-suppress uninitvar (void)wcscpy(dest,s); // cppcheck-suppress uninitvar (void)wcscpy(d,src); // cppcheck-suppress uninitvar (void)wcscpy(dest,src); // No warning is expected (void)wcscpy(d,s); } void uninitvar_strftime(void) { char *s; size_t max; char *fmt; struct tm *p; // cppcheck-suppress uninitvar (void)strftime(s,max,fmt,p); struct tmx *px; // cppcheck-suppress uninitvar (void)strfxtime(s,max,fmt,px); } void uninitvar_strlen(const char *str) { char *s; // cppcheck-suppress uninitvar (void)strlen(s); const char x; const char *xPtr = &x; // cppcheck-suppress uninitvar (void)strlen(xPtr); // No warning is expected (void)strlen(str); } void uninitvar_wcslen(void) { wchar_t *s; // cppcheck-suppress uninitvar (void)wcslen(s); } //char * strncpy ( char * destination, const char * source, size_t num ); void uninitvar_strncpy(char * dest, const char * src, size_t num) { char *d; char *s; size_t n; // cppcheck-suppress uninitvar (void)strncpy(d,src,num); // cppcheck-suppress uninitvar (void)strncpy(dest,s,num); // cppcheck-suppress uninitvar (void)strncpy(dest,src,n); // No warning is expected for (void)strncpy(dest,src,num); } void uninitvar_strncpy_s(char *Ct, size_t N1, char *S, size_t N2) { char dest[42]; char *s; size_t n1; size_t n2; size_t n3; size_t n4; // cppcheck-suppress uninitvar (void)strncpy_s(dest,n1,s,n2); // cppcheck-suppress uninitvar (void)strncpy_s(Ct,n3,S,N2); // cppcheck-suppress uninitvar (void)strncpy_s(Ct,N1,s,N2); // cppcheck-suppress uninitvar (void)strncpy_s(Ct,N1,S,n4); // no warning is expected for (void)strncpy_s(Ct,N1,S,N2); (void)strncpy_s(dest,N1,S,N2); } void uninitvar_strpbrk(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strpbrk(cs,ct); } // char * strncat ( char * destination, const char * source, size_t num ); void uninitvar_strncat(char *d, char *s, size_t n) { char *dest; char *src; size_t num; // cppcheck-suppress uninitvar (void)strncat(dest,s,n); // cppcheck-suppress uninitvar (void)strncat(d,src,n); // cppcheck-suppress uninitvar (void)strncat(d,s,num); // no warning is expected for (void)strncat(d,s,n); } // errno_t strcat_s(char *restrict dest, rsize_t destsz, const char *restrict src); // since C11 void uninitvar_strcat_s(char *Ct, size_t N, char *S) { char *ct_1, *ct_2; char *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)strcat_s(ct_1,n1,s); // cppcheck-suppress uninitvar (void)strcat_s(ct_2,N,S); // cppcheck-suppress uninitvar (void)strcat_s(Ct,N,s); // cppcheck-suppress uninitvar (void)strcat_s(Ct,n2,S); // no warning is expected for (void) strcat_s(Ct,N,S); } // errno_t wcscat_s(wchar_t *restrict dest, rsize_t destsz, const wchar_t *restrict src); // since C11 void uninitvar_wcscat_s(wchar_t *Ct, size_t N, wchar_t *S) { wchar_t *ct_1, *ct_2; wchar_t *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)wcscat_s(ct_1,n1,s); // cppcheck-suppress uninitvar (void)wcscat_s(ct_2,N,S); // cppcheck-suppress uninitvar (void)wcscat_s(Ct,N,s); // cppcheck-suppress uninitvar (void)wcscat_s(Ct,n2,S); // no warning is expected for (void) wcscat_s(Ct,N,S); } void uninitvar_strncat_s(char *Ct, size_t N1, char *S, size_t N2) { char *ct_1, *ct_2; char *s; size_t n1; size_t n2; size_t n3; size_t n4; // cppcheck-suppress uninitvar (void)strncat_s(ct_1,n1,s,n2); // cppcheck-suppress uninitvar (void)strncat_s(ct_2,N1,S,N2); // cppcheck-suppress uninitvar (void)strncat_s(Ct,n3,S,N2); // cppcheck-suppress uninitvar (void)strncat_s(Ct,N1,s,N2); // cppcheck-suppress uninitvar (void)strncat_s(Ct,N1,S,n4); // no warning is expected for (void)strncat_s(Ct,N1,S,N2); } void uninitvar_wcsncat(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct_1, *ct_2; wchar_t *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)wcsncat(ct_1,s,n1); // cppcheck-suppress uninitvar (void)wcsncat(ct_2,S,N); // cppcheck-suppress uninitvar (void)wcsncat(Ct,s,N); // cppcheck-suppress uninitvar (void)wcsncat(Ct,S,n2); // no warning is expected for (void)wcsncat(Ct,S,N); } void uninitvar_strncmp(char *Ct, char *S, size_t N) { char *ct; char *s; size_t n1; // cppcheck-suppress uninitvar (void)strncmp(ct,S,N); // cppcheck-suppress uninitvar (void)strncmp(Ct,s,N); // cppcheck-suppress uninitvar (void)strncmp(Ct,S,n1); // no warning is expected for (void)strncmp(Ct,S,N); } void uninitvar_wcsncmp(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct; wchar_t *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)wcsncmp(ct,s,n1); // cppcheck-suppress uninitvar (void)wcsncmp(ct,S,N); // cppcheck-suppress uninitvar (void)wcsncmp(Ct,s,N); // cppcheck-suppress uninitvar (void)wcsncmp(Ct,S,n2); // no warning is expected for (void)wcsncmp(Ct,S,N); } void uninitvar_strstr(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strstr(cs,ct); } void uninitvar_wcsstr(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcsstr(cs,ct); } void uninitvar_strspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strspn(cs,ct); } void uninitvar_strxfrm(void) { char *ds; char *ss; size_t n; // cppcheck-suppress uninitvar (void)strxfrm(ds,ss,n); } void uninitvar_wcsxfrm(void) { wchar_t *ds; wchar_t *ss; size_t n; // cppcheck-suppress uninitvar (void)wcsxfrm(ds,ss,n); } void uninitvar_wcsspn(void) { wchar_t *ds; wchar_t *ss; // cppcheck-suppress uninitvar (void)wcsspn(ds,ss); } void uninitvar_setlocale(void) { int category; char* locale; // cppcheck-suppress uninitvar (void)setlocale(category,locale); } void uninitvar_strerror(void) { int i; // cppcheck-suppress uninitvar (void)strerror(i); } void uninitvar_strcspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strcspn(cs,ct); } void uninitvar_wcscspn(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcscspn(cs,ct); } void uninitvar_wcspbrk(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcspbrk(cs,ct); } void uninitvar_wcsncpy(void) { wchar_t *cs; wchar_t *ct; size_t n; // cppcheck-suppress uninitvar (void)wcsncpy(cs,ct,n); } void uninitvar_strcoll(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)strcoll(cs,ct); } void uninitvar_wcscoll(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)wcscoll(cs,ct); } //const char * strrchr ( const char * str, int character ); // char * strrchr ( char * str, int character ); void uninitvar_strrchr(const char * s, int c) { char * str; int character; // cppcheck-suppress uninitvar (void)strrchr(str,c); // cppcheck-suppress uninitvar (void)strrchr(s,character); // No warning is expected for (void)strrchr(s,c); } void uninitvar_wcsrchr(void) { wchar_t* ws; wchar_t wc; // cppcheck-suppress uninitvar (void)wcsrchr(ws,wc); } void uninitvar_wcsrtombs(void) { char *dst; const wchar_t * p;; size_t len; mbstate_t *ps; // cppcheck-suppress uninitvar (void)wcsrtombs(dst,&p,len,ps); } void uninitvar_strtok(void) { char *s; char *ct; // cppcheck-suppress uninitvar (void)strtok(s,ct); } void uninitvar_strtoimax(void) { const char *s; char **endp; int base; // cppcheck-suppress uninitvar (void)strtoimax(s,endp,base); // cppcheck-suppress uninitvar (void)strtoumax(s,endp,base); } void uninitvar_strtof(void) { const char *s; char **endp; // cppcheck-suppress uninitvar (void)strtof(s,endp); // cppcheck-suppress uninitvar (void)strtod(s,endp); // cppcheck-suppress uninitvar (void)strtold(s,endp); } void uninitvar_strtol(void) { const char *s; char **endp; int base; // cppcheck-suppress uninitvar (void)strtol(s,endp,base); // cppcheck-suppress uninitvar (void)strtoll(s,endp,base); // cppcheck-suppress uninitvar (void)strtoul(s,endp,base); // cppcheck-suppress uninitvar (void)strtoull(s,endp,base); } void uninitvar_time(void) { time_t *tp; // cppcheck-suppress uninitvar (void)time(tp); } void uninitvar_tmpnam(void) { char *s; // cppcheck-suppress uninitvar (void)tmpnam(s); } void uninitvar_tolower(int character) { int c; // cppcheck-suppress uninitvar (void)tolower(c); int *pc=&c; // cppcheck-suppress uninitvar (void)tolower(*pc); // No warning is expected (void)tolower(character); int *pChar = &character; // No warning is expected (void)tolower(*pChar); } void uninitvar_toupper(int character) { int c; // cppcheck-suppress uninitvar (void)toupper(c); int *pc=&c; // cppcheck-suppress uninitvar (void)toupper(*pc); // No warning is expected (void)toupper(character); int *pChar = &character; // No warning is expected (void)toupper(*pChar); } void uninitvar_wcstof(void) { const wchar_t *s; wchar_t **endp; // cppcheck-suppress uninitvar (void)wcstof(s,endp); // cppcheck-suppress uninitvar (void)wcstod(s,endp); // cppcheck-suppress uninitvar (void)wcstold(s,endp); } void uninitvar_mbrtowc(void) { wchar_t* pwc; const char* pmb; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)mbrtowc(pwc,pmb,max,ps); } void uninitvar_wcstok(void) { wchar_t *s; const wchar_t *ct; wchar_t **ptr; // cppcheck-suppress uninitvar (void)wcstok(s,ct,ptr); } void uninitvar_wcstoimax(void) { const wchar_t *s; wchar_t ** endp; int base; // cppcheck-suppress uninitvar (void)wcstoimax(s,endp,base); // cppcheck-suppress uninitvar (void)wcstoumax(s,endp,base); } void uninitvar_wcstol(void) { const wchar_t *s; wchar_t ** endp; int base; // cppcheck-suppress uninitvar (void)wcstol(s,endp,base); // cppcheck-suppress uninitvar (void)wcstoll(s,endp,base); // cppcheck-suppress uninitvar (void)wcstoul(s,endp,base); // cppcheck-suppress uninitvar (void)wcstoull(s,endp,base); } void uninitvar_wprintf(wchar_t *Format, int Argument) { const wchar_t *format1, *format2, *format3; int argument1, argument2; // cppcheck-suppress uninitvar (void)wprintf(format1,argument1); // cppcheck-suppress uninitvar (void)wprintf(format2); // cppcheck-suppress uninitvar (void)wprintf(Format,argument2); // cppcheck-suppress uninitvar (void)wprintf(format3,Argument); // no warning is expected (void)wprintf(Format,Argument); (void)wprintf(Format); } void uninitvar_sprintf(char *S, char *Format, int Argument) { char *s1, *s2; const char *format1, *format2; int argument1, argument2; // cppcheck-suppress uninitvar (void)sprintf(s1,format1,argument1); // cppcheck-suppress uninitvar (void)sprintf(s2,Format,Argument); // cppcheck-suppress uninitvar (void)sprintf(S,format2,Argument); // cppcheck-suppress uninitvar (void)sprintf(S,Format,argument2); // no warning is expected for (void)sprintf(S,Format,Argument); } void uninitvar_swprintf(void) { wchar_t *s; size_t n; const wchar_t *format; // cppcheck-suppress uninitvar (void)swprintf(s,n,format); } void uninitvar_vsprintf(void) { char *s; const char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vsprintf(s,format,arg); } void valid_vsprintf_helper(const char * format, ...) { char buffer[2]; va_list args; va_start(args, format); vsprintf(buffer, format, args); printf(buffer); va_end(args); } void valid_vsprintf() { // buffer will contain "2\0" => no bufferAccessOutOfBounds // cppcheck-suppress checkLibraryFunction // cppcheck-suppress checkLibraryNoReturn valid_vsprintf_helper("%1.0f", 2.0f); } void uninitvar_vswprintf(void) { wchar_t *s; size_t n; const wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vswprintf(s,n,format,arg); } void uninitvar_fwprintf(void) { FILE* stream; const wchar_t* format; int i; // cppcheck-suppress uninitvar (void)fwprintf(stream,format,i); } void uninitvar_snprintf(char *S, size_t N, char *Format, int Int) { size_t n1,n2; char *format; int i1, i2; char *s1, *s2; // cppcheck-suppress uninitvar (void)snprintf(s1,n1,format,i1); // cppcheck-suppress uninitvar (void)snprintf(S,n2,Format,Int); // n is uninitialized // cppcheck-suppress uninitvar (void)snprintf(S,N,format,Int); // format is uninitialized // cppcheck-suppress uninitvar (void)snprintf(S,N,Format,i2); // i is uninitialized // cppcheck-suppress uninitvar (void)snprintf(s2,N,Format,Int); // no warning is expected for (void)snprintf(S,N,Format,Int); } void uninitvar_vsnprintf(char *S, size_t N, char *Format, va_list Arg) { char *s1, *s2; size_t n1, n2; char *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)vsnprintf(s1,n1,format1,arg); // cppcheck-suppress uninitvar (void)vsnprintf(s2,N,Format,Arg); // cppcheck-suppress uninitvar (void)vsnprintf(S,n2,Format,Arg); // cppcheck-suppress uninitvar (void)vsnprintf(S,N,format2,Arg); // no warning is expected for (void)vsnprintf(S,N,Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)vsnprintf(S,N,Format,arg); } void uninitvar_wscanf(void) { wchar_t *format; int i; // cppcheck-suppress uninitvar (void)wscanf(format); // cppcheck-suppress uninitvar (void)wscanf(format,&i); } void uninitvar_sscanf(char *s, const char *f, int i, int *ip) { char *string; const char * format; int *pInteger; // cppcheck-suppress uninitvar (void)sscanf(string,f); // cppcheck-suppress uninitvar (void)sscanf(string,f,i); // cppcheck-suppress uninitvar (void)sscanf(string,f,ip); // cppcheck-suppress uninitvar (void)sscanf(s,format,&i); // cppcheck-suppress uninitvar (void)sscanf(s,f,pInteger); // No warning is expected (void)sscanf(s,f,&i); (void)sscanf(s,f,ip); } void uninitvar_fwscanf(void) { FILE* stream; wchar_t* format1, *format2; int i; // cppcheck-suppress uninitvar (void)fwscanf(stream,format1); // cppcheck-suppress uninitvar (void)fwscanf(stream,format2,&i); } void uninitvar_swscanf(void) { wchar_t* s; wchar_t* format1, *format2; int i; // cppcheck-suppress uninitvar (void)swscanf(s,format1); // cppcheck-suppress uninitvar (void)swscanf(s,format2,&i); } void uninitvar_system(void) { char *c; // cppcheck-suppress uninitvar (void)system(c); } void uninitvar_zonetime(void) { time_t *tp; int zone; // cppcheck-suppress uninitvar (void)zonetime(tp,zone); } void uninitvar_itoa(void) { int value; char * str; int base; // cppcheck-suppress uninitvar (void)itoa(value,str,base); } #ifdef __STD_UTF_16__ void uninitvar_c16rtomb(void) { char * pmb; char16_t c16; mbstate_t * ps; // cppcheck-suppress uninitvar (void)c16rtomb(pmb,c16,ps); } void uninitvar_mbrtoc16(void) { char16_t * pc16; char * pmb; size_t max; mbstate_t * ps; // cppcheck-suppress uninitvar (void)mbrtoc16(pc16,pmb,max,ps); } #endif // __STD_UTF_16__ #ifdef __STD_UTF_32__ void uninitvar_c32rtomb(void) { char * pmb; char32_t c32; mbstate_t * ps; // cppcheck-suppress uninitvar (void)c32rtomb(pmb,c32,ps); } void uninitvar_mbrtoc32(void) { char32_t * pc32; char * pmb; size_t max; mbstate_t * ps; // cppcheck-suppress uninitvar (void)mbrtoc32(pc32,pmb,max,ps); } #endif // __STD_UTF_32__ void invalidFunctionArgBool_abs(bool b, double x, double y) { // cppcheck-suppress invalidFunctionArgBool (void)abs(true); // #6990 // cppcheck-suppress invalidFunctionArgBool (void)abs(b); // #6990 // cppcheck-suppress invalidFunctionArgBool (void)abs(x // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void valid_code() { std::vector vecInt{0, 1, 2}; std::fill_n(vecInt.begin(), 2, 0); vecInt.push_back(1); vecInt.pop_back(); } void returnValue_std_isgreater(void) { // cppcheck-suppress knownConditionTrueFalse if (std::isgreater(4,2) == 0) {} // @todo support floats if (std::isgreater(4.0f,2.0f) == 0) {} } void returnValue_std_isgreaterequal(void) { // cppcheck-suppress knownConditionTrueFalse if (std::isgreaterequal(4,2) == 0) {} // @todo support floats if (std::isgreaterequal(4.0f,2.0f) == 0) {} } void returnValue_std_isless(void) { // cppcheck-suppress knownConditionTrueFalse if (std::isless(4,2) == 0) {} // @todo support floats if (std::isless(4.0f,2.0f) == 0) {} } void returnValue_std_islessequal(void) { // cppcheck-suppress knownConditionTrueFalse if (std::islessequal(4,2) == 0) {} // @todo support floats if (std::islessequal(4.0f,2.0f) == 0) {} } void returnValue_std_islessgreater(void) { // cppcheck-suppress knownConditionTrueFalse if (std::islessgreater(4,2) == 0) {} // cppcheck-suppress knownConditionTrueFalse if (std::islessgreater(2,4) == 0) {} if (std::islessgreater(4.0f,2.0f) == 0) {} // @todo support floats if (std::islessgreater(2.0f,4.0f) == 0) {} // @todo support floats } void bufferAccessOutOfBounds(void) { char a[5]; std::strcpy(a,"abcd"); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy std::strcpy(a, "abcde"); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress terminateStrncpy std::strncpy(a,"abcde",5); // cppcheck-suppress bufferAccessOutOfBounds // TODO cppcheck-suppress redundantCopy std::strncpy(a,"abcde",6); } void uninitvar_abs(void) { int i; // cppcheck-suppress uninitvar (void)std::abs(i); } void uninivar_imaxabs(void) { intmax_t i1, i2; // cppcheck-suppress uninitvar (void)std::imaxabs(i1); // cppcheck-suppress uninitvar (void)imaxabs(i2); } void uninitvar_isalnum(void) { int i; // cppcheck-suppress uninitvar (void)std::isalnum(i); } void uninitvar_isalpha(void) { int i; // cppcheck-suppress uninitvar (void)std::isalpha(i); } void uninitvar_iscntrl(void) { int i; // cppcheck-suppress uninitvar (void)std::iscntrl(i); } void uninitvar_isdigit(void) { int i; // cppcheck-suppress uninitvar (void)std::isdigit(i); } void uninitvar_isgraph(void) { int i; // cppcheck-suppress uninitvar (void)std::isgraph(i); } void uninitvar_islower(void) { int i; // cppcheck-suppress uninitvar (void)std::islower(i); } void uninitvar_isprint(void) { int i; // cppcheck-suppress uninitvar (void)std::isprint(i); } void uninitvar_isspace(void) { int i; // cppcheck-suppress uninitvar (void)std::isspace(i); } void uninitvar_isupper(void) { int i; // cppcheck-suppress uninitvar (void)std::isupper(i); } void uninitvar_isxdigit(void) { int i; // cppcheck-suppress uninitvar (void)std::isxdigit(i); } void uninitvar_proj(void) { double d; const std::complex dc(d,d); // TODO cppcheck-suppress uninitvar (void)std::proj(dc); } void uninitvar_acos(void) { float f; // cppcheck-suppress uninitvar (void)std::acos(f); double d; // cppcheck-suppress uninitvar (void)std::acos(d); long double ld; // cppcheck-suppress uninitvar (void)std::acos(ld); } void uninitvar_acosh(void) { float f; // cppcheck-suppress uninitvar (void)std::acoshf(f); double d; // cppcheck-suppress uninitvar (void)std::acosh(d); long double ld; // cppcheck-suppress uninitvar (void)std::acoshl(ld); } void uninitvar_asctime(void) { const struct tm *tm; // cppcheck-suppress uninitvar // cppcheck-suppress asctimeCalled (void)std::asctime(tm); } void uninitvar_sqrt(void) { float f; // cppcheck-suppress uninitvar (void)std::sqrt(f); double d; // cppcheck-suppress uninitvar (void)std::sqrt(d); long double ld; // cppcheck-suppress uninitvar (void)std::sqrt(ld); } void uninitvar_sinh(void) { float f; // cppcheck-suppress uninitvar (void)std::sinh(f); double d; // cppcheck-suppress uninitvar (void)std::sinh(d); long double ld; // cppcheck-suppress uninitvar (void)std::sinh(ld); } void uninitvar_sin(void) { float f; // cppcheck-suppress uninitvar (void)std::sin(f); double d; // cppcheck-suppress uninitvar (void)std::sin(d); long double ld; // cppcheck-suppress uninitvar (void)std::sin(ld); } void uninitvar_asin(void) { float f; // cppcheck-suppress uninitvar (void)std::asin(f); double d; // cppcheck-suppress uninitvar (void)std::asin(d); long double ld; // cppcheck-suppress uninitvar (void)std::asin(ld); } void uninitvar_asinh(void) { float f; // cppcheck-suppress uninitvar (void)std::asinhf(f); double d; // cppcheck-suppress uninitvar (void)std::asinh(d); long double ld; // cppcheck-suppress uninitvar (void)std::asinhl(ld); } void uninitvar_wcsftime(wchar_t* ptr) { size_t maxsize; wchar_t* format; struct tm* timeptr; // cppcheck-suppress uninitvar (void)std::wcsftime(ptr, maxsize, format, timeptr); } void uninitvar_tan(void) { float f; // cppcheck-suppress uninitvar (void)std::tan(f); double d; // cppcheck-suppress uninitvar (void)std::tan(d); long double ld; // cppcheck-suppress uninitvar (void)std::tan(ld); } void uninitvar_tanh(void) { float f; // cppcheck-suppress uninitvar (void)std::tanh(f); double d; // cppcheck-suppress uninitvar (void)std::tanh(d); long double ld; // cppcheck-suppress uninitvar (void)std::tanh(ld); } void uninitvar_atan(void) { float f; // cppcheck-suppress uninitvar (void)std::atan(f); double d; // cppcheck-suppress uninitvar (void)std::atan(d); long double ld; // cppcheck-suppress uninitvar (void)std::atan(ld); } void uninitvar_tgamma(void) { float f; // cppcheck-suppress uninitvar (void)std::tgammaf(f); double d; // cppcheck-suppress uninitvar (void)std::tgamma(d); long double ld; // cppcheck-suppress uninitvar (void)std::tgammal(ld); } void uninitvar_trunc(void) { float f; // cppcheck-suppress uninitvar (void)std::truncf(f); double d; // cppcheck-suppress uninitvar (void)std::trunc(d); long double ld; // cppcheck-suppress uninitvar (void)std::truncl(ld); } void uninitvar_atanh(void) { float f; // cppcheck-suppress uninitvar (void)std::atanhf(f); double d; // cppcheck-suppress uninitvar (void)std::atanh(d); long double ld; // cppcheck-suppress uninitvar (void)std::atanhl(ld); } void uninitvar_atan2(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::atan2(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::atan2(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::atan2(ld1,ld2); } void uninitvar_atof(void) { char * c; // cppcheck-suppress uninitvar (void)std::atof(c); } void uninitvar_atol(void) { char * c; // cppcheck-suppress uninitvar (void)std::atoi(c); // cppcheck-suppress uninitvar (void)std::atol(c); // cppcheck-suppress uninitvar (void)std::atoll(c); } void uninitvar_ceil(void) { float f; // cppcheck-suppress uninitvar (void)std::ceil(f); double d; // cppcheck-suppress uninitvar (void)std::ceil(d); long double ld; // cppcheck-suppress uninitvar (void)std::ceil(ld); } void uninitvar_copysign(void) { float f1, f2; // cppcheck-suppress uninitvar (void)std::copysignf(f1, f2); double d1, d2; // cppcheck-suppress uninitvar (void)std::copysign(d1, d2); long double ld1, ld2; // cppcheck-suppress uninitvar (void)std::copysignl(ld1, ld2); } void uninitvar_cbrt(void) { float f; // cppcheck-suppress uninitvar (void)std::cbrtf(f); double d; // cppcheck-suppress uninitvar (void)std::cbrt(d); long double ld; // cppcheck-suppress uninitvar (void)std::cbrtl(ld); } void uninitvar_cos(void) { float f; // cppcheck-suppress uninitvar (void)std::cos(f); double d; // cppcheck-suppress uninitvar (void)std::cos(d); long double ld; // cppcheck-suppress uninitvar (void)std::cos(ld); } void uninitvar_clearerr(void) { FILE * stream; // cppcheck-suppress uninitvar std::clearerr(stream); } void uninitvar_cosh(void) { float f; // cppcheck-suppress uninitvar (void)std::cosh(f); double d; // cppcheck-suppress uninitvar (void)std::cosh(d); long double ld; // cppcheck-suppress uninitvar (void)std::cosh(ld); } void uninitvar_feraiseexcept(void) { int excepts; // cppcheck-suppress uninitvar (void)std::feraiseexcept(excepts); } void uninitvar_fesetexceptflag(fexcept_t* flagp) { int excepts; // cppcheck-suppress uninitvar (void)std::fesetexceptflag(flagp, excepts); } void uninitvar_feclearexcept(void) { int i; // cppcheck-suppress uninitvar (void)std::feclearexcept(i); } void uninitvar_fesetenv(void) { fenv_t* envp; // cppcheck-suppress uninitvar (void)std::fesetenv(envp); } void uninitvar_fesetround(void) { int i; // cppcheck-suppress uninitvar (void)std::fesetround(i); } void uninitvar_fetestexcept(void) { int i; // cppcheck-suppress uninitvar (void)std::fetestexcept(i); } void uninitvar_feupdateenv(void) { fenv_t* envp; // cppcheck-suppress uninitvar (void)std::feupdateenv(envp); } void uninitvar_ctime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)std::ctime(tp); } void uninitvar_difftime(void) { time_t t1,t2; // cppcheck-suppress uninitvar (void)std::difftime(t1, t2); } void uninitvar_div(void) { int num; int denom; // cppcheck-suppress uninitvar (void)std::div(num,denom); } void uninitvar_imaxdiv(void) { intmax_t numer1, numer2; intmax_t denom1, denom2; // cppcheck-suppress uninitvar (void)std::imaxdiv(numer1,denom1); // cppcheck-suppress uninitvar (void)imaxdiv(numer2,denom2); } void uninitvar_exit(void) { int i; // cppcheck-suppress uninitvar std::exit(i); } void uninitvar_erf(void) { float f; // cppcheck-suppress uninitvar (void)std::erff(f); double d; // cppcheck-suppress uninitvar (void)std::erf(d); long double ld; // cppcheck-suppress uninitvar (void)std::erfl(ld); } void uninitvar_erfc(void) { float f; // cppcheck-suppress uninitvar (void)std::erfcf(f); double d; // cppcheck-suppress uninitvar (void)std::erfc(d); long double ld; // cppcheck-suppress uninitvar (void)std::erfcl(ld); } void uninitvar_exp(void) { float f; // cppcheck-suppress uninitvar (void)std::exp(f); double d; // cppcheck-suppress uninitvar (void)std::exp(d); long double ld; // cppcheck-suppress uninitvar (void)std::exp(ld); } void uninitvar_exp2(void) { float f; // cppcheck-suppress uninitvar (void)std::exp2f(f); double d; // cppcheck-suppress uninitvar (void)std::exp2(d); long double ld; // cppcheck-suppress uninitvar (void)std::exp2l(ld); } void uninitvar_expm1(void) { float f; // cppcheck-suppress uninitvar (void)std::expm1f(f); double d; // cppcheck-suppress uninitvar (void)std::expm1(d); long double ld; // cppcheck-suppress uninitvar (void)std::expm1l(ld); } void uninitvar_fabs(void) { float f; // cppcheck-suppress uninitvar (void)std::fabs(f); double d; // cppcheck-suppress uninitvar (void)std::fabs(d); long double ld; // cppcheck-suppress uninitvar (void)std::fabs(ld); } void uninitvar_fdim(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::fdimf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::fdim(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::fdiml(ld1,ld2); } void uninitvar_fclose(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::fclose(stream); } void uninitvar_ferror(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::ferror(stream); } void uninitvar_feof(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::feof(stream); } void uninitvar_fflush(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::fflush(stream); } void uninitvar_fgetc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::fgetc(stream); } void uninitvar_fgetwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::fgetwc(stream); } void uninitvar_fgetpos(void) { FILE* stream; fpos_t *ptr; // cppcheck-suppress uninitvar (void)std::fgetpos(stream,ptr); } void uninitvar_floor(void) { float f; // cppcheck-suppress uninitvar (void)std::floor(f); double d; // cppcheck-suppress uninitvar (void)std::floor(d); long double ld; // cppcheck-suppress uninitvar (void)std::floor(ld); } void uninitvar_fma(void) { float f1,f2,f3; // cppcheck-suppress uninitvar (void)std::fmaf(f1,f2,f3); double d1,d2,d3; // cppcheck-suppress uninitvar (void)std::fma(d1,d2,d3); long double ld1,ld2,ld3; // cppcheck-suppress uninitvar (void)std::fmal(ld1,ld2,ld3); } void uninitvar_fmax(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::fmaxf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::fmax(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::fmaxl(ld1,ld2); } void uninitvar_fmin(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::fminf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::fmin(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::fminl(ld1,ld2); } void uninitvar_fmod(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::fmod(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::fmod(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::fmod(ld1,ld2); } void uninitar_fopen(void) { char *filename; char *mode; // cppcheck-suppress uninitvar FILE * fp = std::fopen(filename, mode); fclose(fp); } void uninitar_fprintf(FILE *Stream, char *Format, int Argument) { FILE *stream; char *format1, *format2; int argument1, argument2; // cppcheck-suppress uninitvar (void)std::fprintf(stream, format1, argument1); // cppcheck-suppress uninitvar (void)std::fprintf(stream, Format, Argument); // cppcheck-suppress uninitvar (void)std::fprintf(Stream, format2, Argument); // cppcheck-suppress uninitvar (void)std::fprintf(Stream, Format, argument2); // no warning is expected (void)std::fprintf(Stream, Format, Argument); } void uninitar_vfprintf(FILE *Stream, const char *Format, va_list Arg) { FILE *stream; char *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfprintf(stream, format1, arg); // cppcheck-suppress uninitvar (void)std::vfprintf(stream, Format, Arg); // cppcheck-suppress uninitvar (void)std::vfprintf(Stream, format2, Arg); // no warning is expected (void)std::vfprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vfprintf(Stream, Format, arg); } void uninitar_vfwprintf(FILE *Stream, wchar_t *Format, va_list Arg) { FILE *stream; wchar_t *format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfwprintf(stream, format1, arg); // cppcheck-suppress uninitvar (void)std::vfwprintf(stream, Format, Arg); // cppcheck-suppress uninitvar (void)std::vfwprintf(Stream, format2, Arg); // no warning is expected (void)std::vfwprintf(Stream, Format, Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vfwprintf(Stream, Format, arg); } void uninitvar_fputc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)std::fputc(c,stream); } void uninitvar_fputwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)std::fputwc(c,stream); } void uninitvar_fputs(void) { char *string; FILE *stream; // cppcheck-suppress uninitvar (void)std::fputs(string,stream); } void uninitvar_fputws(void) { wchar_t *string; FILE *stream; // cppcheck-suppress uninitvar (void)std::fputws(string,stream); } void uninitvar_fread(void) { void *ptr; size_t size; size_t nobj; FILE *stream; // cppcheck-suppress uninitvar (void)std::fread(ptr,size,nobj,stream); } void uninitvar_free(void) { // cppcheck-suppress unassignedVariable void *block; // cppcheck-suppress uninitvar std::free(block); } void uninitvar_freopen(void) { char *filename; char *mode; FILE *stream; // cppcheck-suppress uninitvar FILE * p = std::freopen(filename,mode,stream); free(p); } void uninitvar_frexp(void) { float f1; int *i1; // cppcheck-suppress uninitvar (void)std::frexp(f1,i1); double d1; int *i2; // cppcheck-suppress uninitvar (void)std::frexp(d1,i2); long double ld1; int *i3; // cppcheck-suppress uninitvar (void)std::frexp(ld1,i3); } void uninitvar_hypot(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::hypotf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::hypot(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::hypotl(ld1,ld2); } void uninitvar_fscanf(void) { FILE *stream; char *format; int i; // cppcheck-suppress uninitvar (void)std::fscanf(stream,format,i); } void uninitvar_vfscanf(void) { FILE *stream; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfscanf(stream,format,arg); } void uninitvar_vfwscanf(void) { FILE *stream; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vfwscanf(stream,format,arg); } void uninitvar_fseek(void) { FILE* stream; long int offset; int origin; // cppcheck-suppress uninitvar (void)std::fseek(stream,offset,origin); } void uninitvar_fsetpos(void) { FILE* stream; fpos_t *ptr; // cppcheck-suppress uninitvar (void)std::fsetpos(stream,ptr); } void uninitvar_fgets(void) { char *buffer; int n; FILE *stream; // cppcheck-suppress uninitvar (void)std::fgets(buffer,n,stream); } void uninitvar_fgetws(void) { wchar_t *buffer; int n; FILE *stream; // cppcheck-suppress uninitvar (void)std::fgetws(buffer,n,stream); } void uninitvar_ftell(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::ftell(stream); } void uninitvar_fwide(void) { FILE *stream; int mode; // cppcheck-suppress uninitvar (void)std::fwide(stream,mode); } void uninitvar_fwrite(void) { void *ptr; size_t size; size_t nobj; FILE *stream; // cppcheck-suppress uninitvar (void)std::fwrite(ptr,size,nobj,stream); } void uninitvar_mblen(void) { char *string; size_t size; // cppcheck-suppress uninitvar (void)std::mblen(string,size); } void uninitvar_mbtowc(void) { wchar_t* pwc; char* pmb; size_t max; // cppcheck-suppress uninitvar (void)std::mbtowc(pwc,pmb,max); } void uninitvar_mbrlen(const char* p, size_t m, mbstate_t* s) { char* pmb; size_t max1, max2; mbstate_t* ps1, *ps2; // cppcheck-suppress uninitvar (void)std::mbrlen(pmb,max1,ps1); // cppcheck-suppress uninitvar (void)std::mbrlen(pmb,m,s); // cppcheck-suppress uninitvar (void)std::mbrlen(p,max2,s); // cppcheck-suppress uninitvar (void)std::mbrlen(p,m,ps2); // no warning is expected (void)std::mbrlen(p,m,s); } void nullPointer_mbrlen(const char* p, size_t m, mbstate_t* s) { // no warning is expected: A call to the function with a null pointer as pmb resets the shift state (and ignores parameter max). (void)std::mbrlen(NULL,m,s); (void)std::mbrlen(NULL,0,s); // cppcheck-suppress nullPointer (void)std::mbrlen(p,m,NULL); } void uninitvar_btowc(void) { int c; // cppcheck-suppress uninitvar (void)std::btowc(c); } void uninitvar_mbsinit(void) { mbstate_t* ps; // cppcheck-suppress uninitvar (void)std::mbsinit(ps); } void uninitvar_mbstowcs(void) { wchar_t *ws; char *s; size_t n; // cppcheck-suppress uninitvar (void)std::mbstowcs(ws,s,n); } void uninitvar_mbsrtowcs(void) { wchar_t* dest; const char* src; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)std::mbsrtowcs(dest,&src,max,ps); } void uninitvar_wctob(void) { wint_t wc; // cppcheck-suppress uninitvar (void)std::wctob(wc); } void uninitvar_wctomb(void) { char *s; wchar_t wc; // cppcheck-suppress uninitvar (void)std::wctomb(s,wc); } void uninitvar_wcstombs(void) { char *mbstr; wchar_t *wcstr; size_t n; // cppcheck-suppress uninitvar (void)std::wcstombs(mbstr,wcstr,n); } void uninitvar_getc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::getc(stream); } void uninitvar_getwc(void) { FILE *stream; // cppcheck-suppress uninitvar (void)std::getwc(stream); } void uninitvar_ungetc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)std::ungetc(c,stream); } void uninitvar_ungetwc(void) { wint_t c; FILE *stream; // cppcheck-suppress uninitvar (void)std::ungetwc(c,stream); } void uninitvar_getenv(void) { char *name; // cppcheck-suppress uninitvar (void)std::getenv(name); } void uninitvar_gmtime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)std::gmtime(tp); } void uninitvar_iswalnum(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswalnum(i); } void uninitvar_iswalpha(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswalpha(i); } void uninitvar_isblank(void) { int i; // cppcheck-suppress uninitvar (void)std::isblank(i); } void uninitvar_iswblank(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswblank(i); } void uninitvar_iswcntrl(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswcntrl(i); } void uninitvar_iswctype(void) { wint_t c; wctype_t desc; // cppcheck-suppress uninitvar (void)std::iswctype(c,desc); } void uninitvar_iswdigit(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswdigit(i); } void uninitvar_iswgraph(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswgraph(i); } void uninitvar_iswlower(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswlower(i); } void uninitvar_iswprint(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswprint(i); } void uninitvar_ispunct(void) { int i; // cppcheck-suppress uninitvar (void)std::ispunct(i); } void uninitvar_iswpunct(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswpunct(i); } void uninitvar_iswspace(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswspace(i); } void uninitvar_iswupper(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswupper(i); } void uninitvar_iswxdigit(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::iswxdigit(i); } void uninitvar_towctrans(void) { wint_t c; wctrans_t desc; // cppcheck-suppress uninitvar (void)std::towctrans(c,desc); } void uninitvar_towlower(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::towlower(i); } void uninitvar_towupper(void) { wint_t i; // cppcheck-suppress uninitvar (void)std::towupper(i); } void uninitvar_wctrans(void) { char* property; // cppcheck-suppress uninitvar (void)std::wctrans(property); } void uninitvar_wctype(void) { char* property; // cppcheck-suppress uninitvar (void)std::wctype(property); } void uninitvar_labs(void) { long int li; // cppcheck-suppress uninitvar (void)std::labs(li); long long int lli; // cppcheck-suppress uninitvar (void)std::llabs(lli); } void uninitvar_ldexp(void) { float fd; int e1; // cppcheck-suppress uninitvar (void)std::ldexp(fd,e1); double dc; int e2; // cppcheck-suppress uninitvar (void)std::ldexp(dc,e2); long double ldc; int e3; // cppcheck-suppress uninitvar (void)std::ldexp(ldc,e3); } void uninitvar_lgamma(void) { float f; // cppcheck-suppress uninitvar (void)std::lgammaf(f); double d; // cppcheck-suppress uninitvar (void)std::lgamma(d); long double ld; // cppcheck-suppress uninitvar (void)std::lgammal(ld); } void uninitvar_rint(void) { float f; // cppcheck-suppress uninitvar (void)std::rintf(f); double d; // cppcheck-suppress uninitvar (void)std::rint(d); long double ld; // cppcheck-suppress uninitvar (void)std::rintl(ld); } void uninitvar_lrint(void) { float f; // cppcheck-suppress uninitvar (void)std::lrintf(f); double d; // cppcheck-suppress uninitvar (void)std::lrint(d); long double ld; // cppcheck-suppress uninitvar (void)std::lrintl(ld); } void uninitvar_llrint(void) { float f; // cppcheck-suppress uninitvar (void)std::llrintf(f); double d; // cppcheck-suppress uninitvar (void)std::llrint(d); long double ld; // cppcheck-suppress uninitvar (void)std::llrintl(ld); } void uninitvar_lround(void) { float f; // cppcheck-suppress uninitvar (void)std::lroundf(f); double d; // cppcheck-suppress uninitvar (void)std::lround(d); long double ld; // cppcheck-suppress uninitvar (void)std::lroundl(ld); } void uninitvar_llround(void) { float f; // cppcheck-suppress uninitvar (void)std::llroundf(f); double d; // cppcheck-suppress uninitvar (void)std::llround(d); long double ld; // cppcheck-suppress uninitvar (void)std::llroundl(ld); } void uninitvar_srand(void) { unsigned int seed; // cppcheck-suppress uninitvar (void)std::srand(seed); } void uninitvar_ldiv(void) { long int l1; long int l2; // cppcheck-suppress uninitvar (void)std::ldiv(l1,l2); long long int ll1; long long int ll2; // cppcheck-suppress uninitvar (void)std::lldiv(ll1,ll2); } void uninitvar_localtime(void) { time_t *tp; // cppcheck-suppress uninitvar (void)std::localtime(tp); } void uninitvar_log(void) { float f; // cppcheck-suppress uninitvar (void)std::log(f); double d; // cppcheck-suppress uninitvar (void)std::log(d); long double ld; // cppcheck-suppress uninitvar (void)std::log(ld); } void uninitvar_fpclassify(void) { float f; // cppcheck-suppress uninitvar (void)std::fpclassify(f); double d; // cppcheck-suppress uninitvar (void)std::fpclassify(d); long double ld; // cppcheck-suppress uninitvar (void)std::fpclassify(ld); } void uninitvar_isfinite(void) { float f; // cppcheck-suppress uninitvar (void)std::isfinite(f); double d; // cppcheck-suppress uninitvar (void)std::isfinite(d); long double ld; // cppcheck-suppress uninitvar (void)std::isfinite(ld); } void uninitvar_isgreater(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::isgreater(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::isgreater(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::isgreater(ld1,ld2); } void uninitvar_isgreaterequal(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::isgreaterequal(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::isgreaterequal(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::isgreaterequal(ld1,ld2); } void uninitvar_isinf(void) { double d; // cppcheck-suppress uninitvar (void)std::isinf(d); } void uninitvar_logb(void) { float f; // cppcheck-suppress uninitvar (void)std::logbf(f); double d; // cppcheck-suppress uninitvar (void)std::logb(d); long double ld; // cppcheck-suppress uninitvar (void)std::logbl(ld); } void uninitvar_isless(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::isless(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::isless(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::isless(ld1,ld2); } void uninitvar_islessequal(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::islessequal(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::islessequal(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::islessequal(ld1,ld2); } void uninitvar_islessgreater(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::islessgreater(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::islessgreater(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::islessgreater(ld1,ld2); } void uninitvar_nan(void) { char *tagp; // cppcheck-suppress uninitvar (void)std::nanf(tagp); // cppcheck-suppress uninitvar (void)std::nan(tagp); // cppcheck-suppress uninitvar (void)std::nanl(tagp); } void uninitvar_isnan(void) { double d; // cppcheck-suppress uninitvar (void)std::isnan(d); } void uninitvar_isnormal(void) { double d; // cppcheck-suppress uninitvar (void)std::isnormal(d); } void uninitvar_isunordered(void) { double d1,d2; // cppcheck-suppress uninitvar (void)std::isunordered(d1,d2); } void uninitvar_ilogb(void) { float f; // cppcheck-suppress uninitvar (void)std::ilogb(f); double d; // cppcheck-suppress uninitvar (void)std::ilogb(d); long double ld; // cppcheck-suppress uninitvar (void)std::ilogb(ld); } void uninitvar_log10(void) { float f; // cppcheck-suppress uninitvar (void)std::log10(f); double d; // cppcheck-suppress uninitvar (void)std::log10(d); long double ld; // cppcheck-suppress uninitvar (void)std::log10(ld); } void uninitvar_log1p(void) { float f; // cppcheck-suppress uninitvar (void)std::log1pf(f); double d; // cppcheck-suppress uninitvar (void)std::log1p(d); long double ld; // cppcheck-suppress uninitvar (void)std::log1pl(ld); } void uninitvar_log2(void) { float f; // cppcheck-suppress uninitvar (void)std::log2f(f); double d; // cppcheck-suppress uninitvar (void)std::log2(d); long double ld; // cppcheck-suppress uninitvar (void)std::log2l(ld); } void uninitvar_nearbyint(void) { float f; // cppcheck-suppress uninitvar (void)std::nearbyintf(f); double d; // cppcheck-suppress uninitvar (void)std::nearbyint(d); long double ld; // cppcheck-suppress uninitvar (void)std::nearbyintl(ld); } void uninitvar_nextafter(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::nextafterf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::nextafter(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::nextafterl(ld1,ld2); } void uninitvar_nexttoward(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::nexttowardf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::nexttoward(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::nexttowardl(ld1,ld2); } void uninitvar_longjmp(void) { jmp_buf env; int val; // cppcheck-suppress uninitvar (void)std::longjmp(env,val); } void uninitvar_malloc(void) { size_t size; // cppcheck-suppress uninitvar int *p = (int*)std::malloc(size); free(p); } void uninitvar_memchr(void) { void *cs; int c; size_t n; // cppcheck-suppress uninitvar (void)std::memchr(cs,c,n); } void uninitvar_wmemchr(void) { wchar_t *cs; wchar_t c; size_t n; // cppcheck-suppress uninitvar (void)std::wmemchr(cs,c,n); } void uninitvar_memcmp(void) { void *s1; void *s2; size_t n; // cppcheck-suppress uninitvar (void)std::memcmp(s1,s2,n); } void uninitvar_wmemcmp(void) { wchar_t *s1; wchar_t *s2; size_t n; // cppcheck-suppress uninitvar (void)std::wmemcmp(s1,s2,n); } void uninitvar_memcpy(void) { void *ct; void *cs; size_t n; // cppcheck-suppress uninitvar (void)std::memcpy(ct,cs,n); } void uninitvar_wmemcpy(void) { wchar_t *cs; wchar_t *c; size_t n; // cppcheck-suppress uninitvar (void)std::wmemcpy(cs,c,n); } void uninitvar_memmove(void) { void *ct; void *cs; size_t n; // cppcheck-suppress uninitvar (void)std::memmove(ct,cs,n); } void uninitvar_wmemmove(void) { wchar_t *cs; wchar_t *c; size_t n; // cppcheck-suppress uninitvar (void)std::wmemmove(cs,c,n); } void uninitvar_memset(void) { void *s; int c; size_t n; // cppcheck-suppress uninitvar (void)std::memset(s,c,n); } void uninitvar_wmemset(void) { wchar_t *cs; wchar_t c; size_t n; // cppcheck-suppress uninitvar (void)std::wmemset(cs,c,n); } void uninitvar_mktime(void) { struct tm *tp; // cppcheck-suppress uninitvar (void)std::mktime(tp); } void uninivar_modf(void) { float f1; float *f2; // cppcheck-suppress uninitvar (void)std::modf(f1,f2); double d1; double *d2; // cppcheck-suppress uninitvar (void)std::modf(d1,d2); long double ld1; long double *ld2; // cppcheck-suppress uninitvar (void)std::modf(ld1,ld2); } void uninivar_perror(void) { char *string; // cppcheck-suppress uninitvar (void)std::perror(string); } void uninitvar_pow(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::pow(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::pow(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::pow(ld1,ld2); } void uninitvar_remainder(void) { float f1,f2; // cppcheck-suppress uninitvar (void)std::remainderf(f1,f2); double d1,d2; // cppcheck-suppress uninitvar (void)std::remainder(d1,d2); long double ld1,ld2; // cppcheck-suppress uninitvar (void)std::remainderl(ld1,ld2); } void uninitvar_remquo(void) { float f1,f2; int *i1; // cppcheck-suppress uninitvar (void)std::remquof(f1,f2,i1); double d1,d2; int *i2; // cppcheck-suppress uninitvar (void)std::remquo(d1,d2,i2); long double ld1,ld2; int *i3; // cppcheck-suppress uninitvar (void)std::remquol(ld1,ld2,i3); } void uninivar_printf(char *Format, int Argument) { char * format_1, * format_2, * format_3; int argument1, argument2; // no warning is expected (void)std::printf("x"); // cppcheck-suppress uninitvar (void)std::printf(format_1,argument1); // cppcheck-suppress uninitvar (void)std::printf(Format,argument2); // cppcheck-suppress uninitvar (void)std::printf(format_2,Argument); // cppcheck-suppress uninitvar (void)std::printf(format_3,1); // no warning is expected (void)std::printf(Format,Argument); } void uninivar_vprintf(char *Format, va_list Arg) { char * format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vprintf(format1,arg); // cppcheck-suppress uninitvar (void)std::vprintf(format2,Arg); // no warning is expected (void)std::vprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vprintf(Format,arg); } void uninivar_vwprintf(wchar_t *Format, va_list Arg) { wchar_t * format1, *format2; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vwprintf(format1,arg); // cppcheck-suppress uninitvar (void)std::vwprintf(format2,Arg); // no warning is expected (void)std::vwprintf(Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vwprintf(Format,arg); } void uninivar_bsearch(void) { void* key; void* base; size_t num; size_t size; // cppcheck-suppress uninitvar (void)std::bsearch(key,base,num,size,(int(*)(const void*,const void*)) strcmp); } void uninitvar_qsort(void) { void *base; size_t n; size_t size; // cppcheck-suppress uninitvar (void)std::qsort(base,n,size, (int(*)(const void*,const void*)) strcmp); } void uninitvar_putc(void) { int c; FILE *stream; // cppcheck-suppress uninitvar (void)std::putc(c,stream); } void uninitvar_putwc(void) { wchar_t c; FILE *stream; // cppcheck-suppress uninitvar (void)std::putc(c,stream); } void uninitvar_putchar(void) { int c; // cppcheck-suppress uninitvar (void)std::putchar(c); } void uninitvar_putwchar(void) { wchar_t c; // cppcheck-suppress uninitvar (void)std::putwchar(c); } void uninitvar_puts(void) { char *s; // cppcheck-suppress uninitvar (void)std::puts(s); } void uninitvar_realloc(void) { void *block; size_t newsize; // cppcheck-suppress uninitvar void *p = std::realloc(block, newsize); free(p); } void uninitvar_remove(void) { char *s; // cppcheck-suppress uninitvar (void)std::remove(s); } void uninitvar_rename(void) { char *s1; char *s2; // cppcheck-suppress uninitvar (void)std::rename(s1,s2); } void uninitvar_rewind(void) { FILE *f; // cppcheck-suppress uninitvar (void)std::rewind(f); } void uninitvar_round(void) { float f; // cppcheck-suppress uninitvar (void)std::roundf(f); double d; // cppcheck-suppress uninitvar (void)std::round(d); long double ld; // cppcheck-suppress uninitvar (void)std::roundl(ld); } void uninivar_scalbn(void) { float f; int i1; // cppcheck-suppress uninitvar (void)std::scalbnf(f,i1); double d; int i2; // cppcheck-suppress uninitvar (void)std::scalbn(d,i2); long double ld; int i3; // cppcheck-suppress uninitvar (void)std::scalbnl(ld,i3); } void uninivar_scalbln(void) { float f; long int i1; // cppcheck-suppress uninitvar (void)std::scalblnf(f,i1); double d; long int i2; // cppcheck-suppress uninitvar (void)std::scalbln(d,i2); long double ld; long int i3; // cppcheck-suppress uninitvar (void)std::scalblnl(ld,i3); } void uninitvar_signbit(void) { double d; // cppcheck-suppress uninitvar (void)std::signbit(d); } void uninivar_signal(void) { int i; // cppcheck-suppress uninitvar std::signal(i, exit); } void uninivar_raise(void) { int i; // cppcheck-suppress uninitvar (void)std::raise(i); } void uninivar_scanf(void) { char *format; char str[42]; // cppcheck-suppress uninitvar (void)std::scanf(format, str); } void uninivar_vsscanf(void) { char *s; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vsscanf(s,format,arg); } void uninivar_vswscanf(void) { wchar_t *s; wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vswscanf(s,format,arg); } void uninivar_vscanf(void) { char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vscanf(format,arg); } void uninivar_vwscanf(void) { wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vwscanf(format,arg); } void uninivar_setbuf(void) { FILE *stream; char *buf; // cppcheck-suppress uninitvar (void)std::setbuf(stream,buf); } void uninivar_setvbuf(void) { FILE *stream; char *buf; int mode; size_t size; // cppcheck-suppress uninitvar (void)std::setvbuf(stream,buf,mode,size); } void uninitvar_strcat(char *dest, const char * const source) { char *deststr1, *deststr2; char *srcstr; // cppcheck-suppress uninitvar (void)std::strcat(deststr1,srcstr); // cppcheck-suppress uninitvar (void)std::strcat(dest,srcstr); // cppcheck-suppress uninitvar (void)std::strcat(deststr2,source); // no warning shall be shown for (void)std::strcat(dest,source); } void uninitvar_wcscat(wchar_t *dest, const wchar_t * const source) { wchar_t *deststr_1, *deststr_2; wchar_t *srcstr; // cppcheck-suppress uninitvar (void)std::wcscat(deststr_1,srcstr); // cppcheck-suppress uninitvar (void)std::wcscat(dest,srcstr); // cppcheck-suppress uninitvar (void)std::wcscat(deststr_2,source); // no warning shall be shown for (void)std::wcscat(dest,source); } void uninivar_wcrtomb(void) { char *s; wchar_t wc; mbstate_t *ps; // cppcheck-suppress uninitvar (void)std::wcrtomb(s,wc,ps); } void uninivar_strchr(void) { char *cs; int c; // cppcheck-suppress uninitvar (void)std::strchr(cs,c); } void uninivar_wcschr(void) { wchar_t *cs; wchar_t c; // cppcheck-suppress uninitvar (void)std::wcschr(cs,c); } void uninivar_strcmp(void) { char *str1; char *str2; // cppcheck-suppress uninitvar (void)std::strcmp(str1,str2); } void uninivar_wcscmp(void) { wchar_t *str1; wchar_t *str2; // cppcheck-suppress uninitvar (void)std::wcscmp(str1,str2); } void uninivar_strcpy(void) { char *str1; char *str2; // cppcheck-suppress uninitvar (void)std::strcpy(str1,str2); } void uninivar_wcscpy(void) { wchar_t *str1; wchar_t *str2; // cppcheck-suppress uninitvar (void)std::wcscpy(str1,str2); } void uninivar_strftime(void) { char *s; size_t max; char *fmt; struct tm *p; // cppcheck-suppress uninitvar (void)std::strftime(s,max,fmt,p); } void uninivar_strlen(void) { char *s; // cppcheck-suppress uninitvar (void)std::strlen(s); } void uninivar_wcslen(void) { wchar_t *s; // cppcheck-suppress uninitvar (void)std::wcslen(s); } void uninivar_strncpy(void) { char *s; char *ct; size_t n; // cppcheck-suppress uninitvar (void)std::strncpy(s,ct,n); } void uninivar_strpbrk(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strpbrk(cs,ct); } void uninivar_strncat(char *Ct, char *S, size_t N) { char *ct_1, *ct_2; char *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)std::strncat(ct_1,s,n1); // cppcheck-suppress uninitvar (void)std::strncat(ct_2,S,N); // cppcheck-suppress uninitvar (void)std::strncat(Ct,s,N); // cppcheck-suppress uninitvar (void)std::strncat(Ct,S,n2); // no warning is expected for (void)std::strncat(Ct,S,N); } void uninivar_wcsncat(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct_1, *ct_2; wchar_t *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)std::wcsncat(ct_1,s,n1); // cppcheck-suppress uninitvar (void)std::wcsncat(ct_2,S,N); // cppcheck-suppress uninitvar (void)std::wcsncat(Ct,s,N); // cppcheck-suppress uninitvar (void)std::wcsncat(Ct,S,n2); // no warning is expected for (void)std::wcsncat(Ct,S,N); } void uninivar_strncmp(char *Ct, char *S, size_t N) { char *ct; char *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)std::strncmp(ct,s,n1); // cppcheck-suppress uninitvar (void)std::strncmp(ct,S,N); // cppcheck-suppress uninitvar (void)std::strncmp(Ct,s,N); // cppcheck-suppress uninitvar (void)std::strncmp(Ct,S,n2); // no warning is expected for (void)std::strncmp(Ct,S,N); } void uninivar_wcsncmp(wchar_t *Ct, wchar_t *S, size_t N) { wchar_t *ct; wchar_t *s; size_t n1, n2; // cppcheck-suppress uninitvar (void)std::wcsncmp(ct,s,n1); // cppcheck-suppress uninitvar (void)std::wcsncmp(ct,S,N); // cppcheck-suppress uninitvar (void)std::wcsncmp(Ct,s,N); // cppcheck-suppress uninitvar (void)std::wcsncmp(Ct,S,n2); // no warning is expected for (void)std::wcsncmp(Ct,S,N); } void uninivar_strstr(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strstr(cs,ct); } void uninivar_wcsstr(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)std::wcsstr(cs,ct); } void uninivar_strspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strspn(cs,ct); } void uninivar_strxfrm(void) { char *ds; char *ss; size_t n; // cppcheck-suppress uninitvar (void)std::strxfrm(ds,ss,n); } void uninivar_wcsxfrm(void) { wchar_t *ds; wchar_t *ss; size_t n; // cppcheck-suppress uninitvar (void)std::wcsxfrm(ds,ss,n); } void uninivar_wcsspn(void) { wchar_t *ds; wchar_t *ss; // cppcheck-suppress uninitvar (void)std::wcsspn(ds,ss); } void uninivar_setlocale(void) { int category; char* locale; // cppcheck-suppress uninitvar (void)std::setlocale(category,locale); } void uninivar_strerror(void) { int i; // cppcheck-suppress uninitvar (void)std::strerror(i); } void uninivar_strcspn(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strcspn(cs,ct); } void uninivar_wcscspn(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)std::wcscspn(cs,ct); } void uninivar_wcspbrk(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)std::wcspbrk(cs,ct); } void uninivar_wcsncpy(void) { wchar_t *cs; wchar_t *ct; size_t n; // cppcheck-suppress uninitvar (void)std::wcsncpy(cs,ct,n); } void uninivar_strcoll(void) { char *cs; char *ct; // cppcheck-suppress uninitvar (void)std::strcoll(cs,ct); } void uninivar_wcscoll(void) { wchar_t *cs; wchar_t *ct; // cppcheck-suppress uninitvar (void)std::wcscoll(cs,ct); } void uninivar_strrchr(void) { char * str; int c; // cppcheck-suppress uninitvar (void)std::strrchr(str,c); } void uninivar_wcsrchr(void) { wchar_t* ws; wchar_t wc; // cppcheck-suppress uninitvar (void)std::wcsrchr(ws,wc); } void uninivar_wcsrtombs(void) { char *dst; const wchar_t * p;; size_t len; mbstate_t *ps; // cppcheck-suppress uninitvar (void)std::wcsrtombs(dst,&p,len,ps); } void uninivar_strtok(void) { char *s; char *ct; // cppcheck-suppress uninitvar (void)std::strtok(s,ct); } void uninivar_strtoimax(void) { const char *s; char **endp; int base; // cppcheck-suppress uninitvar (void)std::strtoimax(s,endp,base); // cppcheck-suppress uninitvar (void)std::strtoumax(s,endp,base); } void uninivar_strtof(void) { const char *s; char **endp; // cppcheck-suppress uninitvar (void)std::strtof(s,endp); // cppcheck-suppress uninitvar (void)std::strtod(s,endp); // cppcheck-suppress uninitvar (void)std::strtold(s,endp); } void uninivar_strtol(void) { const char *s; char **endp; int base; // cppcheck-suppress uninitvar (void)std::strtol(s,endp,base); // cppcheck-suppress uninitvar (void)std::strtoll(s,endp,base); // cppcheck-suppress uninitvar (void)std::strtoul(s,endp,base); // cppcheck-suppress uninitvar (void)std::strtoull(s,endp,base); // cppcheck-suppress uninitvar (void)std::strtoimax(s,endp,base); // cppcheck-suppress uninitvar (void)strtoimax(s,endp,base); // cppcheck-suppress uninitvar (void)std::strtoumax(s,endp,base); // cppcheck-suppress uninitvar (void)strtoumax(s,endp,base); } void uninitvar_time(void) { time_t *tp; // cppcheck-suppress uninitvar (void)std::time(tp); } void uninitvar_tmpnam(void) { char *s; // cppcheck-suppress uninitvar (void)std::tmpnam(s); } void uninivar_tolower(void) { int c; // cppcheck-suppress uninitvar (void)std::tolower(c); } void uninivar_toupper(void) { int c; // cppcheck-suppress uninitvar (void)std::toupper(c); } void uninivar_wcstof(void) { const wchar_t *s; wchar_t **endp; // cppcheck-suppress uninitvar (void)std::wcstof(s,endp); // cppcheck-suppress uninitvar (void)std::wcstod(s,endp); // cppcheck-suppress uninitvar (void)std::wcstold(s,endp); } void uninivar_stoX(void) { std::string str; std::wstring wstr; size_t* idx1; size_t* idx2; size_t* idx3; size_t* idx4; size_t* idx5; size_t* idx6; size_t* idx7; size_t* idx8; size_t* idx9; size_t* idx10; size_t* idx11; size_t* idx12; size_t* idx13; size_t* idx14; size_t* idx15; size_t* idx16; // cppcheck-suppress uninitvar (void)std::stod(str,idx1); // cppcheck-suppress uninitvar (void)std::stod(wstr,idx2); // cppcheck-suppress uninitvar (void)std::stof(str,idx3); // cppcheck-suppress uninitvar (void)std::stof(wstr,idx4); // cppcheck-suppress uninitvar (void)std::stoi(str,idx5); // cppcheck-suppress uninitvar (void)std::stoi(wstr,idx6); // cppcheck-suppress uninitvar (void)std::stol(str,idx7); // cppcheck-suppress uninitvar (void)std::stol(wstr,idx8); // cppcheck-suppress uninitvar (void)std::stold(str,idx9); // cppcheck-suppress uninitvar (void)std::stold(wstr,idx10); // cppcheck-suppress uninitvar (void)std::stoll(str,idx11); // cppcheck-suppress uninitvar (void)std::stoll(wstr,idx12); // cppcheck-suppress uninitvar (void)std::stoul(str,idx13); // cppcheck-suppress uninitvar (void)std::stoul(wstr,idx14); // cppcheck-suppress uninitvar (void)std::stoull(str,idx15); // cppcheck-suppress uninitvar (void)std::stoull(wstr,idx16); } void uninivar_to_string(void) { int i; long l; long long ll; unsigned u; unsigned long ul; unsigned long long ull; float f; double d; long double ld; // cppcheck-suppress uninitvar (void)std::to_string(i); // cppcheck-suppress uninitvar (void)std::to_string(l); // cppcheck-suppress uninitvar (void)std::to_string(ll); // cppcheck-suppress uninitvar (void)std::to_string(u); // cppcheck-suppress uninitvar (void)std::to_string(ul); // cppcheck-suppress uninitvar (void)std::to_string(ull); // cppcheck-suppress uninitvar (void)std::to_string(f); // cppcheck-suppress uninitvar (void)std::to_string(d); // cppcheck-suppress uninitvar (void)std::to_string(ld); } void uninivar_to_wstring(void) { int i; long l; long long ll; unsigned u; unsigned long ul; unsigned long long ull; float f; double d; long double ld; // cppcheck-suppress uninitvar (void)std::to_wstring(i); // cppcheck-suppress uninitvar (void)std::to_wstring(l); // cppcheck-suppress uninitvar (void)std::to_wstring(ll); // cppcheck-suppress uninitvar (void)std::to_wstring(u); // cppcheck-suppress uninitvar (void)std::to_wstring(ul); // cppcheck-suppress uninitvar (void)std::to_wstring(ull); // cppcheck-suppress uninitvar (void)std::to_wstring(f); // cppcheck-suppress uninitvar (void)std::to_wstring(d); // cppcheck-suppress uninitvar (void)std::to_wstring(ld); } void uninivar_mbrtowc(void) { wchar_t* pwc; const char* pmb; size_t max; mbstate_t* ps; // cppcheck-suppress uninitvar (void)std::mbrtowc(pwc,pmb,max,ps); } void uninivar_wcstok(void) { wchar_t *s; const wchar_t *ct; wchar_t **ptr; // cppcheck-suppress uninitvar (void)std::wcstok(s,ct,ptr); } void uninivar_wcstoimax(void) { const wchar_t *s; wchar_t ** endp; int base; // cppcheck-suppress uninitvar (void)std::wcstoimax(s,endp,base); // cppcheck-suppress uninitvar (void)std::wcstoumax(s,endp,base); } void uninivar_wcstol(void) { const wchar_t *s; wchar_t ** endp; int base; // cppcheck-suppress uninitvar (void)std::wcstol(s,endp,base); // cppcheck-suppress uninitvar (void)std::wcstoll(s,endp,base); // cppcheck-suppress uninitvar (void)std::wcstoul(s,endp,base); // cppcheck-suppress uninitvar (void)std::wcstoull(s,endp,base); // cppcheck-suppress uninitvar (void)std::wcstoimax(s,endp,base); // cppcheck-suppress uninitvar (void)wcstoimax(s,endp,base); // cppcheck-suppress uninitvar (void)std::wcstoumax(s,endp,base); // cppcheck-suppress uninitvar (void)wcstoumax(s,endp,base); } void uninitvar_wprintf(wchar_t *Format, int Argument) { const wchar_t *format; int argument1, argument2; // cppcheck-suppress uninitvar (void)std::wprintf(format,argument1); // cppcheck-suppress uninitvar (void)std::wprintf(format); // cppcheck-suppress uninitvar (void)std::wprintf(Format,argument2); // cppcheck-suppress uninitvar (void)std::wprintf(format,Argument); // no warning is expected (void)std::wprintf(Format,Argument); (void)std::wprintf(Format); } void uninitvar_sprintf(void) { char *s; const char *format; int i; // cppcheck-suppress uninitvar (void)std::sprintf(s,format,i); } void uninitvar_swprintf(void) { wchar_t *s; size_t n; const wchar_t *format; // cppcheck-suppress uninitvar (void)std::swprintf(s,n,format); } void uninitvar_vsprintf(void) { char *s; const char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vsprintf(s,format,arg); } void nullPointer_vsprintf(va_list arg,const char *format) { char *s = NULL; (void)std::vsprintf(s,format,arg); // Its allowed to provide 's' as NULL pointer // cppcheck-suppress nullPointer (void)std::vsprintf(s,NULL,arg); } void uninitvar_vswprintf(void) { wchar_t *s; size_t n; const wchar_t *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vswprintf(s,n,format,arg); } void uninivar_fwprintf(void) { FILE* stream; const wchar_t* format; int i; // cppcheck-suppress uninitvar (void)std::fwprintf(stream,format,i); } void uninivar_snprintf(char *S, size_t N, char *Format, int Int) { size_t n1, n2; char *format; int i1, i2; char *s1, *s2; // cppcheck-suppress uninitvar (void)std::snprintf(s1,n1,format,i1); // cppcheck-suppress uninitvar (void)std::snprintf(S,n2,Format,Int); // n is uninitialized // cppcheck-suppress uninitvar (void)std::snprintf(S,N,format,Int); // format is uninitialized // cppcheck-suppress uninitvar (void)std::snprintf(S,N,Format,i2); // i is uninitialized // cppcheck-suppress uninitvar (void)std::snprintf(s2,N,Format,Int); // no warning is expected for (void)std::snprintf(S,N,Format,Int); } void uninivar_vsnprintf(char *S, size_t N, char *Format, va_list Arg) { char *s1, *s2; size_t n1, n2; char *format; va_list arg; // cppcheck-suppress va_list_usedBeforeStarted // cppcheck-suppress uninitvar (void)std::vsnprintf(s1,n1,format,arg); // cppcheck-suppress uninitvar (void)std::vsnprintf(s2,N,Format,Arg); // cppcheck-suppress uninitvar (void)std::vsnprintf(S,n2,Format,Arg); // cppcheck-suppress uninitvar (void)std::vsnprintf(S,N,format,Arg); // no warning is expected for (void)std::vsnprintf(S,N,Format,Arg); // cppcheck-suppress va_list_usedBeforeStarted (void)std::vsnprintf(S,N,Format,arg); } void uninivar_wscanf(void) { wchar_t *format; int i; // cppcheck-suppress uninitvar (void)std::wscanf(format); // cppcheck-suppress uninitvar (void)std::wscanf(format,&i); } void uninivar_sscanf(void) { char *string; const char * format; int i; // cppcheck-suppress uninitvar (void)std::sscanf(string,format); // cppcheck-suppress uninitvar (void)std::sscanf(string,format,&i); } void uninivar_fwscanf(void) { FILE* stream; wchar_t* format1, *format2; int i; // cppcheck-suppress uninitvar (void)std::fwscanf(stream,format1); // cppcheck-suppress uninitvar (void)std::fwscanf(stream,format2,&i); } void uninivar_swscanf(void) { wchar_t* s; wchar_t* format1, *format2; int i; // cppcheck-suppress uninitvar (void)std::swscanf(s,format1); // cppcheck-suppress uninitvar (void)std::swscanf(s,format2,&i); } void uninitvar_system(void) { char *c; // cppcheck-suppress uninitvar (void)std::system(c); } void uninitvar_setw(void) { int i; // cppcheck-suppress uninitvar std::cout << std::setw(i); } void uninitvar_setiosflags(void) { std::ios_base::fmtflags mask; // TODO cppcheck-suppress uninitvar std::cout << std::setiosflags(mask); // #6987 - false negative } void uninitvar_resetiosflags(void) { std::ios_base::fmtflags mask; // TODO cppcheck-suppress uninitvar std::cout << std::resetiosflags(mask); // #6987 - false negative } void uninitvar_setfill(void) { char c; // cppcheck-suppress uninitvar std::cout << std::setfill(c); wchar_t wc; // cppcheck-suppress uninitvar std::wcout << std::setfill(wc); } void uninitvar_setprecision(void) { int p; // cppcheck-suppress uninitvar std::cout << std::setprecision(p); } void uninitvar_setbase(void) { int p; // cppcheck-suppress uninitvar std::cout << std::setbase(p); } void uninitvar_find(std::string s) { // testing of size_t find (const string& str, size_t pos = 0) size_t pos1, pos2, pos3, pos4, pos5, pos6, pos7; // cppcheck-suppress uninitvar (void)s.find("find",pos1); // #6991 // testing of size_t find (const char* s, size_t pos = 0) const; char *pc, *pc2; // cppcheck-suppress uninitvar (void)s.find(pc,0); // cppcheck-suppress uninitvar (void)s.find(pc,pos2); // cppcheck-suppress uninitvar (void)s.find("test",pos3); // testing of size_t find (char c, size_t pos = 0) const; char c; // cppcheck-suppress uninitvar (void)s.find(c,pos4); // testing of size_t find (const char* pc, size_t pos, size_t n) const; size_t n1,n2,n3; // cppcheck-suppress uninitvar (void)s.find(pc,pos5,n1); // #6991 // cppcheck-suppress uninitvar (void)s.find("test",pos6,n2); // cppcheck-suppress uninitvar (void)s.find("test",1,n3); // cppcheck-suppress uninitvar (void)s.find("test",pos7,1); // cppcheck-suppress uninitvar (void)s.find(pc2,1,1); } void uninivar_ifstream_read(std::ifstream &f) { int size; char buffer[10]; // cppcheck-suppress uninitvar f.read(buffer, size); } void uninivar_istream_read(std::istream &f) { int size; char buffer[10]; // cppcheck-suppress uninitvar f.read(buffer, size); } void uninitvar_string_compare(std::string &teststr, std::wstring &testwstr) { char *pStrUninit; // cppcheck-suppress uninitvar (void)teststr.compare(pStrUninit); wchar_t *pWStrUninit; // cppcheck-suppress uninitvar (void)testwstr.compare(pWStrUninit); } void invalidFunctionArgBool_abs(bool b, double x, double y) { // cppcheck-suppress invalidFunctionArgBool (void)std::abs(true); // #6990 // cppcheck-suppress invalidFunctionArgBool (void)std::abs(b); // #6990 // cppcheck-suppress invalidFunctionArgBool (void)std::abs(x /////////////////////////////////////////////////////////////////////// #include #include #define pred [](int i){return i==0;} void stdalgorithm(const std::list &ints1, const std::list &ints2) { // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::find(ints1.begin(), ints2.end(), 123); // cppcheck-suppress mismatchingContainers if (std::find(ints1.begin(), ints1.end(), 123) == ints2.end()) {} // #9455 std::list::const_iterator uninitItBegin; std::list::const_iterator uninitItEnd; // @todo cppcheck-suppress uninitvar if (std::find(uninitItBegin, uninitItEnd, 123) == uninitItEnd) {} // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::find_if(ints1.begin(), ints2.end(), pred); // cppcheck-suppress mismatchingContainers if (std::find_if(ints1.begin(), ints1.end(), pred) == ints2.end()) {} // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::find_if_not(ints1.begin(), ints2.end(), pred); // cppcheck-suppress mismatchingContainers if (std::find_if_not(ints1.begin(), ints1.end(), pred) == ints2.end()) {} // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::all_of(ints1.begin(), ints2.end(), pred); // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::any_of(ints1.begin(), ints2.end(), pred); // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::none_of(ints1.begin(), ints2.end(), pred); // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::count(ints1.begin(), ints2.end(), 123); // // cppcheck-suppress mismatchingContainers // cppcheck-suppress ignoredReturnValue std::count_if(ints1.begin(), ints2.end(), pred); // // cppcheck-suppress mismatchingContainers std::for_each(ints1.begin(), ints2.end(), [](int i) {}); } void getline() { // #837 std::ifstream in("test1.txt"); char cBuf[10]; // cppcheck-suppress bufferAccessOutOfBounds in.getline(cBuf, 100); // cppcheck-suppress bufferAccessOutOfBounds in.read(cBuf, 100); // cppcheck-suppress bufferAccessOutOfBounds in.readsome(cBuf, 100); // cppcheck-suppress bufferAccessOutOfBounds in.get(cBuf, 100); // cppcheck-suppress bufferAccessOutOfBounds in.get(cBuf, 100, 'a'); // cppcheck-suppress bufferAccessOutOfBounds in.getline(cBuf, 100, 'a'); in.close(); } void stdstring() { std::string s; // cppcheck-suppress ignoredReturnValue s.size(); // valid s.assign("a"); } void stdvector() { int uninit1, uninit2, uninit3; std::vector v; // cppcheck-suppress ignoredReturnValue v.size(); // cppcheck-suppress ignoredReturnValue v.capacity(); // cppcheck-suppress uselessCallsEmpty // cppcheck-suppress ignoredReturnValue v.empty(); // cppcheck-suppress ignoredReturnValue v.max_size(); // cppcheck-suppress uninitvar v.push_back(uninit1); // cppcheck-suppress uninitvar v.reserve(uninit2); // cppcheck-suppress invalidFunctionArg v.reserve(-1); // no warning is expected for capacity 0 as it simply has no effect v.reserve(0); // cppcheck-suppress uninitvar v.resize(uninit3); // cppcheck-suppress invalidFunctionArg v.resize(-1); v.clear(); v.shrink_to_fit(); // no warning is expected for pop_back() v.push_back(42); v.pop_back(); v.push_back(42); // cppcheck-suppress ignoredReturnValue v.back(); // cppcheck-suppress ignoredReturnValue v.front(); } void stdbind_helper(int a) { printf("%d", a); } void stdbind() { using namespace std::placeholders; // TODO cppcheck-suppress ignoredReturnValue #9369 std::bind(stdbind_helper, 1); // cppcheck-suppress unreadVariable auto f1 = std::bind(stdbind_helper, _1); // cppcheck-suppress unreadVariable auto f2 = std::bind(stdbind_helper, 10); } cppcheck-1.90/test/cfg/windows.cpp000066400000000000000000000745071357737443600171770ustar00rootroot00000000000000 // Test library configuration for windows.cfg // // Usage: // $ cppcheck --check-library --library=windows --enable=information --error-exitcode=1 --inline-suppr --suppress=missingIncludeSystem test/cfg/windows.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include void uninitvar__putenv(char * envstr) { // No warning is expected (void)_putenv(envstr); char * p; // cppcheck-suppress uninitvar (void)_putenv(p); } void nullPointer__putenv(char * envstr) { // No warning is expected (void)_putenv(envstr); char * p=NULL; // cppcheck-suppress nullPointer (void)_putenv(p); } void invalidFunctionArg__getcwd(char * buffer) { // Passing NULL as the buffer forces getcwd to allocate // memory for the path, which allows the code to support file paths // longer than _MAX_PATH, which are supported by NTFS. if ((buffer = _getcwd(NULL, 0)) == NULL) { return; } free(buffer); } void nullPointer__get_timezone(long *sec) { // No warning is expected (void)_get_timezone(sec); long *pSec = NULL; // cppcheck-suppress nullPointer (void)_get_timezone(pSec); } void nullPointer__get_daylight(int *h) { // No warning is expected (void)_get_daylight(h); int *pHours = NULL; // cppcheck-suppress nullPointer (void)_get_daylight(pHours); } void validCode() { DWORD dwordInit = 0; WORD wordInit = 0; BYTE byteInit = 0; // Valid Semaphore usage, no leaks, valid arguments HANDLE hSemaphore1; hSemaphore1 = CreateSemaphore(NULL, 0, 1, NULL); CloseHandle(hSemaphore1); HANDLE hSemaphore2; hSemaphore2 = CreateSemaphoreEx(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); CloseHandle(hSemaphore2); HANDLE hSemaphore3; hSemaphore3 = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, "sem"); CloseHandle(hSemaphore3); // Valid lstrcat usage, but with warning because it is deprecated char buf[30] = "hello world"; // cppcheck-suppress lstrcatCalled lstrcat(buf, "test"); // cppcheck-suppress strlwrCalled strlwr(buf); // cppcheck-suppress struprCalled strupr(buf); // Valid Mutex usage, no leaks, valid arguments HANDLE hMutex1; hMutex1 = CreateMutex(NULL, TRUE, NULL); if (hMutex1) { ReleaseMutex(hMutex); } CloseHandle(hMutex1); HANDLE hMutex2; hMutex2 = CreateMutexEx(NULL, NULL, 0, MUTEX_ALL_ACCESS); CloseHandle(hMutex2); HANDLE hMutex3; hMutex3 = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "sem"); CloseHandle(hMutex3); // Valid Module usage, no leaks, valid arguments HMODULE hModule = GetModuleHandle(L"My.dll"); FreeLibrary(hModule); hModule = GetModuleHandle(TEXT("somedll")); FreeLibrary(hModule); hModule = GetModuleHandle(NULL); FreeLibrary(hModule); // Valid Event usage, no leaks, valid arguments HANDLE event; event = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL != event) { SetEvent(event); CloseHandle(event); } event = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"testevent"); if (NULL != event) { PulseEvent(event); SetEvent(event); CloseHandle(event); } event = CreateEventEx(NULL, L"testevent3", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); if (NULL != event) { ResetEvent(event); CloseHandle(event); } void *pMem1 = _malloca(1); _freea(pMem1); // Memory from _alloca must not be freed void *pMem2 = _alloca(10); memset(pMem2, 0, 10); SYSTEMTIME st; GetSystemTime(&st); DWORD lastError = GetLastError(); SetLastError(lastError); PSID pEveryoneSID = NULL; SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID) FreeSid(pEveryoneSID); LPVOID pMem = HeapAlloc(GetProcessHeap(), 0, 10); pMem = HeapReAlloc(GetProcessHeap(), 0, pMem, 0); HeapFree(GetProcessHeap(), 0, pMem); char bufC[50]; sprintf_s(bufC, "Hello"); printf("%s", bufC); sprintf_s(bufC, "%s", "test"); printf("%s", bufC); sprintf_s(bufC, _countof(bufC), "%s", "test"); printf("%s", bufC); wchar_t bufWC[50]; swprintf_s(bufWC, L"Hello"); wprintf(L"%s\n", bufWC); swprintf_s(bufWC, L"%s %d", L"swprintf_s", 3); wprintf(L"%s\n", bufWC); swprintf_s(bufWC, _countof(bufWC), L"%s %d", L"swprintf_s", 6); wprintf(L"%s\n", bufWC); TCHAR bufTC[50]; _stprintf(bufTC, TEXT("Hello")); _tprintf(TEXT("%s"), bufTC); _stprintf(bufTC, TEXT("%d"), 1); _tprintf(TEXT("%s"), bufTC); _stprintf(bufTC, _countof(bufTC), TEXT("%d"), 2); _tprintf(TEXT("%s"), bufTC); GetUserName(NULL, &dwordInit); dwordInit = 10; GetUserName(bufTC, _countof(bufTC)); WSADATA wsaData = {0}; WSAStartup(2, &wsaData); SOCKET sock = socket(1, 2, 3); u_long ulongInit = 0; ioctlsocket(sock, FIONBIO, &ulongInit); if (sock != INVALID_SOCKET) { closesocket(sock); } WSACleanup(); wordInit = MAKEWORD(1, 2); // cppcheck-suppress redundantAssignment dwordInit = MAKELONG(1, 2); // cppcheck-suppress redundantAssignment wordInit = LOWORD(dwordInit); byteInit = LOBYTE(wordInit); wordInit = HIWORD(dwordInit); // cppcheck-suppress redundantAssignment byteInit = HIBYTE(wordInit); if (byteInit) {} bool boolVar; uint8_t byteBuf[5] = {0}; uint8_t byteBuf2[10] = {0}; boolVar = RtlEqualMemory(byteBuf, byteBuf2, sizeof(byteBuf)); if (boolVar) {} boolVar = RtlCompareMemory(byteBuf, byteBuf2, sizeof(byteBuf)); if (boolVar) {} RtlMoveMemory(byteBuf, byteBuf2, sizeof(byteBuf)); RtlCopyMemory(byteBuf, byteBuf2, sizeof(byteBuf)); RtlZeroMemory(byteBuf, sizeof(byteBuf)); ZeroMemory(byteBuf, sizeof(byteBuf)); RtlSecureZeroMemory(byteBuf, sizeof(byteBuf)); SecureZeroMemory(byteBuf, sizeof(byteBuf)); RtlFillMemory(byteBuf, sizeof(byteBuf), 0xff); // cppcheck-suppress LocalAllocCalled HLOCAL pLocalAlloc = LocalAlloc(1, 2); LocalFree(pLocalAlloc); // cppcheck-suppress lstrlenCalled (void)lstrlen(bufTC); // cppcheck-suppress lstrlenCalled (void)lstrlen(NULL); // Intrinsics __noop(); __noop(1, "test", NULL); __nop(); void * pAlloc1 = _aligned_malloc(100, 2); _aligned_free(pAlloc1); ::PostMessage(nullptr, WM_QUIT, 0, 0); printf("%zu", __alignof(int)); printf("%zu", _alignof(double)); // Valid Library usage, no leaks, valid arguments HINSTANCE hInstLib = LoadLibrary(L"My.dll"); FreeLibrary(hInstLib); hInstLib = LoadLibraryA("My.dll"); FreeLibrary(hInstLib); hInstLib = LoadLibraryEx(L"My.dll", NULL, 0); FreeLibrary(hInstLib); hInstLib = LoadLibraryExW(L"My.dll", NULL, 0); FreeLibrary(hInstLib); hInstLib = ::LoadLibrary(L"My.dll"); FreeLibraryAndExitThread(hInstLib, 0); // Does not return! Must be at the end! } void bufferAccessOutOfBounds() { wchar_t buf[10]; // Verifying _countof macro configuration // Valid loop over array for (size_t i = 0; i < _countof(buf); ++i) { buf[i] = L'\0'; } // Wrong loop over array accessing one element past the end for (size_t i = 0; i <= _countof(buf); ++i) { // cppcheck-suppress arrayIndexOutOfBounds buf[i] = L'\0'; } uint8_t byteBuf[5] = {0}; uint8_t byteBuf2[10] = {0}; // TODO ticket #8412 cppcheck-suppress ignoredReturnValue // cppcheck-suppress bufferAccessOutOfBounds RtlEqualMemory(byteBuf, byteBuf2, 20); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress bufferAccessOutOfBounds RtlCompareMemory(byteBuf, byteBuf2, 20); // cppcheck-suppress bufferAccessOutOfBounds RtlMoveMemory(byteBuf, byteBuf2, 20); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds MoveMemory(byteBuf, byteBuf2, 20); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds RtlCopyMemory(byteBuf, byteBuf2, 20); // TODO cppcheck-suppress redundantCopy // cppcheck-suppress bufferAccessOutOfBounds CopyMemory(byteBuf, byteBuf2, 20); // cppcheck-suppress bufferAccessOutOfBounds RtlZeroMemory(byteBuf, sizeof(byteBuf)+1); // cppcheck-suppress bufferAccessOutOfBounds ZeroMemory(byteBuf, sizeof(byteBuf)+1); // cppcheck-suppress bufferAccessOutOfBounds RtlSecureZeroMemory(byteBuf, sizeof(byteBuf)+1); // cppcheck-suppress bufferAccessOutOfBounds SecureZeroMemory(byteBuf, sizeof(byteBuf)+1); // cppcheck-suppress bufferAccessOutOfBounds RtlFillMemory(byteBuf, sizeof(byteBuf)+1, 0x01); // cppcheck-suppress bufferAccessOutOfBounds FillMemory(byteBuf, sizeof(byteBuf)+1, 0x01); char * pAlloc1 = _malloca(32); memset(pAlloc1, 0, 32); // cppcheck-suppress bufferAccessOutOfBounds memset(pAlloc1, 0, 33); _freea(pAlloc1); } void mismatchAllocDealloc() { char * pChar = _aligned_malloc(100, 2); // cppcheck-suppress mismatchAllocDealloc free(pChar); pChar = _malloca(32); // cppcheck-suppress mismatchAllocDealloc _aligned_free(pChar); } void nullPointer() { HANDLE hSemaphore; // cppcheck-suppress nullPointer hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, NULL); CloseHandle(hSemaphore); // cppcheck-suppress lstrcatCalled // cppcheck-suppress nullPointer lstrcat(NULL, "test"); char buf[10] = "\0"; // cppcheck-suppress lstrcatCalled // cppcheck-suppress nullPointer lstrcat(buf, NULL); HANDLE hMutex; // cppcheck-suppress nullPointer hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, NULL); CloseHandle(hMutex); //Incorrect: 1. parameter, must not be null // cppcheck-suppress nullPointer FARPROC pAddr = GetProcAddress(NULL, "name"); (void)pAddr; HMODULE * phModule = NULL; // cppcheck-suppress nullPointer GetModuleHandleEx(0, NULL, phModule); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer OpenEvent(EVENT_ALL_ACCESS, FALSE, NULL); HANDLE hEvent = NULL; // cppcheck-suppress nullPointer PulseEvent(hEvent); // cppcheck-suppress nullPointer ResetEvent(hEvent); // cppcheck-suppress nullPointer SetEvent(hEvent); char *str = NULL; // cppcheck-suppress strlwrCalled // cppcheck-suppress nullPointer strlwr(str); // cppcheck-suppress struprCalled // cppcheck-suppress nullPointer strupr(str); // cppcheck-suppress nullPointer GetSystemTime(NULL); // cppcheck-suppress nullPointer GetLocalTime(NULL); // TODO: error message: arg1 must not be nullptr if variable pointed to by arg2 is not 0 DWORD dwordInit = 10; GetUserName(NULL, &dwordInit); TCHAR bufTC[10]; // cppcheck-suppress nullPointer GetUserName(bufTC, NULL); SOCKET socketInit = {0}; sockaddr sockaddrUninit; int intInit = 0; int *pIntNull = NULL; char charArray[] = "test"; // cppcheck-suppress nullPointer WSAStartup(1, NULL); // cppcheck-suppress nullPointer bind(socketInit, NULL, 5); // cppcheck-suppress nullPointer getpeername(socketInit, NULL, &intInit); // cppcheck-suppress nullPointer getpeername(socketInit, &sockaddrUninit, pIntNull); // cppcheck-suppress nullPointer getsockopt(sockInit, 1, 2, NULL, &intInit); // cppcheck-suppress nullPointer getsockopt(sockInit, 1, 2, charArray, pIntNull); } void memleak_malloca() { // cppcheck-suppress unreadVariable void *pMem = _malloca(10); // cppcheck-suppress memleak } void memleak_AllocateAndInitializeSid() { PSID pEveryoneSID = NULL; SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID) // TODO: enable when #6994 is implemented cppcheck-suppress memleak } void memleak_HeapAlloc() { LPVOID pMem; pMem = HeapAlloc(GetProcessHeap(), 0, 10); HeapValidate(GetProcessHeap(), 0, pMem); // cppcheck-suppress unreadVariable SIZE_T memSize = HeapSize(GetProcessHeap(), 0, pMem); // cppcheck-suppress memleak } void memleak_LocalAlloc() { LPTSTR pszBuf; // cppcheck-suppress LocalAllocCalled pszBuf = (LPTSTR)LocalAlloc(LPTR, MAX_PATH*sizeof(TCHAR)); (void)LocalSize(pszBuf); (void)LocalFlags(pszBuf); LocalLock(pszBuf); LocalUnlock(pszBuf); // cppcheck-suppress memleak } void resourceLeak_CreateSemaphoreA() { HANDLE hSemaphore; // cppcheck-suppress unreadVariable hSemaphore = CreateSemaphoreA(NULL, 0, 1, "sem1"); // cppcheck-suppress resourceLeak } void resourceLeak_CreateSemaphoreEx() { HANDLE hSemaphore; // cppcheck-suppress unreadVariable hSemaphore = CreateSemaphoreEx(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); // cppcheck-suppress resourceLeak } void resourceLeak_OpenSemaphore() { HANDLE hSemaphore; // cppcheck-suppress unreadVariable hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, "sem"); // cppcheck-suppress resourceLeak } void resourceLeak_CreateMutexA() { HANDLE hMutex; // cppcheck-suppress unreadVariable hMutex = CreateMutexA(NULL, TRUE, "sem1"); // cppcheck-suppress resourceLeak } void resourceLeak_CreateMutexEx() { HANDLE hMutex; // cppcheck-suppress unreadVariable hMutex = CreateMutexEx(NULL, "sem", 0, MUTEX_ALL_ACCESS); // cppcheck-suppress resourceLeak } void resourceLeak_OpenMutex() { HANDLE hMutex; // cppcheck-suppress unreadVariable hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, "sem"); // cppcheck-suppress resourceLeak } void resourceLeak_LoadLibrary() { HINSTANCE hInstLib; hInstLib = ::LoadLibrary(L"My.dll"); typedef BOOL (WINAPI *fpFunc)(); // cppcheck-suppress unreadVariable fpFunc pFunc = GetProcAddress(hInstLib, "name"); // cppcheck-suppress resourceLeak } void resourceLeak_CreateEvent() { HANDLE hEvent; hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); SetEvent(hEvent); // cppcheck-suppress resourceLeak } void resourceLeak_CreateEventExA() { HANDLE hEvent; // cppcheck-suppress unreadVariable hEvent = CreateEventExA(NULL, "test", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); // cppcheck-suppress resourceLeak } void resourceLeak_OpenEventW() { HANDLE hEvent; // cppcheck-suppress unreadVariable hEvent = OpenEventW(EVENT_ALL_ACCESS, TRUE, L"testevent"); // cppcheck-suppress resourceLeak } void resourceLeak_socket() { SOCKET sock; // cppcheck-suppress unreadVariable sock = socket(1, 2, 3); // cppcheck-suppress resourceLeak } void ignoredReturnValue() { // cppcheck-suppress leakReturnValNotUsed CreateSemaphoreW(NULL, 0, 1, NULL); // cppcheck-suppress leakReturnValNotUsed CreateSemaphoreExA(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); // cppcheck-suppress leakReturnValNotUsed OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, "sem"); // cppcheck-suppress leakReturnValNotUsed CreateMutexW(NULL, FALSE, NULL); // cppcheck-suppress leakReturnValNotUsed CreateMutexExA(NULL, NULL, 1, MUTEX_ALL_ACCESS); // cppcheck-suppress leakReturnValNotUsed OpenMutexA(MUTEX_ALL_ACCESS, TRUE, "sem"); // cppcheck-suppress leakReturnValNotUsed LoadLibrary(L"My.dll"); // cppcheck-suppress leakReturnValNotUsed LoadLibraryEx(L"My.dll", NULL, 0); HINSTANCE hInstLib = LoadLibrary(L"My.dll"); // cppcheck-suppress ignoredReturnValue GetProcAddress(hInstLib, "name"); FreeLibrary(hInstLib); // cppcheck-suppress leakReturnValNotUsed CreateEvent(NULL, FALSE, FALSE, NULL); // cppcheck-suppress leakReturnValNotUsed OpenEvent(EVENT_ALL_ACCESS, FALSE, L"testevent"); // cppcheck-suppress leakReturnValNotUsed CreateEventEx(NULL, L"test", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed _malloca(10); // cppcheck-suppress ignoredReturnValue _alloca(5); // cppcheck-suppress ignoredReturnValue GetLastError(); // cppcheck-suppress ignoredReturnValue GetProcessHeap() // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed HeapAlloc(GetProcessHeap(), 0, 10); // cppcheck-suppress ignoredReturnValue // cppcheck-suppress leakReturnValNotUsed HeapReAlloc(GetProcessHeap(), 0, 1, 0); // cppcheck-suppress leakReturnValNotUsed socket(1, 2, 3); // cppcheck-suppress ignoredReturnValue _fileno(stdio); // cppcheck-suppress lstrlenCalled // cppcheck-suppress ignoredReturnValue lstrlen(TEXT("test")); } void invalidFunctionArg() { HANDLE hSemaphore; // cppcheck-suppress invalidFunctionArg hSemaphore = CreateSemaphore(NULL, 0, 0, NULL); CloseHandle(hSemaphore); // cppcheck-suppress invalidFunctionArgBool hSemaphore = CreateSemaphore(NULL, 0, 1, true); CloseHandle(hSemaphore); // cppcheck-suppress invalidFunctionArg hSemaphore = CreateSemaphoreEx(NULL, 0, 0, NULL, 0, SEMAPHORE_ALL_ACCESS); CloseHandle(hSemaphore); // cppcheck-suppress invalidFunctionArg hSemaphore = CreateSemaphoreEx(NULL, 0, 1, NULL, 1, SEMAPHORE_ALL_ACCESS); CloseHandle(hSemaphore); HANDLE hMutex; // cppcheck-suppress invalidFunctionArgBool hMutex = CreateMutex(NULL, TRUE, false); CloseHandle(hMutex); // cppcheck-suppress invalidFunctionArgBool hMutex = CreateMutex(NULL, FALSE, true); CloseHandle(hMutex); // cppcheck-suppress invalidFunctionArg hMutex = CreateMutexEx(NULL, NULL, 3, MUTEX_ALL_ACCESS); CloseHandle(hMutex); //Incorrect: 2. parameter to LoadLibraryEx() must be NULL // cppcheck-suppress invalidFunctionArg HINSTANCE hInstLib = LoadLibraryEx(L"My.dll", 1, 0); FreeLibrary(hInstLib); // cppcheck-suppress invalidFunctionArg void *pMem = _malloca(-1); _freea(pMem); // FIXME cppcheck-suppress unreadVariable // cppcheck-suppress invalidFunctionArg pMem = _alloca(-5); } void uninitvar() { HANDLE hSemaphore; // cppcheck-suppress uninitvar CloseHandle(hSemaphore); char buf[10]; // cppcheck-suppress lstrcatCalled // cppcheck-suppress uninitvar lstrcat(buf, "test"); buf[0] = '\0'; char buf2[2]; // cppcheck-suppress lstrcatCalled // cppcheck-suppress uninitvar lstrcat(buf, buf2); HANDLE hMutex1, hMutex2; // cppcheck-suppress uninitvar ReleaseMutex(hMutex1); // cppcheck-suppress uninitvar CloseHandle(hMutex2); HANDLE hEvent; // cppcheck-suppress uninitvar PulseEvent(hEvent); // cppcheck-suppress uninitvar ResetEvent(hEvent); // cppcheck-suppress uninitvar SetEvent(hEvent); // cppcheck-suppress uninitvar CloseHandle(hEvent); char buf_uninit1[10]; char buf_uninit2[10]; // cppcheck-suppress strlwrCalled // cppcheck-suppress uninitvar strlwr(buf_uninit1); // cppcheck-suppress struprCalled // cppcheck-suppress uninitvar strupr(buf_uninit2); DWORD dwordUninit; // cppcheck-suppress uninitvar SetLastError(dwordUninit); DWORD dwordUninit; // cppcheck-suppress uninitvar GetUserName(NULL, &dwordUninit); FILE *pFileUninit; // cppcheck-suppress uninitvar // cppcheck-suppress ignoredReturnValue _fileno(pFileUninit); } void errorPrintf() { char bufC[50]; // cppcheck-suppress wrongPrintfScanfArgNum sprintf_s(bufC, _countof(bufC), "%s %d", "sprintf_s"); printf("%s\n", bufC); // cppcheck-suppress wrongPrintfScanfArgNum sprintf_s(bufC, "%s %d", "sprintf_s"); printf("%s\n", bufC); // cppcheck-suppress wrongPrintfScanfArgNum sprintf_s(bufC, _countof(bufC), "test", 0); printf("%s\n", bufC); // cppcheck-suppress wrongPrintfScanfArgNum sprintf_s(bufC, "test", "sprintf_s"); printf("%s\n", bufC); // cppcheck-suppress invalidPrintfArgType_s sprintf_s(bufC, _countof(bufC), "%s", 1); printf("%s\n", bufC); // cppcheck-suppress invalidPrintfArgType_s sprintf_s(bufC, "%s", 1); printf("%s\n", bufC); wchar_t bufWC[50]; // cppcheck-suppress wrongPrintfScanfArgNum swprintf_s(bufWC, _countof(bufWC), L"%s %d", L"swprintf_s"); wprintf(L"%s\n", bufWC); // cppcheck-suppress wrongPrintfScanfArgNum swprintf_s(bufWC, L"%s %d", L"swprintf_s"); wprintf(L"%s\n", bufWC); // cppcheck-suppress wrongPrintfScanfArgNum swprintf_s(bufWC, _countof(bufWC), L"test", 0); wprintf(L"%s\n", bufWC); // cppcheck-suppress wrongPrintfScanfArgNum swprintf_s(bufWC, L"test", L"swprintf_s"); wprintf(L"%s\n", bufWC); // cppcheck-suppress invalidPrintfArgType_s swprintf_s(bufWC, _countof(bufWC), L"%s", 1); wprintf(L"%s\n", bufWC); // cppcheck-suppress invalidPrintfArgType_s swprintf_s(bufWC, L"%s", 1); wprintf(L"%s\n", bufWC); TCHAR bufTC[50]; // cppcheck-suppress wrongPrintfScanfArgNum _stprintf_s(bufTC, _countof(bufTC), TEXT("%s %d"), TEXT("_stprintf_s")); _tprintf(L"%s\n", bufTC); // cppcheck-suppress wrongPrintfScanfArgNum _stprintf_s(bufTC, TEXT("%s %d"), TEXT("_stprintf_s")); _tprintf(TEXT("%s\n"), bufTC); // cppcheck-suppress wrongPrintfScanfArgNum _stprintf_s(bufTC, _countof(bufTC), TEXT("test"), 0); _tprintf(TEXT("%s\n"), bufTC); // cppcheck-suppress wrongPrintfScanfArgNum _stprintf_s(bufTC, TEXT("test"), TEXT("_stprintf_s")); _tprintf(TEXT("%s\n"), bufTC); // cppcheck-suppress invalidPrintfArgType_s _stprintf_s(bufTC, _countof(bufTC), TEXT("%s"), 1); _tprintf(TEXT("%s\n"), bufTC); // cppcheck-suppress invalidPrintfArgType_s _stprintf_s(bufTC, TEXT("%s"), 1); _tprintf(TEXT("%s\n"), bufTC); } void allocDealloc_GetModuleHandleEx() { // For GetModuleHandleEx it depends on the first argument if FreeLibrary // must be called or is not allowed to be called. // If the first argument is GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT // (0x00000002), a call to FreeLibrary is not allowed, otherwise it is // necessary. // Since this is not possible to configure at the moment cppcheck should // accept not calling FreeLibrary and also calling it for the handle. // TODO: Enhance cppcheck to conditionally check for alloc/dealloc issues. // No warning because of correct FreeLibrary on 'hModule' should be issued. HMODULE hModule1; if (GetModuleHandleEx(0, NULL, &hModule1)) { FreeLibrary(hModule1); } //This is a false negative, but it is not detected to avoid false positives. HMODULE hModule2; GetModuleHandleEx(0, NULL, &hModule2); } void uninitvar_tolower(_locale_t l) { int c1, c2; // cppcheck-suppress uninitvar (void)_tolower(c1); // cppcheck-suppress uninitvar (void)_tolower_l(c2, l); } void uninitvar_toupper(_locale_t l) { int c1, c2; // cppcheck-suppress uninitvar (void)_toupper(c1); // cppcheck-suppress uninitvar (void)_toupper_l(c2, l); } void uninitvar_towlower(_locale_t l) { wint_t i; // cppcheck-suppress uninitvar (void)_towlower_l(i, l); } void uninitvar_towupper(_locale_t l) { wint_t i; // cppcheck-suppress uninitvar (void)_towupper_l(i, l); } void oppositeInnerCondition_SUCCEEDED_FAILED(HRESULT hr) { if (SUCCEEDED(hr)) { // TODO ticket #8596 cppcheck-suppress oppositeInnerCondition if (FAILED(hr)) { } } } /*HANDLE WINAPI CreateThread( _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_opt_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_opt_ LPDWORD lpThreadId );*/ HANDLE test_CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) { // Create uninitialized variables LPSECURITY_ATTRIBUTES uninit_lpThreadAttributes; SIZE_T uninit_dwStackSize; LPTHREAD_START_ROUTINE uninit_lpStartAddress; LPVOID uninit_lpParameter; DWORD uninit_dwCreationFlags; // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress uninitvar (void) CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, uninit_dwCreationFlags, lpThreadId); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress uninitvar (void) CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, uninit_lpParameter, dwCreationFlags, lpThreadId); // @todo uninitvar shall be reported // cppcheck-suppress leakReturnValNotUsed (void) CreateThread(lpThreadAttributes, dwStackSize, uninit_lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress uninitvar (void) CreateThread(lpThreadAttributes, uninit_dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); // @todo uninitvar shall be reported // cppcheck-suppress leakReturnValNotUsed (void) CreateThread(uninit_lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress nullPointer (void) CreateThread(lpThreadAttributes, dwStackSize, 0, lpParameter, dwCreationFlags, lpThreadId); // cppcheck-suppress leakReturnValNotUsed // cppcheck-suppress invalidFunctionArg (void) CreateThread(lpThreadAttributes, -1, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); // no warning shall be shown for return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); } // unsigned char *_mbscat(unsigned char *strDestination, const unsigned char *strSource); unsigned char * uninitvar_mbscat(unsigned char *strDestination, const unsigned char *strSource) { unsigned char *uninit_deststr; unsigned char *uninit_srcstr; // cppcheck-suppress uninitvar (void)_mbscat(uninit_deststr,uninit_srcstr); // cppcheck-suppress uninitvar (void)_mbscat(strDestination,uninit_srcstr); // cppcheck-suppress uninitvar (void)_mbscat(uninit_deststr,uninit_deststr); // no warning shall be shown for return _mbscat(strDestination,strSource); } // unsigned char *_mbscat(unsigned char *strDestination, const unsigned char *strSource); unsigned char * nullPointer_mbscat(unsigned char *strDestination, const unsigned char *strSource) { // cppcheck-suppress nullPointer (void)_mbscat(0,strSource); // cppcheck-suppress nullPointer (void)_mbscat(strDestination,0); // no warning shall be shown for return _mbscat(strDestination,strSource); } // errno_t _mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource ); error_t uninitvar_mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource) { unsigned char *uninit_strDestination; size_t uninit_numberOfElements; unsigned char *uninit_strSource; // cppcheck-suppress uninitvar (void)_mbscat_s(uninit_strDestination, numberOfElements, strSource); // cppcheck-suppress uninitvar (void)_mbscat_s(strDestination, uninit_numberOfElements, strSource); // cppcheck-suppress uninitvar (void)_mbscat_s(strDestination, numberOfElements, uninit_strSource); // no warning shall be shown for return _mbscat_s(strDestination, numberOfElements, strSource); } // errno_t _mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource ); error_t nullPointer_mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource) { // cppcheck-suppress nullPointer (void)_mbscat_s(0, numberOfElements, strSource); // cppcheck-suppress nullPointer (void)_mbscat_s(strDestination, numberOfElements, 0); // no warning shall be shown for return _mbscat_s(strDestination, numberOfElements, strSource); } // errno_t _strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale); error_t uninitvar__strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale) { size_t uninit_numberOfElements; const char *uninit_strSource; size_t uninit_count; _locale_t uninit_locale; // cppcheck-suppress uninitvar (void)_strncpy_s_l(strDest, uninit_numberOfElements, strSource, count, locale); // cppcheck-suppress uninitvar (void)_strncpy_s_l(strDest, numberOfElements, uninit_strSource, count, locale); // cppcheck-suppress uninitvar (void)_strncpy_s_l(strDest, numberOfElements, strSource, uninit_count, locale); // cppcheck-suppress uninitvar (void)_strncpy_s_l(strDest, numberOfElements, strSource, count, uninit_locale); // no warning shall be shown for return _strncpy_s_l(strDest, numberOfElements, strSource, count, locale); } // errno_t _strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale); error_t nullPointer__strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale) { // cppcheck-suppress nullPointer (void)_strncpy_s_l(0, numberOfElements, strSource, count, locale); // cppcheck-suppress nullPointer (void)_strncpy_s_l(strDest, numberOfElements, 0, count, locale); // no warning shall be shown for return _strncpy_s_l(strDest, numberOfElements, strSource, count, locale); } void GetShortPathName_validCode(TCHAR* lpszPath) { long length = GetShortPathName(lpszPath, NULL, 0); if (length == 0) { _tprintf(TEXT("error")); return; } TCHAR* buffer = new TCHAR[length]; length = GetShortPathName(lpszPath, buffer, length); if (length == 0) { delete [] buffer; _tprintf(TEXT("error")); return; } _tprintf(TEXT("long name = %s short name = %s"), lpszPath, buffer); delete [] buffer; } class MyClass :public CObject { DECLARE_DYNAMIC(MyClass) DECLARE_DYNCREATE(MyClass) DECLARE_SERIAL(MyClass) public: MyClass() {} }; IMPLEMENT_DYNAMIC(MyClass, CObject) IMPLEMENT_DYNCREATE(MyClass, CObject) IMPLEMENT_SERIAL(MyClass,CObject, 42) cppcheck-1.90/test/cfg/wxwidgets.cpp000066400000000000000000000207011357737443600175150ustar00rootroot00000000000000 // Test library configuration for wxwidgets.cfg // // Usage: // $ ./cppcheck --check-library --enable=information --enable=style --error-exitcode=1 --suppress=missingIncludeSystem --inline-suppr '--template="{file}:{line}:{severity}:{id}:{message}"' --inconclusive --library=wxwidgets -f test/cfg/wxwidgets.cpp // => // No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void validCode() { wxString str = wxGetCwd(); (void)str; wxLogGeneric(wxLOG_Message, "test %d", 0); wxLogMessage("test %s", "str"); wxString translation1 = _("text"); wxString translation2 = wxGetTranslation("text"); wxString translation3 = wxGetTranslation("string", "domain"); (void)translation1; (void)translation2; (void)translation3; } #if wxUSE_GUI==1 void validGuiCode() { #if wxUSE_SPINCTRL==1 extern wxSpinCtrl spinCtrlInstance; spinCtrlInstance.SetBase(10); spinCtrlInstance.SetBase(16); #endif } #endif void nullPointer(const wxString &str) { // cppcheck-suppress nullPointer wxLogGeneric(wxLOG_Message, (char*)NULL); // cppcheck-suppress nullPointer wxLogMessage((char*)NULL); double *doublePtr = NULL; // cppcheck-suppress nullPointer (void)str.ToDouble(doublePtr); double *doublePtr1 = NULL; // cppcheck-suppress nullPointer (void)str.ToCDouble(doublePtr1); long * longPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToLong(longPtr); long * longPtr1 = NULL; // cppcheck-suppress nullPointer (void)str.ToCLong(longPtr1); unsigned long * ulongPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToULong(ulongPtr); unsigned long * ulongPtr1 = NULL; // cppcheck-suppress nullPointer (void)str.ToCULong(ulongPtr1); long long * longLongPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToLongLong(longLongPtr); unsigned long long * ulongLongPtr = NULL; // cppcheck-suppress nullPointer (void)str.ToULongLong(ulongLongPtr); } void nullPointer_wxSizer_Add(wxSizer &sizer, wxWindow *w) { wxWindow * const ptr = 0; // @todo cppcheck-suppress nullPointer sizer.Add(ptr); // No warning shall be issued for sizer.Add(w); } void uninitvar_wxSizer_Add(wxSizer &sizer, wxWindow *w,wxObject* userData) { int uninit1, uninit2, uninit3; // cppcheck-suppress uninitvar sizer.Add(w,uninit1); // cppcheck-suppress uninitvar sizer.Add(w,4,uninit2); // cppcheck-suppress uninitvar sizer.Add(w,4,2,uninit3,userData); } void ignoredReturnValue(const wxString &s) { // cppcheck-suppress ignoredReturnValue wxGetCwd(); // cppcheck-suppress ignoredReturnValue wxAtoi(s); // cppcheck-suppress ignoredReturnValue wxAtol(s); // cppcheck-suppress ignoredReturnValue wxAtof(s); } void invalidFunctionArg(const wxString &str) { #if wxUSE_SPINCTRL==1 extern wxSpinCtrl spinCtrlInstance; // cppcheck-suppress invalidFunctionArg spinCtrlInstance.SetBase(0); // cppcheck-suppress invalidFunctionArg spinCtrlInstance.SetBase(5); #endif long l; // cppcheck-suppress invalidFunctionArg (void)str.ToLong(&l, -1); // cppcheck-suppress invalidFunctionArg (void)str.ToLong(&l, 1); // cppcheck-suppress invalidFunctionArg (void)str.ToLong(&l, 37); } void uninitvar(wxWindow &w) { wxLogLevel logLevelUninit; char cBufUninit[10]; char *pcUninit; bool uninitBool; // cppcheck-suppress uninitvar wxLogGeneric(logLevelUninit, "test"); // cppcheck-suppress uninitvar wxLogMessage(cBufUninit); // cppcheck-suppress uninitvar wxLogMessage(pcUninit); // cppcheck-suppress uninitvar w.Close(uninitBool); } void uninitvar_wxStaticText(wxStaticText &s) { // no warning s.Wrap(-1); bool uninitBool; // cppcheck-suppress uninitvar s.Wrap(uninitBool); } void uninitvar_wxString_NumberConversion(const wxString &str, const int numberBase) { int uninitInteger1; int uninitInteger2; int uninitInteger3; int uninitInteger4; int uninitInteger5; int uninitInteger6; long l; long long ll; unsigned long ul; unsigned long long ull; // cppcheck-suppress uninitvar (void)str.ToLong(&l, uninitInteger1); // cppcheck-suppress uninitvar (void)str.ToLongLong(&ll, uninitInteger2); // cppcheck-suppress uninitvar (void)str.ToULong(&ul, uninitInteger3); // cppcheck-suppress uninitvar (void)str.ToULongLong(&ull, uninitInteger4); // cppcheck-suppress uninitvar (void)str.ToCLong(&l, uninitInteger5); // cppcheck-suppress uninitvar (void)str.ToCULong(&ul, uninitInteger6); } void uninitvar_SetMenuBar(wxFrame * const framePtr, wxMenuBar * const menuBarPtr) { wxMenuBar *menuBar; // cppcheck-suppress uninitvar framePtr->SetMenuBar(menuBar); framePtr->SetMenuBar(menuBarPtr); } void uninitvar_wxMenuBarAppend(wxMenuBar * const menuBarPtr, wxMenu * const menuPtr, const wxString &title) { wxMenu *menu; // cppcheck-suppress uninitvar menuBarPtr->Append(menu, title); menuBarPtr->Append(menuPtr, title); } void deprecatedFunctions_wxDataViewCustomRenderer(wxDataViewCustomRenderer &dataViewCustomRenderer, wxPoint cursor, wxRect cell, wxDataViewModel *model, const wxDataViewItem &item, unsigned int col) { // cppcheck-suppress ActivateCalled dataViewCustomRenderer.Activate(cell, model, item, col); // cppcheck-suppress LeftClickCalled dataViewCustomRenderer.LeftClick(cursor, cell, model, item, col); } void deprecatedFunctions(wxApp &a, const wxString &s, wxArtProvider *artProvider, wxCalendarCtrl &calenderCtrl, wxComboCtrl &comboCtrl, wxChar * path) { #ifdef __WXOSX__ // cppcheck-suppress MacOpenFileCalled a.MacOpenFile(s); #endif #if wxCHECK_VERSION(3, 1, 0) // wxWidets-3.1.0 or higher: // Some functions are not available anymore in newer versions // @todo cppcheck-suppress ShowPopupCalled comboCtrl.ShowPopup(); #else // cppcheck-suppress InsertCalled wxArtProvider::Insert(artProvider); // cppcheck-suppress GetTextIndentCalled // cppcheck-suppress ignoredReturnValue comboCtrl.GetTextIndent(); // cppcheck-suppress HidePopupCalled comboCtrl.HidePopup(true); // cppcheck-suppress HidePopupCalled comboCtrl.HidePopup(false); // cppcheck-suppress HidePopupCalled comboCtrl.HidePopup(/*default=false*/); // cppcheck-suppress SetTextIndentCalled comboCtrl.SetTextIndent(0); #if wxUSE_DEBUG_CONTEXT==1 // cppcheck-suppress GetLevelCalled // cppcheck-suppress ignoredReturnValue wxDebugContext::GetLevel(); // cppcheck-suppress SetLevelCalled wxDebugContext::SetLevel(42); #endif // cppcheck-suppress wxDos2UnixFilenameCalled wxDos2UnixFilename(path); // cppcheck-suppress wxFileNameFromPathCalled // cppcheck-suppress ignoredReturnValue wxFileNameFromPath(wxT_2("../test.c")); #endif #if defined(__WXMSW__) || defined(__WXGTK__) // EnableYearChange() is not available on these GUI systems #else // cppcheck-suppress EnableYearChangeCalled calenderCtrl.EnableYearChange(false); // cppcheck-suppress EnableYearChangeCalled calenderCtrl.EnableYearChange(true); // cppcheck-suppress EnableYearChangeCalled calenderCtrl.EnableYearChange(/*default=yes*/); #endif } void wxString_test1(wxString s) { for (int i = 0; i <= s.size(); ++i) { // cppcheck-suppress stlOutOfBounds s[i] = 'x'; } } void wxString_test2() { wxString s; // cppcheck-suppress containerOutOfBounds s[1] = 'a'; s.append("abc"); s[1] = 'B'; printf("%s", static_cast(s.c_str())); wxPrintf("%s", s); wxPrintf("%s", s.c_str()); s.Clear(); } wxString::iterator wxString_test3() { wxString wxString1; wxString wxString2; // cppcheck-suppress iterators2 for (wxString::iterator it = wxString1.begin(); it != wxString2.end(); ++it) {} wxString::iterator it = wxString1.begin(); // cppcheck-suppress returnDanglingLifetime return it; } cppcheck-1.90/test/cli/000077500000000000000000000000001357737443600147745ustar00rootroot00000000000000cppcheck-1.90/test/cli/helloworld/000077500000000000000000000000001357737443600171475ustar00rootroot00000000000000cppcheck-1.90/test/cli/helloworld/helloworld.cppcheck000066400000000000000000000003121357737443600230200ustar00rootroot00000000000000 helloworld.sln false cppcheck-1.90/test/cli/helloworld/helloworld.sln000066400000000000000000000026311357737443600220420ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "helloworld", "helloworld.vcxproj", "{7319858B-261C-4F0D-B022-92BB896242DD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x64.ActiveCfg = Debug|x64 {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x64.Build.0 = Debug|x64 {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x86.ActiveCfg = Debug|Win32 {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x86.Build.0 = Debug|Win32 {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x64.ActiveCfg = Release|x64 {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x64.Build.0 = Release|x64 {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x86.ActiveCfg = Release|Win32 {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C787A78A-D377-4103-9389-2594C573ED55} EndGlobalSection EndGlobal cppcheck-1.90/test/cli/helloworld/helloworld.vcxproj000066400000000000000000000134721357737443600227460ustar00rootroot00000000000000 Debug Win32 Release Win32 Debug x64 Release x64 15.0 {7319858B-261C-4F0D-B022-92BB896242DD} helloworld 10.0.16299.0 Application true v141 MultiByte Application false v141 true MultiByte Application true v141 MultiByte Application false v141 true MultiByte Level3 Disabled true true Level3 Disabled true true Level3 MaxSpeed true true true true true true Level3 MaxSpeed true true true true true true cppcheck-1.90/test/cli/helloworld/main.c000066400000000000000000000002241357737443600202350ustar00rootroot00000000000000#include int main() { (void)printf("Hello world!\n"); x = 3 / 0; // ERROR return 0; } #ifdef SOME_CONFIG void foo(); #endif cppcheck-1.90/test/cli/proj-inline-suppress/000077500000000000000000000000001357737443600211045ustar00rootroot00000000000000cppcheck-1.90/test/cli/proj-inline-suppress/1.c000066400000000000000000000000171357737443600214060ustar00rootroot00000000000000#include <1.h> cppcheck-1.90/test/cli/proj-inline-suppress/1.h000066400000000000000000000000711357737443600214130ustar00rootroot00000000000000 // cppcheck-suppress zerodiv const int x = 10000 / 0; cppcheck-1.90/test/cli/proj-inline-suppress/2.c000066400000000000000000000001451357737443600214110ustar00rootroot00000000000000// cppcheck-suppress some_warning_id ; there should be a unmatchedSuppression warning about this x; cppcheck-1.90/test/cli/proj-suppress-syntaxError/000077500000000000000000000000001357737443600221665ustar00rootroot00000000000000cppcheck-1.90/test/cli/proj-suppress-syntaxError/1.c000066400000000000000000000002661357737443600224760ustar00rootroot00000000000000 void validCode(int argInt) { // if G_UNLIKELY is not defined this results in a syntax error if G_UNLIKELY(argInt == 1) { } else if (G_UNLIKELY(argInt == 2)) { } } cppcheck-1.90/test/cli/proj-suppress-syntaxError/2.c000066400000000000000000000000131357737443600224650ustar00rootroot00000000000000void f1(); cppcheck-1.90/test/cli/proj-suppress-syntaxError/3.c000066400000000000000000000000141357737443600224670ustar00rootroot00000000000000 void f2(); cppcheck-1.90/test/cli/proj2/000077500000000000000000000000001357737443600160305ustar00rootroot00000000000000cppcheck-1.90/test/cli/proj2/a/000077500000000000000000000000001357737443600162505ustar00rootroot00000000000000cppcheck-1.90/test/cli/proj2/a/a.c000066400000000000000000000000471357737443600166350ustar00rootroot00000000000000x = 3 / 0; #ifdef AAA void aa; #endif cppcheck-1.90/test/cli/proj2/b/000077500000000000000000000000001357737443600162515ustar00rootroot00000000000000cppcheck-1.90/test/cli/proj2/b/b.c000066400000000000000000000000141357737443600166310ustar00rootroot00000000000000x = 3 / 0; cppcheck-1.90/test/cli/proj2/proj2.cppcheck000066400000000000000000000002271357737443600205670ustar00rootroot00000000000000 compile_commands.json cppcheck-1.90/test/cli/proj2/proj2.sln000066400000000000000000000026171357737443600176100ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proj2", "proj2.vcxproj", "{B9ED3CF9-9DB9-4876-9923-6FAD501885D5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x64.ActiveCfg = Debug|x64 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x64.Build.0 = Debug|x64 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x86.ActiveCfg = Debug|Win32 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x86.Build.0 = Debug|Win32 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x64.ActiveCfg = Release|x64 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x64.Build.0 = Release|x64 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x86.ActiveCfg = Release|Win32 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DDBB0F1F-12C9-4A06-B09A-D55E5F0D0A43} EndGlobalSection EndGlobal cppcheck-1.90/test/cli/proj2/proj2.vcxproj000066400000000000000000000135271357737443600205110ustar00rootroot00000000000000 Debug Win32 Release Win32 Debug x64 Release x64 15.0 {B9ED3CF9-9DB9-4876-9923-6FAD501885D5} proj2 10.0.16299.0 Application true v141 MultiByte Application false v141 true MultiByte Application true v141 MultiByte Application false v141 true MultiByte Level3 Disabled true true Level3 Disabled true true Level3 MaxSpeed true true true true true true Level3 MaxSpeed true true true true true true cppcheck-1.90/test/cli/readme.txt000066400000000000000000000004031357737443600167670ustar00rootroot00000000000000 Systemtesting of Cppcheck CLI on some projects addons base path exclude folders importing projects * visual studio * compile database - different generators bear/cmake/.. - different platforms suppressions Different paths: * relative * absolute cppcheck-1.90/test/cli/test-helloworld.py000066400000000000000000000165161357737443600205070ustar00rootroot00000000000000 # python -m pytest test-helloworld.py import os import re from testutils import create_gui_project_file, cppcheck # Run Cppcheck from project path def cppcheck_local(args): cwd = os.getcwd() os.chdir('helloworld') ret, stdout, stderr = cppcheck(args) os.chdir(cwd) return (ret, stdout, stderr) def getRelativeProjectPath(): return 'helloworld' def getAbsoluteProjectPath(): return os.path.join(os.getcwd(), 'helloworld') # Get Visual Studio configurations checking a file # Checking {file} {config}... def getVsConfigs(stdout, filename): ret = [] for line in stdout.split('\n'): if not line.startswith('Checking %s ' % (filename)): continue if not line.endswith('...'): continue res = re.match(r'.* ([A-Za-z0-9|]+)...', line) if res: ret.append(res.group(1)) ret.sort() return ' '.join(ret) def test_relative_path(): ret, stdout, stderr = cppcheck(['--template=cppcheck1', 'helloworld']) filename = os.path.join('helloworld', 'main.c') assert ret == 0 assert stderr == '[%s:5]: (error) Division by zero.\n' % (filename) def test_local_path(): ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '.']) assert ret == 0 assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', prjpath]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert stderr == '[%s:5]: (error) Division by zero.\n' % (filename) def test_addon_local_path(): ret, stdout, stderr = cppcheck_local(['--addon=misra', '--template=cppcheck1', '.']) assert ret == 0 assert stderr == ('[main.c:5]: (error) Division by zero.\n' '[main.c:1]: (style) misra violation (use --rule-texts= to get proper output)\n') def test_addon_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--addon=misra', '--template=cppcheck1', prjpath]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert stderr == ('[%s:5]: (error) Division by zero.\n' '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) def test_addon_relative_path(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['--addon=misra', '--template=cppcheck1', prjpath]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert stdout == 'Checking %s ...\n' % (filename) assert stderr == ('[%s:5]: (error) Division by zero.\n' '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) def test_addon_relative_path(): project_file = 'helloworld/test.cppcheck' create_gui_project_file(project_file, paths=['.'], addon='misra') ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--project=' + project_file]) filename = os.path.join('helloworld', 'main.c') assert ret == 0 assert stdout == 'Checking %s ...\n' % (filename) assert stderr == ('[%s:5]: (error) Division by zero.\n' '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) def test_basepath_relative_path(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck([prjpath, '--template=cppcheck1', '-rp=' + prjpath]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_basepath_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', prjpath, '-rp=' + prjpath]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_vs_project_local_path(): ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '--project=helloworld.vcxproj']) assert ret == 0 assert getVsConfigs(stdout, 'main.c') == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_vs_project_relative_path(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--project=' + os.path.join(prjpath, 'helloworld.vcxproj')]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert getVsConfigs(stdout, filename) == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % (filename) def test_vs_project_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--project=' + os.path.join(prjpath, 'helloworld.vcxproj')]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert getVsConfigs(stdout, filename) == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % (filename) def test_cppcheck_project_local_path(): ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '--platform=win64', '--project=helloworld.cppcheck']) assert ret == 0 assert getVsConfigs(stdout, 'main.c') == 'Debug|x64' assert stderr == '[main.c:5]: (error) Division by zero.\n' def test_cppcheck_project_relative_path(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert getVsConfigs(stdout, filename) == 'Debug|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % (filename) def test_cppcheck_project_absolute_path(): prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) filename = os.path.join(prjpath, 'main.c') assert ret == 0 assert getVsConfigs(stdout, filename) == 'Debug|x64' assert stderr == '[%s:5]: (error) Division by zero.\n' % (filename) def test_suppress_command_line(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['--suppress=zerodiv:' + os.path.join(prjpath, 'main.c'), prjpath]) assert ret == 0 assert stderr == '' prjpath = getAbsoluteProjectPath() ret, stdout, stderr = cppcheck(['--suppress=zerodiv:' + os.path.join(prjpath, 'main.c'), prjpath]) assert ret == 0 assert stderr == '' def test_suppress_project(): project_file = os.path.join('helloworld', 'test.cppcheck') create_gui_project_file(project_file, paths=['.'], suppressions=[{'fileName':'main.c', 'id':'zerodiv'}]) # Relative path ret, stdout, stderr = cppcheck(['--project=' + project_file]) assert ret == 0 assert stderr == '' # Absolute path ret, stdout, stderr = cppcheck(['--project=' + os.path.join(os.getcwd(), 'helloworld', 'test.cppcheck')]) assert ret == 0 assert stderr == '' def test_exclude(): prjpath = getRelativeProjectPath() ret, stdout, stderr = cppcheck(['-i' + prjpath, '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) assert stdout == 'cppcheck: No C or C++ source files found.\n' cppcheck-1.90/test/cli/test-inline-suppress.py000066400000000000000000000011441357737443600214630ustar00rootroot00000000000000 # python -m pytest test-inline-suppress.py import os import re from testutils import cppcheck def test1(): ret, stdout, stderr = cppcheck(['--inline-suppr', 'proj-inline-suppress']) assert ret == 0 # TODO assert len(stderr) == 0 def test2(): ret, stdout, stderr = cppcheck(['proj-inline-suppress']) assert ret == 0 assert len(stderr) > 0 def test_unmatched_suppression(): ret, stdout, stderr = cppcheck(['--inline-suppr', '--enable=information', '--error-exitcode=1', 'proj-inline-suppress/2.c']) assert ret == 1 assert 'Unmatched suppression: some_warning_id' in stderr cppcheck-1.90/test/cli/test-proj2.py000066400000000000000000000147101357737443600173620ustar00rootroot00000000000000 # python -m pytest test-proj2.py import json import os from testutils import create_gui_project_file, cppcheck COMPILE_COMMANDS_JSON = os.path.join('proj2', 'compile_commands.json') ERR_A = ('a/a.c:1:7: error: Division by zero. [zerodiv]\n' + 'x = 3 / 0;\n' + ' ^\n') ERR_B = ('b/b.c:1:7: error: Division by zero. [zerodiv]\n' + 'x = 3 / 0;\n' + ' ^\n') def create_compile_commands(): prjpath = os.path.join(os.getcwd(), 'proj2') j = [{'directory': os.path.join(prjpath,'a'), 'command': 'gcc -c a.c', 'file': 'a.c'}, {'directory': os.path.join(prjpath,'b'), 'command': 'gcc -c b.c', 'file': 'b.c'}] f = open(COMPILE_COMMANDS_JSON, 'wt') f.write(json.dumps(j)) # Run Cppcheck from project path def cppcheck_local(args): cwd = os.getcwd() os.chdir('proj2') ret, stdout, stderr = cppcheck(args) os.chdir(cwd) return (ret, stdout, stderr) def test_local_path(): create_compile_commands() ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json']) cwd = os.getcwd() file1 = os.path.join(cwd, 'proj2', 'a', 'a.c') file2 = os.path.join(cwd, 'proj2', 'b', 'b.c') assert ret == 0 assert stdout.find('Checking %s ...' % (file1)) >= 0 assert stdout.find('Checking %s ...' % (file2)) >= 0 def test_local_path_force(): create_compile_commands() ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json', '--force']) cwd = os.getcwd() file1 = os.path.join(cwd, 'proj2', 'a', 'a.c') file2 = os.path.join(cwd, 'proj2', 'b', 'b.c') assert ret == 0 assert stdout.find('AAA') >= 0 def test_local_path_maxconfigs(): create_compile_commands() ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json', '--max-configs=2']) cwd = os.getcwd() file1 = os.path.join(cwd, 'proj2', 'a', 'a.c') file2 = os.path.join(cwd, 'proj2', 'b', 'b.c') assert ret == 0 assert stdout.find('AAA') >= 0 def test_relative_path(): create_compile_commands() ret, stdout, stderr = cppcheck(['--project=' + COMPILE_COMMANDS_JSON]) cwd = os.getcwd() file1 = os.path.join(cwd, 'proj2', 'a', 'a.c') file2 = os.path.join(cwd, 'proj2', 'b', 'b.c') assert ret == 0 assert stdout.find('Checking %s ...' % (file1)) >= 0 assert stdout.find('Checking %s ...' % (file2)) >= 0 def test_absolute_path(): create_compile_commands() cwd = os.getcwd() ret, stdout, stderr = cppcheck(['--project=' + os.path.join(cwd,COMPILE_COMMANDS_JSON)]) file1 = os.path.join(cwd, 'proj2', 'a', 'a.c') file2 = os.path.join(cwd, 'proj2', 'b', 'b.c') assert ret == 0 assert stdout.find('Checking %s ...' % (file1)) >= 0 assert stdout.find('Checking %s ...' % (file2)) >= 0 def test_gui_project_loads_compile_commands_1(): create_compile_commands() ret, stdout, stderr = cppcheck(['--project=proj2/proj2.cppcheck']) cwd = os.getcwd() file1 = os.path.join(cwd, 'proj2', 'a', 'a.c') file2 = os.path.join(cwd, 'proj2', 'b', 'b.c') assert ret == 0 assert stdout.find('Checking %s ...' % (file1)) >= 0 assert stdout.find('Checking %s ...' % (file2)) >= 0 def test_gui_project_loads_compile_commands_2(): create_compile_commands() exclude_path_1 = os.path.join(os.getcwd(), 'proj2', 'b').replace('\\', '/') create_gui_project_file('proj2/test.cppcheck', import_project='compile_commands.json', exclude_paths=[exclude_path_1]) ret, stdout, stderr = cppcheck(['--project=proj2/test.cppcheck']) cwd = os.getcwd() file1 = os.path.join(cwd, 'proj2', 'a', 'a.c') file2 = os.path.join(cwd, 'proj2', 'b', 'b.c') # Excluded by test.cppcheck assert ret == 0 assert stdout.find('Checking %s ...' % (file1)) >= 0 assert stdout.find('Checking %s ...' % (file2)) < 0 def test_gui_project_loads_relative_vs_solution(): create_gui_project_file('test.cppcheck', import_project='proj2/proj2.sln') ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) file1 = os.path.join('proj2', 'a', 'a.c') file2 = os.path.join('proj2', 'b', 'b.c') assert ret == 0 assert stdout.find('Checking %s Debug|Win32...' % (file1)) >= 0 assert stdout.find('Checking %s Debug|x64...' % (file1)) >= 0 assert stdout.find('Checking %s Release|Win32...' % (file1)) >= 0 assert stdout.find('Checking %s Release|x64...' % (file1)) >= 0 assert stdout.find('Checking %s Debug|Win32...' % (file2)) >= 0 assert stdout.find('Checking %s Debug|x64...' % (file2)) >= 0 assert stdout.find('Checking %s Release|Win32...' % (file2)) >= 0 assert stdout.find('Checking %s Release|x64...' % (file2)) >= 0 def test_gui_project_loads_absolute_vs_solution(): create_gui_project_file('test.cppcheck', import_project=os.path.join(os.getcwd(),'proj2', 'proj2.sln').replace('\\', '/')) ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) file1 = os.path.join(os.getcwd(), 'proj2', 'a', 'a.c') file2 = os.path.join(os.getcwd(), 'proj2', 'b', 'b.c') print(stdout) assert ret == 0 assert stdout.find('Checking %s Debug|Win32...' % (file1)) >= 0 assert stdout.find('Checking %s Debug|x64...' % (file1)) >= 0 assert stdout.find('Checking %s Release|Win32...' % (file1)) >= 0 assert stdout.find('Checking %s Release|x64...' % (file1)) >= 0 assert stdout.find('Checking %s Debug|Win32...' % (file2)) >= 0 assert stdout.find('Checking %s Debug|x64...' % (file2)) >= 0 assert stdout.find('Checking %s Release|Win32...' % (file2)) >= 0 assert stdout.find('Checking %s Release|x64...' % (file2)) >= 0 def test_gui_project_loads_relative_vs_solution(): create_gui_project_file('test.cppcheck', root_path='proj2', import_project='proj2/proj2.sln') ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) assert stderr == ERR_A + ERR_B def test_gui_project_loads_relative_vs_solution(): create_gui_project_file('test.cppcheck', root_path='proj2', import_project='proj2/proj2.sln', exclude_paths=['b']) ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) assert stderr == ERR_A def test_gui_project_loads_absolute_vs_solution(): create_gui_project_file('test.cppcheck', root_path=os.path.join(os.getcwd(), 'proj2').replace('\\', '/'), import_project=os.path.join(os.getcwd(), 'proj2', 'proj2.sln').replace('\\', '/')) ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) assert stderr == ERR_A + ERR_B cppcheck-1.90/test/cli/test-suppress-syntaxError.py000066400000000000000000000007441357737443600225520ustar00rootroot00000000000000 # python -m pytest test-suppress-syntaxError.py import os import re from testutils import cppcheck def test_j2(): ret, stdout, stderr = cppcheck(['--error-exitcode=1', '-j2', '-q', 'proj-suppress-syntaxError']) assert ret == 1 assert len(stderr) > 0 def test_j2_suppress(): ret, stdout, stderr = cppcheck(['--error-exitcode=1', '--suppress=*:proj-suppress-syntaxError/*', '-j2', '-q', 'proj-suppress-syntaxError']) assert ret == 0 assert len(stderr) == 0 cppcheck-1.90/test/cli/testutils.py000066400000000000000000000043421357737443600174110ustar00rootroot00000000000000 import logging import os import subprocess # Create Cppcheck project file def create_gui_project_file(project_file, root_path=None, import_project=None, paths=None, exclude_paths=None, suppressions=None, addon=None): cppcheck_xml = ('\n' '\n') if root_path: cppcheck_xml += ' \n' if import_project: cppcheck_xml += ' ' + import_project + '\n' if paths: cppcheck_xml += ' \n' for path in paths: cppcheck_xml += ' \n' cppcheck_xml += ' \n' if exclude_paths: cppcheck_xml += ' \n' for path in exclude_paths: cppcheck_xml += ' \n' cppcheck_xml += ' \n' if suppressions: cppcheck_xml += ' \n' for suppression in suppressions: cppcheck_xml += ' \n' cppcheck_xml += ' \n' if addon: cppcheck_xml += ' \n' cppcheck_xml += ' %s\n' % (addon) cppcheck_xml += ' \n' cppcheck_xml += '\n' f = open(project_file, 'wt') f.write(cppcheck_xml) f.close() # Run Cppcheck with args def cppcheck(args): exe = None if os.path.isfile('../../cppcheck.exe'): exe = '../../cppcheck.exe' elif os.path.isfile('../../../cppcheck.exe'): exe = '../../../cppcheck.exe' elif os.path.isfile('../../cppcheck'): exe = '../../cppcheck' else: exe = '../../../cppcheck' logging.info(exe + ' ' + ' '.join(args)) p = subprocess.Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) comm = p.communicate() stdout = comm[0].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') stderr = comm[1].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') return p.returncode, stdout, stderr cppcheck-1.90/test/options.cpp000066400000000000000000000030601357737443600164230ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2019 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "options.h" #include options::options(int argc, const char* const argv[]) :mWhichTests(argv + 1, argv + argc) ,mQuiet(mWhichTests.count("-q") != 0) ,mHelp(mWhichTests.count("-h") != 0 || mWhichTests.count("--help")) { for (std::set::const_iterator it = mWhichTests.begin(); it != mWhichTests.end();) { if (!(*it).empty() && (((*it)[0] == '-') || ((*it).find("::") != std::string::npos && mWhichTests.count((*it).substr(0, (*it).find("::")))))) it = mWhichTests.erase(it); else ++it; } if (mWhichTests.empty()) { mWhichTests.insert(""); } } bool options::quiet() const { return mQuiet; } bool options::help() const { return mHelp; } const std::set& options::which_test() const { return mWhichTests; } cppcheck-1.90/test/options.h000066400000000000000000000030651357737443600160750ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2019 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef OPTIONS_H #define OPTIONS_H #include #include /** * @brief Class to parse command-line parameters for ./testrunner . * Has getters for available switches and parameters. * See test/testoptions.cpp for sample usage. */ class options { public: /** Call from main() to populate object */ options(int argc, const char* const argv[]); /** Don't print the name of each method being tested. */ bool quiet() const; /** Print help. */ bool help() const; /** Which test should be run. Empty string means 'all tests' */ const std::set& which_test() const; private: options(); options(const options& non_copy); const options& operator =(const options& non_assign); private: std::set mWhichTests; const bool mQuiet; const bool mHelp; }; #endif cppcheck-1.90/test/redirect.h000066400000000000000000000053671357737443600162120ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2017 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef REDIRECT_H #define REDIRECT_H #include #include #include extern std::ostringstream errout; extern std::ostringstream output; /** * @brief Utility class for capturing cout and cerr to ostringstream buffers * for later use. Uses RAII to stop redirection when the object goes out of * scope. */ class RedirectOutputError { public: /** Set up redirection, flushing anything in the pipes. */ RedirectOutputError() { // flush all old output std::cout.flush(); std::cerr.flush(); _oldCout = std::cout.rdbuf(); // back up cout's streambuf _oldCerr = std::cerr.rdbuf(); // back up cerr's streambuf std::cout.rdbuf(_out.rdbuf()); // assign streambuf to cout std::cerr.rdbuf(_err.rdbuf()); // assign streambuf to cerr } /** Revert cout and cerr behaviour */ ~RedirectOutputError() { std::cout.rdbuf(_oldCout); // restore cout's original streambuf std::cerr.rdbuf(_oldCerr); // restore cerrs's original streambuf errout << _err.str(); output << _out.str(); } /** Return what would be printed to cout. See also clearOutput() */ std::string getOutput() const { return _out.str(); } /** Normally called after getOutput() to prevent same text to be returned twice. */ void clearOutput() { _out.str(""); } /** Return what would be printed to cerr. See also clearErrout() */ std::string getErrout() const { return _err.str(); } /** Normally called after getErrout() to prevent same text to be returned twice. */ void clearErrout() { _err.str(""); } private: std::ostringstream _out; std::ostringstream _err; std::streambuf *_oldCout; std::streambuf *_oldCerr; }; #define REDIRECT RedirectOutputError redir; #define GET_REDIRECT_OUTPUT redir.getOutput() #define CLEAR_REDIRECT_OUTPUT redir.clearOutput() #define GET_REDIRECT_ERROUT redir.getErrout() #define CLEAR_REDIRECT_ERROUT redir.clearErrout() #endif cppcheck-1.90/test/synthetic/000077500000000000000000000000001357737443600162375ustar00rootroot00000000000000cppcheck-1.90/test/synthetic/Makefile000066400000000000000000000004631357737443600177020ustar00rootroot00000000000000ifndef CC CC=gcc endif all: controlflow.o data.o functions.o ub.o controlflow.o: controlflow.c $(CC) -c controlflow.c data.o: data.c $(CC) -c data.c functions.o: functions.c $(CC) -c functions.c ub.o: ub.c $(CC) -c ub.c clean: rm -rf controlflow.o data.o functions.o ub.o cppcheck-1.90/test/synthetic/controlflow.c000066400000000000000000000017611357737443600207600ustar00rootroot00000000000000 ////////////////////////////// // control flow analysis ////////////////////////////// int buf[2]; void in_if(int a) { if (a==100) buf[a] = 0; // BUG } void before_if(int a) { buf[a] = 0; // WARNING if (a==100) {} } void after_if(int a) { if (a==100) {} buf[a] = 0; // WARNING } void in_for(void) { int x; for (x = 0; x<100; x++) { buf[x] = 0; // BUG } } void after_for(void) { int x; for (x = 0; x<100; x++) {} buf[x] = 0; // BUG } void in_switch(int x) { switch (x) { case 100: buf[x] = 0; // BUG break; } } void before_switch(int x) { buf[x] = 0; // WARNING switch (x) { case 100: break; } } void after_switch(int x) { switch (x) { case 100: break; } buf[x] = 0; // WARNING } void in_while(void) { int x = 0; while (x<100) { buf[x] = 0; // BUG x++; } } void after_while(void) { int x = 0; while (x<100) x++; buf[x] = 0; // BUG } cppcheck-1.90/test/synthetic/data.c000066400000000000000000000017341357737443600173210ustar00rootroot00000000000000 int TestData[10]; int g; void global() { g = 1000; TestData[g] = 0; // BUG } int garr[10]; void global_array() { garr[3] = 1000; TestData[garr[3]] = 0; // BUG } int *gp; void global_pointer() { *gp = 1000; TestData[*gp] = 0; // BUG } void local() { int x; x = 1000; TestData[x] = 0; // BUG } void local_array() { int arr[10]; arr[3] = 1000; TestData[arr[3]] = 0; // BUG } void local_alias_1() { int x; int *p = &x; *p = 1000; TestData[*p] = 0; // BUG } void local_alias_2() { int x; int *p = &x; x = 1000; TestData[*p] = 0; // BUG } struct ABC { int a; int b[10]; int c; }; void struct_member_init() { struct ABC abc = {1000,{0},3}; TestData[abc.a] = 0; // BUG } void struct_member_assign(struct ABC *abc) { abc->a = 1000; TestData[abc->a] = 0; // BUG } void struct_arraymember(struct ABC *abc) { abc->b[3] = 1000; TestData[abc->b[3]] = 0; // BUG } cppcheck-1.90/test/synthetic/functions.c000066400000000000000000000005571357737443600204220ustar00rootroot00000000000000 int TestData[100]; void par_not_dependant(int par) { TestData[par] = 0; // BUG } void par_dependant(int x, int y) { if (x < 10) TestData[y] = 0; // BUG } void call(int x) { par_not_dependant(1000); par_dependant(0, 1000); } int getLargeIndex() { return 1000; } void return_value() { TestData[getLargeIndex()] = 0; // BUG } cppcheck-1.90/test/synthetic/report.py000077500000000000000000000033021357737443600201250ustar00rootroot00000000000000#!/usr/bin/env python import os import re def hasresult(filename, result): if not os.path.isfile(filename): return False for line in open(filename, 'rt'): if result in line: return True return False def parsefile(filename): ret = [] linenr = 0 functionName = None for line in open(filename, 'rt'): linenr = linenr + 1 res = re.match('^[a-z]+[ *]+([a-z0-9_]+)[(]', line) if res: functionName = res.group(1) if line.startswith('}'): functionName = '' elif 'BUG' in line or 'WARN' in line or filename == 'ub.c': spaces = ' ' * 100 s = filename + spaces s = s[:15] + str(linenr) + spaces s = s[:20] + functionName + spaces s = s[:50] if hasresult('cppcheck.txt', '[' + filename + ':' + str(linenr) + ']'): s = s + ' X' else: s = s + ' ' if hasresult('clang.txt', filename + ':' + str(linenr)): s = s + ' X' else: s = s + ' ' if hasresult('lint.txt', filename + ' ' + str(linenr)): s = s + ' X' else: s = s + ' ' if hasresult('cov.txt', filename + ':' + str(linenr)): s = s + ' X' else: s = s + ' ' ret.append(s) return ret bugs = [] bugs.extend(parsefile('controlflow.c')) bugs.extend(parsefile('data.c')) bugs.extend(parsefile('functions.c')) bugs.extend(parsefile('ub.c')) for bug in bugs: print(bug) cppcheck-1.90/test/synthetic/run-clang.sh000077500000000000000000000003641357737443600204670ustar00rootroot00000000000000~/llvm/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.security controlflow.c data.c functions.c 2>&1 /dev/null | grep warning ~/llvm/build/bin/clang -cc1 -analyze -analyzer-checker=alpha.security,core ub.c 2>&1 /dev/null | grep warning cppcheck-1.90/test/synthetic/run-lint.bat000077500000000000000000000005621357737443600205050ustar00rootroot00000000000000\lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 controlflow.c \lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 data.c \lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 functions.c \lint\lint-nt.exe -e526 -e529 -e550 -e552 -e714 -e744 -e765 -e830 -e831 -e843 -h1 ub.c cppcheck-1.90/test/synthetic/ub.c000066400000000000000000000016631357737443600170170ustar00rootroot00000000000000void alias() { int x; int *ip=&x; float *fp = (float *)ip; } int buffer_overflow() { int x[10]={0}; return x[100]; } int dead_pointer(int a) { int *p=&a; if (a) { int x=0; p = &x; } return *p; } int division_by_zero() { return 100 / 0; } int float_to_int() { double d=1E100; return (int)d; } void negative_size(int sz) { if (sz < 0) { int buf[sz]; } } int no_return() {} int null_pointer() { int *p = 0; return *p; } int *pointer_arithmetic() { static int buf[10]; return buf + 100; } unsigned char pointer_to_u8() { static int buf[10]; return (int*)buf; } int pointer_subtraction() { char a[10]; char b[10]; return b-a; } int pointer_comparison() { char a[10]; char b[10]; return b> 1; return intmax * 2; } void string_literal() { *((char *)"hello") = 0; } int uninit() { int x; return x + 2; } cppcheck-1.90/test/test.cxx000066400000000000000000000007271357737443600157360ustar00rootroot00000000000000/* This is testing data for the GUI. Used for testing GUI with various error styles reported by cppcheck. Not meant to be compiled. */ #include void unused() { int a = 15; } void f(char k) { delete k; } void possible_style() { std::list::iterator it; for (it = ab.begin(); it != ab.end(); it++) ; } int main() { char *b = new char[1]; char *a = new char[8]; if (a); b = gets(); f(a); possible_style(); } cppcheck-1.90/test/test64bit.cpp000066400000000000000000000165101357737443600165640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check64bit.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class Test64BitPortability : public TestFixture { public: Test64BitPortability() : TestFixture("Test64BitPortability") { } private: Settings settings; void run() OVERRIDE { settings.addEnabled("portability"); TEST_CASE(novardecl); TEST_CASE(functionpar); TEST_CASE(structmember); TEST_CASE(ptrcompare); TEST_CASE(ptrarithmetic); TEST_CASE(returnIssues); } void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check char variable usage.. Check64BitPortability check64BitPortability(&tokenizer, &settings, this); check64BitPortability.pointerassignment(); } void novardecl() { // if the variable declarations can't be seen then skip the warning check("void foo()\n" "{\n" " a = p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void functionpar() { check("int foo(int *p)\n" "{\n" " int a = p;\n" " return a + 4;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout.str()); check("int foo(int p[])\n" "{\n" " int a = p;\n" " return a + 4;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout.str()); check("int foo(int p[])\n" "{\n" " int *a = p;\n" " return a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout.str()); check("void foo(int x)\n" "{\n" " int *p = x;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning an integer to a pointer is not portable.\n", errout.str()); check("int f(const char *p) {\n" // #4659 " return 6 + p[2] * 256;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(int *p) {\n" // #6096 " bool a = p;\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void structmember() { check("struct Foo { int *p; };\n" "void f(struct Foo *foo) {\n" " int i = foo->p;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout.str()); } void ptrcompare() { // Ticket #2892 check("void foo(int *p) {\n" " int a = (p != NULL);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ptrarithmetic() { // #3073 check("void foo(int *p) {\n" " int x = 10;\n" " int *a = p + x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p) {\n" " int x = 10;\n" " int *a = x + p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p) {\n" " int x = 10;\n" " int *a = x * x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning an integer to a pointer is not portable.\n", errout.str()); check("void foo(int *start, int *end) {\n" " int len;\n" " int len = end + 10 - start;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnIssues() { check("void* foo(int i) {\n" " return i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an integer in a function with pointer return type is not portable.\n", errout.str()); check("void* foo(int* i) {\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void* foo() {\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(int i) {\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo {};\n" "\n" "int* dostuff(Foo foo) {\n" " return foo;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(char* c) {\n" " return c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout.str()); check("int foo(char* c) {\n" " return 1+c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout.str()); check("std::string foo(char* c) {\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(char *a, char *b) {\n" // #4486 " return a + 1 - b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct s {\n" // 4642 " int i;\n" "};\n" "int func(struct s *p) {\n" " return 1 + p->i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void __iomem *f(unsigned int port_no) {\n" " void __iomem *mmio = hpriv->mmio;\n" " return mmio + (port_no * 0x80);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7247: don't check return statements in nested functions.. check("int foo() {\n" " struct {\n" " const char * name() { return \"abc\"; }\n" " } table;\n" "}"); ASSERT_EQUALS("", errout.str()); // #7451: Lambdas check("const int* test(std::vector outputs, const std::string& text) {\n" " auto it = std::find_if(outputs.begin(), outputs.end(), \n" " [&](int ele) { return \"test\" == text; });\n" " return nullptr;\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(Test64BitPortability) cppcheck-1.90/test/testassert.cpp000066400000000000000000000170601357737443600171360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkassert.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestAssert : public TestFixture { public: TestAssert() : TestFixture("TestAssert") {} private: Settings settings; void check(const char code[], const char *filename = "test.cpp") { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); // Check.. CheckAssert checkAssert; checkAssert.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { settings.addEnabled("warning"); TEST_CASE(assignmentInAssert); TEST_CASE(functionCallInAssert); TEST_CASE(memberFunctionCallInAssert); TEST_CASE(safeFunctionCallInAssert); } void safeFunctionCallInAssert() { check( "int a;\n" "bool b = false;\n" "int foo() {\n" " if (b) { a = 1+2 };\n" " return a;\n" "}\n" "assert(foo() == 3); \n" ); ASSERT_EQUALS("", errout.str()); check( "int foo(int a) {\n" " int b=a+1;\n" " return b;\n" "}\n" "assert(foo(1) == 2); \n" ); ASSERT_EQUALS("", errout.str()); } void functionCallInAssert() { check( "int a;\n" "int foo() {\n" " a = 1+2;\n" " return a;\n" "}\n" "assert(foo() == 3); \n" ); ASSERT_EQUALS("[test.cpp:6]: (warning) Assert statement calls a function which may have desired side effects: 'foo'.\n", errout.str()); // Ticket #4937 "false positive: Assert calls a function which may have desired side effects" check("struct SquarePack {\n" " static bool isRank1Or8( Square sq ) {\n" " sq &= 0x38;\n" " return sq == 0 || sq == 0x38;\n" " }\n" "};\n" "void foo() {\n" " assert( !SquarePack::isRank1Or8(push2) );\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct SquarePack {\n" " static bool isRank1Or8( Square &sq ) {\n" " sq &= 0x38;\n" " return sq == 0 || sq == 0x38;\n" " }\n" "};\n" "void foo() {\n" " assert( !SquarePack::isRank1Or8(push2) );\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (warning) Assert statement calls a function which may have desired side effects: 'isRank1Or8'.\n", errout.str()); check("struct SquarePack {\n" " static bool isRank1Or8( Square *sq ) {\n" " *sq &= 0x38;\n" " return *sq == 0 || *sq == 0x38;\n" " }\n" "};\n" "void foo() {\n" " assert( !SquarePack::isRank1Or8(push2) );\n" "}\n"); ASSERT_EQUALS("[test.cpp:8]: (warning) Assert statement calls a function which may have desired side effects: 'isRank1Or8'.\n", errout.str()); check("struct SquarePack {\n" " static bool isRank1Or8( Square *sq ) {\n" " sq &= 0x38;\n" " return sq == 0 || sq == 0x38;\n" " }\n" "};\n" "void foo() {\n" " assert( !SquarePack::isRank1Or8(push2) );\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void memberFunctionCallInAssert() { check("struct SquarePack {\n" " void Foo();\n" "};\n" "void foo(SquarePack s) {\n" " assert( s.Foo(); );\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Assert statement calls a function which may have desired side effects: 'Foo'.\n", errout.str()); check("struct SquarePack {\n" " void Foo() const;\n" "};\n" "void foo(SquarePack* s) {\n" " assert( s->Foo(); );\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct SquarePack {\n" " static void Foo();\n" "};\n" "void foo(SquarePack* s) {\n" " assert( s->Foo(); );\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct SquarePack {\n" "};\n" "void foo(SquarePack* s) {\n" " assert( s->Foo(); );\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignmentInAssert() { check("void f() {\n" " int a; a = 0;\n" " assert(a = 2);\n" " return a;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f(int a) {\n" " assert(a == 2);\n" " return a;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int a, int b) {\n" " assert(a == 2 && b = 1);\n" " return a;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Assert statement modifies 'b'.\n", errout.str()); check("void f() {\n" " int a; a = 0;\n" " assert(a += 2);\n" " return a;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " int a; a = 0;\n" " assert(a *= 2);\n" " return a;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " int a; a = 0;\n" " assert(a -= 2);\n" " return a;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " int a = 0;\n" " assert(a--);\n" " return a;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout.str()); check("void f() {\n" " assert(std::all_of(first, last, []() {\n" " auto tmp = x.someValue();\n" " auto const expected = someOtherValue;\n" " return tmp == expected;\n" " }));\n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestAssert) cppcheck-1.90/test/testastutils.cpp000066400000000000000000000254041357737443600175060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "astutils.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" class TestAstUtils : public TestFixture { public: TestAstUtils() : TestFixture("TestAstUtils") { } private: void run() OVERRIDE { TEST_CASE(findLambdaEndToken); TEST_CASE(findLambdaStartToken); TEST_CASE(isReturnScope); TEST_CASE(isVariableChanged); TEST_CASE(isVariableChangedByFunctionCall); TEST_CASE(nextAfterAstRightmostLeaf); } bool findLambdaEndToken(const char code[]) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token * const tokEnd = ::findLambdaEndToken(tokenizer.tokens()); return tokEnd && tokEnd->next() == nullptr; } void findLambdaEndToken() { ASSERT(nullptr == ::findLambdaEndToken(nullptr)); ASSERT_EQUALS(false, findLambdaEndToken("void f() { }")); ASSERT_EQUALS(true, findLambdaEndToken("[]{ }")); ASSERT_EQUALS(true, findLambdaEndToken("[]{ return 0; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](){ }")); ASSERT_EQUALS(true, findLambdaEndToken("[&](){ }")); ASSERT_EQUALS(true, findLambdaEndToken("[&, i](){ }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) { return -1; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) { return a + b; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) mutable { return a + b; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) constexpr { return a + b; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) mutable -> int { return -1; }")); ASSERT_EQUALS(false, findLambdaEndToken("[](void) foo -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> int* { return x; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const * int { return x; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) mutable -> const * int { return x; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const ** int { return x; }")); ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const * const* int { return x; }")); } bool findLambdaStartToken(const char code[]) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token * const tokStart = ::findLambdaStartToken(tokenizer.list.back()); return tokStart && tokStart == tokenizer.list.front(); } void findLambdaStartToken() { ASSERT(nullptr == ::findLambdaStartToken(nullptr)); ASSERT_EQUALS(false, findLambdaStartToken("void f() { }")); ASSERT_EQUALS(true, findLambdaStartToken("[]{ }")); ASSERT_EQUALS(true, findLambdaStartToken("[]{ return 0; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](){ }")); ASSERT_EQUALS(true, findLambdaStartToken("[&](){ }")); ASSERT_EQUALS(true, findLambdaStartToken("[&, i](){ }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) { return -1; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) { return a + b; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) mutable { return a + b; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) constexpr { return a + b; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) mutable -> int { return -1; }")); ASSERT_EQUALS(false, findLambdaStartToken("[](void) foo -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> int { return -1; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> int* { return x; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const * int { return x; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) mutable -> const * int { return x; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const ** int { return x; }")); ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const * const* int { return x; }")); } bool isReturnScope(const char code[], int offset) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token * const tok = (offset < 0) ? tokenizer.list.back()->tokAt(1+offset) : tokenizer.tokens()->tokAt(offset); return ::isReturnScope(tok); } void isReturnScope() { ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { return; } }", -2)); ASSERT_EQUALS(true, isReturnScope("int f() { if (a) { return {}; } }", -2)); // #8891 ASSERT_EQUALS(true, isReturnScope("std::string f() { if (a) { return std::string{}; } }", -2)); // #8891 ASSERT_EQUALS(true, isReturnScope("std::string f() { if (a) { return std::string{\"\"}; } }", -2)); // #8891 ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { return (ab){0}; } }", -2)); // #7103 ASSERT_EQUALS(false, isReturnScope("void f() { if (a) { return (ab){0}; } }", -4)); // #7103 ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { {throw new string(x);}; } }", -4)); // #7144 ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { {throw new string(x);}; } }", -2)); // #7144 ASSERT_EQUALS(false, isReturnScope("void f() { [=]() { return data; }; }", -1)); ASSERT_EQUALS(true, isReturnScope("auto f() { return [=]() { return data; }; }", -1)); ASSERT_EQUALS(true, isReturnScope("auto f() { return [=]() { return data; }(); }", -1)); ASSERT_EQUALS(false, isReturnScope("auto f() { [=]() { return data; }(); }", -1)); ASSERT_EQUALS(true, isReturnScope("void negativeTokenOffset() { return; }", -1)); ASSERT_EQUALS(false, isReturnScope("void zeroTokenOffset() { return; }", 0)); ASSERT_EQUALS(true, isReturnScope("void positiveTokenOffset() { return; }", 7)); } bool isVariableChanged(const char code[], const char startPattern[], const char endPattern[]) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token * const tok1 = Token::findsimplematch(tokenizer.tokens(), startPattern); const Token * const tok2 = Token::findsimplematch(tokenizer.tokens(), endPattern); return ::isVariableChanged(tok1,tok2,1,false,&settings,true); } void isVariableChanged() { // #8211 - no lhs for >> , do not crash isVariableChanged("void f() {\n" " int b;\n" " if (b) { (int)((INTOF(8))result >> b); }\n" "}", "if", "}"); // #9235 ASSERT_EQUALS(true, isVariableChanged("void f() {\n" " int &a = a;\n" "}\n", "= a", "}")); } bool isVariableChangedByFunctionCall(const char code[], const char pattern[], bool *inconclusive) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token * const argtok = Token::findmatch(tokenizer.tokens(), pattern); return ::isVariableChangedByFunctionCall(argtok, 0, &settings, inconclusive); } void isVariableChangedByFunctionCall() { const char *code; bool inconclusive; // #8271 - template method code = "void f(int x) {\n" " a(x);\n" "}"; inconclusive = false; ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "x ) ;", &inconclusive)); ASSERT_EQUALS(true, inconclusive); } bool nextAfterAstRightmostLeaf(const char code[], const char parentPattern[], const char rightPattern[]) { Settings settings; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const Token * tok = Token::findsimplematch(tokenizer.tokens(), parentPattern); return Token::simpleMatch(::nextAfterAstRightmostLeaf(tok), rightPattern); } void nextAfterAstRightmostLeaf() { ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("void f(int a, int b) { int x = a + b; }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a); }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b]; }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(g(a)[b]); }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(g(a)[b] + a); }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b + 1]; }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("void f() { int a; int b; int x = [](int a){}; }", "=", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = a + b; }", "+", "; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b + 1]; }", "+", "] ; }")); ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a + 1)[b]; }", "+", ") [")); } }; REGISTER_TEST(TestAstUtils) cppcheck-1.90/test/testautovariables.cpp000066400000000000000000002712431357737443600205030ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkautovariables.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestAutoVariables : public TestFixture { public: TestAutoVariables() : TestFixture("TestAutoVariables") { } private: Settings settings; void check(const char code[], bool inconclusive = false, const char* filename = "test.cpp") { // Clear the error buffer.. errout.str(""); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); CheckAutoVariables checkAutoVariables; checkAutoVariables.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { settings.addEnabled("warning"); settings.addEnabled("style"); LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "qt.cfg"); TEST_CASE(testautovar1); TEST_CASE(testautovar2); TEST_CASE(testautovar3); // ticket #2925 TEST_CASE(testautovar4); // ticket #2928 TEST_CASE(testautovar5); // ticket #2926 TEST_CASE(testautovar6); // ticket #2931 TEST_CASE(testautovar7); // ticket #3066 TEST_CASE(testautovar8); TEST_CASE(testautovar9); TEST_CASE(testautovar10); // ticket #2930 - void f(char *p) { p = '\0'; } TEST_CASE(testautovar11); // ticket #4641 - fp, assign local struct member address to function parameter TEST_CASE(testautovar12); // ticket #5024 - crash TEST_CASE(testautovar13); // ticket #5537 - crash TEST_CASE(testautovar14); // ticket #4776 - assignment of function parameter, goto TEST_CASE(testautovar15); // ticket #6538 TEST_CASE(testautovar16); // ticket #8114 TEST_CASE(testautovar_array1); TEST_CASE(testautovar_array2); TEST_CASE(testautovar_normal); // "normal" token list that does not remove casts etc TEST_CASE(testautovar_ptrptr); // ticket #6956 TEST_CASE(testautovar_return1); TEST_CASE(testautovar_return2); TEST_CASE(testautovar_return3); TEST_CASE(testautovar_return4); TEST_CASE(testautovar_extern); TEST_CASE(testinvaliddealloc); TEST_CASE(testinvaliddealloc_C); TEST_CASE(testassign1); // Ticket #1819 TEST_CASE(testassign2); // Ticket #2765 TEST_CASE(assignAddressOfLocalArrayToGlobalPointer); TEST_CASE(assignAddressOfLocalVariableToGlobalPointer); TEST_CASE(assignAddressOfLocalVariableToMemberVariable); TEST_CASE(returnLocalVariable1); TEST_CASE(returnLocalVariable2); TEST_CASE(returnLocalVariable3); // &x[0] TEST_CASE(returnLocalVariable4); // x+y TEST_CASE(returnLocalVariable5); // cast TEST_CASE(returnLocalVariable6); // valueflow // return reference.. TEST_CASE(returnReference1); TEST_CASE(returnReference2); TEST_CASE(returnReference3); TEST_CASE(returnReference4); TEST_CASE(returnReference5); TEST_CASE(returnReference6); TEST_CASE(returnReference7); TEST_CASE(returnReference8); TEST_CASE(returnReference9); TEST_CASE(returnReference10); TEST_CASE(returnReference11); TEST_CASE(returnReference12); TEST_CASE(returnReference13); TEST_CASE(returnReference14); TEST_CASE(returnReference15); // #9432 TEST_CASE(returnReference16); // #9433 TEST_CASE(returnReference16); // #9433 TEST_CASE(returnReference17); // #9461 TEST_CASE(returnReference18); // #9482 TEST_CASE(returnReferenceFunction); TEST_CASE(returnReferenceContainer); TEST_CASE(returnReferenceLiteral); TEST_CASE(returnReferenceCalculation); TEST_CASE(returnReferenceLambda); TEST_CASE(returnReferenceInnerScope); TEST_CASE(returnReferenceRecursive); TEST_CASE(extendedLifetime); TEST_CASE(danglingReference); // global namespace TEST_CASE(testglobalnamespace); TEST_CASE(returnParameterAddress); TEST_CASE(testconstructor); // ticket #5478 - crash TEST_CASE(variableIsUsedInScope); // ticket #5599 crash in variableIsUsedInScope() TEST_CASE(danglingLifetimeLambda); TEST_CASE(danglingLifetimeContainer); TEST_CASE(danglingLifetime); TEST_CASE(danglingLifetimeFunction); TEST_CASE(danglingLifetimeAggegrateConstructor); TEST_CASE(danglingLifetimeInitList); TEST_CASE(danglingLifetimeImplicitConversion); TEST_CASE(danglingTemporaryLifetime); TEST_CASE(invalidLifetime); TEST_CASE(deadPointer); } void testautovar1() { check("void func1(int **res)\n" "{\n" " int num = 2;\n" " *res = #\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("void func1(int **res)\n" "{\n" " int num = 2;\n" " res = #\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("void func1(int **res)\n" "{\n" " int num = 2;\n" " foo.res = #\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar2() { check("class Fred {\n" " void func1(int **res);\n" "}\n" "void Fred::func1(int **res)\n" "{\n" " int num = 2;\n" " *res = #\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("class Fred {\n" " void func1(int **res);\n" "}\n" "void Fred::func1(int **res)\n" "{\n" " int num = 2;\n" " res = #\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("class Fred {\n" " void func1(int **res);\n" "}\n" "void Fred::func1(int **res)\n" "{\n" " int num = 2;\n" " foo.res = #\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar3() { // ticket #2925 check("void foo(int **p)\n" "{\n" " int x[100];\n" " *p = x;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar4() { // ticket #2928 check("void foo(int **p)\n" "{\n" " static int x[100];\n" " *p = x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar5() { // ticket #2926 check("void foo(struct AB *ab)\n" "{\n" " char a;\n" " ab->a = &a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar6() { // ticket #2931 check("void foo(struct X *x)\n" "{\n" " char a[10];\n" " x->str = a;\n" "}", false); ASSERT_EQUALS("", errout.str()); check("void foo(struct X *x)\n" "{\n" " char a[10];\n" " x->str = a;\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar7() { // ticket #3066 check("struct txt_scrollpane_s * TXT_NewScrollPane(struct txt_widget_s * target)\n" "{\n" " struct txt_scrollpane_s * scrollpane;\n" " target->parent = &scrollpane->widget;\n" " return scrollpane;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void testautovar8() { check("void foo(int*& p) {\n" " int i = 0;\n" " p = &i;\n" "}", false); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("void foo(std::string& s) {\n" " s = foo;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void testautovar9() { check("struct FN {int i;};\n" "struct FP {FN* f};\n" "void foo(int*& p, FN* p_fp) {\n" " FN fn;\n" " FP fp;\n" " p = &fn.i;\n" " p = &p_fp->i;\n" " p = &fp.f->i;\n" "}", false); ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar10() { // #2930 - assignment of function parameter check("void foo(char* p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("void foo(int b) {\n" " b = foo(b);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout.str()); check("void foo(int b) {\n" " b += 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout.str()); check("void foo(std::string s) {\n" " s = foo(b);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout.str()); check("void foo(char* p) {\n" // don't warn for self assignment, there is another warning for this " p = p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* p) {\n" " if (!p) p = buf;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* p) {\n" " if (!p) p = buf;\n" " do_something(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* p) {\n" " while (!p) p = buf;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* p) {\n" " p = 0;\n" " asm(\"somecmd\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Foo* p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("class Foo {};\n" "void foo(Foo p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Assignment of function parameter has no effect outside the function.\n", errout.str()); check("void foo(Foo p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& p) {\n" " p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("double foo(double d) {\n" // #5005 " int i = d;\n" " d = i;\n" " return d;" "}",false); ASSERT_EQUALS("", errout.str()); check("void foo(int* ptr) {\n" // #4793 " ptr++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("void foo(int* ptr) {\n" // #3177 " --ptr;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout.str()); check("void foo(struct S* const x) {\n" // #7839 " ++x->n;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar11() { // #4641 - fp, assign local struct member address to function parameter check("struct A {\n" " char (*data)[10];\n" "};\n" "void foo(char** p) {\n" " struct A a = bar();\n" " *p = &(*a.data)[0];\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " char data[10];\n" "};\n" "void foo(char** p) {\n" " struct A a = bar();\n" " *p = &a.data[0];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); check("void f(char **out) {\n" " struct S *p = glob;\n" " *out = &p->data;\n" "}"); ASSERT_EQUALS("", errout.str()); // #4998 check("void f(s8**out) {\n" " s8 *p;\n" // <- p is pointer => no error " *out = &p[1];\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(s8**out) {\n" " s8 p[10];\n" // <- p is array => error " *out = &p[1];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar12() { // Ticket #5024, #5050 - Crash on invalid input ASSERT_THROW(check("void f(int* a) { a = }"), InternalError); check("struct custom_type { custom_type(int) {} };\n" "void func(int) {}\n" "int var;\n" "void init() { func(var); }\n" "UNKNOWN_MACRO_EXPANDING_TO_SIGNATURE { custom_type a(var); }"); } void testautovar13() { // Ticket #5537 check("class FileManager {\n" " FileManager() : UniqueRealDirs(*new UniqueDirContainer())\n" " {}\n" " ~FileManager() {\n" " delete &UniqueRealDirs;\n" " }\n" "};\n"); } void testautovar14() { // Ticket #4776 check("void f(int x) {\n" "label:" " if (x>0) {\n" " x = x >> 1;\n" " goto label;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar15() { // Ticket #6538 check("static const float4 darkOutline(0.05f, 0.05f, 0.05f, 0.95f);\n" "static const float darkLuminosity = 0.05 +\n" " 0.0722f * math::powf(darkOutline[2], 2.2);\n" "const float4* ChooseOutlineColor(const float4& textColor) {\n" " const float lumdiff = something;\n" " if (lumdiff > 5.0f)\n" " return &darkOutline;\n" " return 0;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void testautovar16() { // Ticket #8114 check("void f(const void* ptr, bool* result) {\n" " int dummy;\n" " *result = (&dummy < ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar_array1() { check("void func1(int* arr[2])\n" "{\n" " int num=2;" " arr[0]=#\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar_array2() { check("class Fred {\n" " void func1(int* arr[2]);\n" "}\n" "void Fred::func1(int* arr[2])\n" "{\n" " int num=2;" " arr[0]=#\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar_normal() { check("void f(XmDestinationCallbackStruct *ds)\n" "{\n" " XPoint DropPoint;\n" " ds->location_data = (XtPointer *)&DropPoint;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar_ptrptr() { // #6596 check("void remove_duplicate_matches (char **matches) {\n" " char dead_slot;\n" " matches[0] = (char *)&dead_slot;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str()); } void testautovar_return1() { check("int* func1()\n" "{\n" " int num=2;" " return #" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:3]: (error) Returning pointer to local variable 'num' that will be invalid when returning.\n", errout.str()); } void testautovar_return2() { check("class Fred {\n" " int* func1();\n" "}\n" "int* Fred::func1()\n" "{\n" " int num=2;" " return #" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6]: (error) Returning pointer to local variable 'num' that will be invalid when returning.\n", errout.str()); } void testautovar_return3() { // #2975 - FP check("void** f()\n" "{\n" " void *&value = tls[id];" " return &value;" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar_return4() { // #8058 - FP ignore return in lambda check("void foo() {\n" " int cond2;\n" " dostuff([&cond2]() { return &cond2; });\n" "}"); ASSERT_EQUALS("", errout.str()); } void testautovar_extern() { check("struct foo *f()\n" "{\n" " extern struct foo f;\n" " return &f;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testinvaliddealloc() { check("void func1() {\n" " char tmp1[256];\n" " free(tmp1);\n" " char tmp2[256];\n" " delete tmp2;\n" " char tmp3[256];\n" " delete tmp3;\n" " char tmp4[256];\n" " delete[] (tmp4);\n" " char tmp5[256];\n" " delete[] tmp5;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:5]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:7]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:9]: (error) Deallocation of an auto-variable results in undefined behaviour.\n" "[test.cpp:11]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("void func1() {\n" " char* tmp1[256];\n" " init(tmp1);\n" " delete tmp1[34];\n" "}"); ASSERT_EQUALS("", errout.str()); check("void func1() {\n" " static char tmp1[256];\n" " char *p = tmp1;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Deallocation of an static variable (tmp1) results in undefined behaviour.\n", errout.str()); check("char tmp1[256];\n" "void func1() {\n" " char *p; if (x) p = tmp1;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Deallocation of an global variable (tmp1) results in undefined behaviour.\n", errout.str()); check("void f()\n" "{\n" " char psz_title[10];\n" " {\n" " char *psz_title = 0;\n" " abc(0, psz_title);\n" " free(psz_title);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #2298 new check: passing stack-address to free() check("int main() {\n" " int *p = malloc(4);\n" " free(&p);\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("int main() {\n" " int i;\n" " free(&i);\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); // #5732 check("int main() {\n" " long (*pKoeff)[256] = new long[9][256];\n" " delete[] pKoeff;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main() {\n" " long *pKoeff[256];\n" " delete[] pKoeff;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("int main() {\n" " long *pKoeff[256];\n" " free (pKoeff);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout.str()); check("void foo() {\n" " const intPtr& intref = Getter();\n" " delete intref;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " MyObj& obj = *new MyObj;\n" " delete &obj;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6506 check("struct F {\n" " void free(void*) {}\n" "};\n" "void foo() {\n" " char c1[1];\n" " F().free(c1);\n" " char *c2 = 0;\n" " F().free(&c2);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class foo {\n" " void free(void* );\n" " void someMethod() {\n" " char **dst_copy = NULL;\n" " free(&dst_copy);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // #6551 check("bool foo( ) {\n" " SwTxtFld * pTxtFld = GetFldTxtAttrAt();\n" " delete static_cast(&pTxtFld->GetAttr());\n" "}"); ASSERT_EQUALS("", errout.str()); // #8910 check("void f() {\n" " char stack[512];\n" " RGNDATA *data;\n" " if (data_size > sizeof (stack)) data = malloc (data_size);\n" " else data = (RGNDATA *)stack;\n" " if ((char *)data != stack) free (data);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #8923 check("void f(char **args1, char *args2[]) {\n" " free((char **)args1);\n" " free((char **)args2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testinvaliddealloc_C() { // #5691 check("void svn_repos_dir_delta2() {\n" " struct context c;\n" " SVN_ERR(delete(&c, root_baton, src_entry, pool));\n" "}\n", false, "test.c"); ASSERT_EQUALS("", errout.str()); } void testassign1() { // Ticket #1819 check("void f(EventPtr *eventP, ActionPtr **actionsP) {\n" " EventPtr event = *eventP;\n" " *actionsP = &event->actions;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testassign2() { // Ticket #2765 check("static void function(unsigned long **datap) {\n" " struct my_s *mr = global_structure_pointer;\n" " *datap = &mr->value;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignAddressOfLocalArrayToGlobalPointer() { check("int *p;\n" "void f() {\n" " int x[10];\n" " p = x;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'p' will use pointer to local variable 'x'.\n", errout.str()); check("int *p;\n" "void f() {\n" " int x[10];\n" " p = x;\n" " p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignAddressOfLocalVariableToGlobalPointer() { check("int *p;\n" "void f() {\n" " int x;\n" " p = &x;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'p' will use pointer to local variable 'x'.\n", errout.str()); check("int *p;\n" "void f() {\n" " int x;\n" " p = &x;\n" " p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assignAddressOfLocalVariableToMemberVariable() { check("struct A {\n" " void f() {\n" " int x;\n" " ptr = &x;\n" " }\n" " int *ptr;\n" "};\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'ptr' will use pointer to local variable 'x'.\n", errout.str()); check("struct A {\n" " void f() {\n" " int x;\n" " ptr = &x;\n" " ptr = 0;\n" " }\n" " int *ptr;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void returnLocalVariable1() { check("char *foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", errout.str()); check("char *foo()\n" // use ValueFlow "{\n" " char str[100] = {0};\n" " char *p = str;\n" " return p;\n" "}"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:5]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", errout.str()); check("class Fred {\n" " char *foo();\n" "};\n" "char *Fred::foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", errout.str()); check("char * format_reg(char *outbuffer_start) {\n" " return outbuffer_start;\n" "}\n" "void print_with_operands() {\n" " char temp[42];\n" " char *tp = temp;\n" " tp = format_reg(tp);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnLocalVariable2() { check("std::string foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred {\n" " std::string foo();\n" "};\n" "std::string Fred::foo()\n" "{\n" " char str[100] = {0};\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnLocalVariable3() { // &x[..] // #3030 check("char *foo() {\n" " char q[] = \"AAAAAAAAAAAA\";\n" " return &q[1];\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'q' that will be invalid when returning.\n", errout.str()); check("char *foo()\n" "{\n" " static char q[] = \"AAAAAAAAAAAA\";\n" " return &q[1];\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *foo()\n" "{\n" "char q[] = \"AAAAAAAAAAAA\";\n" "char *p;\n" "p = &q[1];\n" "return p;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3] -> [test.cpp:6]: (error) Returning pointer to local variable 'q' that will be invalid when returning.\n", errout.str()); } void returnLocalVariable4() { // x+y check("char *foo() {\n" " char x[10] = {0};\n" " return x+5;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("char *foo(int y) {\n" " char x[10] = {0};\n" " return (x+8)-y;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); } void returnLocalVariable5() { // cast check("char *foo() {\n" " int x[10] = {0};\n" " return (char *)x;\n" "}"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); } void returnLocalVariable6() { // valueflow check("int *foo() {\n" " int x = 123;\n" " int p = &x;\n" " return p;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); } void returnReference1() { check("int &foo()\n" "{\n" " int s = 0;\n" " int& x = s;\n" " return x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Reference to local variable returned.\n", errout.str()); check("std::string &foo()\n" "{\n" " std::string s;\n" " return s;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout.str()); check("std::vector &foo()\n" "{\n" " std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout.str()); check("std::vector &foo()\n" "{\n" " static std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::vector &foo()\n" "{\n" " thread_local std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string hello()\n" "{\n" " return \"hello\";\n" "}\n" "\n" "std::string &f()\n" "{\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Reference to temporary returned.\n", errout.str()); // make sure scope is used in function lookup check("class Fred {\n" " std::string hello() {\n" " return std::string();\n" " }\n" "};\n" "std::string &f() {\n" " return hello();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string hello() {\n" " return std::string();\n" "}\n" "\n" "std::string &f() {\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("std::string hello() {\n" " return \"foo\";\n" "}\n" "\n" "std::string &f() {\n" " return hello().substr(1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Foo;\n" "Foo hello() {\n" " return Foo();\n" "}\n" "\n" "Foo& f() {\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Reference to temporary returned.\n", errout.str()); // make sure function overloads are handled properly check("class Foo;\n" "Foo & hello(bool) {\n" " static Foo foo;\n" " return foo;\n" "}\n" "Foo hello() {\n" " return Foo();\n" "}\n" "\n" "Foo& f() {\n" " return hello(true);\n" "}"); ASSERT_EQUALS("", errout.str()); check("Foo hello() {\n" " return Foo();\n" "}\n" "\n" "Foo& f() {\n" // Unknown type - might be a reference " return hello();\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference2() { check("class Fred {\n" " std::string &foo();\n" "}\n" "std::string &Fred::foo()\n" "{\n" " std::string s;\n" " return s;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Reference to local variable returned.\n", errout.str()); check("class Fred {\n" " std::vector &foo();\n" "};\n" "std::vector &Fred::foo()\n" "{\n" " std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Reference to local variable returned.\n", errout.str()); check("class Fred {\n" " std::vector &foo();\n" "};\n" "std::vector &Fred::foo()\n" "{\n" " static std::vector v;\n" " return v;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred {\n" " std::string &f();\n" "};\n" "std::string hello()\n" "{\n" " return \"hello\";\n" "}\n" "std::string &Fred::f()\n" "{\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Reference to temporary returned.\n", errout.str()); check("class Fred {\n" " std::string hello();\n" " std::string &f();\n" "};\n" "std::string Fred::hello()\n" "{\n" " return \"hello\";\n" "}\n" "std::string &Fred::f()\n" "{\n" " return hello();\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Reference to temporary returned.\n", errout.str()); check("class Bar;\n" "Bar foo() {\n" " return something;\n" "}\n" "Bar& bar() {\n" " return foo();\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("std::map foo() {\n" " return something;\n" "}\n" "std::map& bar() {\n" " return foo();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Reference to temporary returned.\n", errout.str()); check("Bar foo() {\n" " return something;\n" "}\n" "Bar& bar() {\n" // Unknown type - might be a typedef to a reference type " return foo();\n" "}"); ASSERT_EQUALS("", errout.str()); // Don't crash with function in unknown scope (#4076) check("X& a::Bar() {}" "X& foo() {" " return Bar();" "}"); } void returnReference3() { check("double & f(double & rd) {\n" " double ret = getValue();\n" " rd = ret;\n" " return rd;\n" "}", false); ASSERT_EQUALS("", errout.str()); } // Returning reference to global variable void returnReference4() { check("double a;\n" "double & f() {\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference5() { check("struct A {\n" " int i;\n" "};\n" "struct B {\n" " A a;\n" "};\n" "struct C {\n" " B *b;\n" " const A& a() const {\n" " const B *pb = b;\n" " const A &ra = pb->a;\n" " return ra;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void returnReference6() { check("Fred & create() {\n" " Fred &fred(*new Fred);\n" " return fred;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference7() { // 3791 - false positive for overloaded function check("std::string a();\n" "std::string &a(int);\n" "std::string &b() {\n" " return a(12);\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string &a(int);\n" "std::string a();\n" "std::string &b() {\n" " return a(12);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReference8() { check("int& f(std::vector &v) {\n" " std::vector::iterator it = v.begin();\n" " int& value = *it;\n" " return value;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference9() { check("int& f(bool b, int& x, int& y) {\n" " return b ? x : y;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference10() { check("class A { int f() const; };\n" "int& g() {\n" " A a;\n" " return a.f();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Reference to temporary returned.\n", errout.str()); check("class A { int& f() const; };\n" "int& g() {\n" " A a;\n" " return a.f();\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference11() { check("class A { static int f(); };\n" "int& g() {\n" " return A::f();\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); check("class A { static int& f(); };\n" "int& g() {\n" " return A::f();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("namespace A { int& f(); }\n" "int& g() {\n" " return A::f();\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference12() { check("class A { static int& f(); };\n" "auto g() {\n" " return &A::f;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("class A { static int& f(); };\n" "auto g() {\n" " auto x = &A::f;\n" " return x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference13() { check("std::vector v;\n" "void* vp = &v;\n" "int& foo(size_t i) {\n" " return ((std::vector*)vp)->at(i);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::vector v;\n" "void* vp = &v;\n" "int& foo(size_t i) {\n" " return static_cast*>(vp)->at(i);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference14() { check("struct C { void* m; };\n" "struct A { void* &f(); };\n" "C* g() {\n" " static C c;\n" " return &c;\n" "}\n" "void* &A::f() {\n" " return g()->m;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference15() { check("template \n" "const int& f() {\n" " static int s;\n" " return s;\n" "}\n" "template \n" "const int& f(const T&) {\n" " return f();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("template \n" "int g();\n" "template \n" "const int& f(const T&) {\n" " return g();\n" "}\n"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void returnReference16() { check("int& f(std::tuple& x) {\n" " return std::get<0>(x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int& f(int x) {\n" " return std::get<0>(std::make_tuple(x));\n" "}\n"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void returnReference17() { check("auto g() -> int&;\n" "int& f() {\n" " return g();\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReference18() { check("template\n" "auto f(T& x) -> decltype(x);\n" "int& g(int* x) {\n" " return f(*x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReferenceFunction() { check("int& f(int& a) {\n" " return a;\n" "}\n" "int& hello() {\n" " int x = 0;\n" " return f(x);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:6]: (error) Reference to local variable returned.\n", errout.str()); check("int& f(int& a) {\n" " return a;\n" "}\n" "int* hello() {\n" " int x = 0;\n" " return &f(x);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:6] -> [test.cpp:5] -> [test.cpp:6]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("int* f(int * x) {\n" " return x;\n" "}\n" "int * g(int x) {\n" " return f(&x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("int* f(int * x) {\n" " x = nullptr;\n" " return x;\n" "}\n" "int * g(int x) {\n" " return f(&x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(int& a) {\n" " return a;\n" "}\n" "int& hello() {\n" " int x = 0;\n" " return f(x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("int& f(int a) {\n" " return a;\n" "}\n" "int& hello() {\n" " int x = 0;\n" " return f(x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to local variable returned.\n", errout.str()); check("int f(int a) {\n" " return a;\n" "}\n" "int& hello() {\n" " int x = 0;\n" " return f(x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout.str()); check("template\n" "int& f(int& x, T y) {\n" " x += y;\n" " return x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReferenceContainer() { check("auto& f() {\n" " std::vector x;\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Reference to local variable returned.\n", errout.str()); check("auto& f() {\n" " std::vector x;\n" " return x.front();\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to local variable returned.\n", errout.str()); check("std::vector g();\n" "auto& f() {\n" " return g().front();\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); check("auto& f() {\n" " return std::vector{1}.front();\n" "}\n"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", "", errout.str()); check("struct A { int foo; };\n" "int& f(std::vector v) {\n" " auto it = v.begin();\n" " return it->foo;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Reference to local variable returned.\n", errout.str()); check("template \n" "const V& get_default(const T& t, const K& k, const V& v) {\n" " auto it = t.find(k);\n" " if (it == t.end()) return v;\n" " return it->second;\n" "}\n" "const int& bar(const std::unordered_map& m, int k) {\n" " auto x = 0;\n" " return get_default(m, k, x);\n" "}\n", true); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:9] -> [test.cpp:9]: (error, inconclusive) Reference to local variable returned.\n", errout.str()); check("template \n" "const V& get_default(const T& t, const K& k, const V& v) {\n" " auto it = t.find(k);\n" " if (it == t.end()) return v;\n" " return it->second;\n" "}\n" "const int& bar(const std::unordered_map& m, int k) {\n" " return get_default(m, k, 0);\n" "}\n", true); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:8] -> [test.cpp:8]: (error, inconclusive) Reference to temporary returned.\n", errout.str()); check("struct A { int foo; };\n" "int& f(std::vector& v) {\n" " auto it = v.begin();\n" " return it->foo;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void returnReferenceLiteral() { check("const std::string &a() {\n" " return \"foo\";\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("const std::string a() {\n" " return \"foo\";\n" "}"); ASSERT_EQUALS("", errout.str()); check("const std::string& f(const std::string& x) { return x; }\n" "const std::string &a() {\n" " return f(\"foo\");\n" "}"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:1] -> [test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); check("const char * f(const char * x) { return x; }\n" "const std::string &a() {\n" " return f(\"foo\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout.str()); } void returnReferenceCalculation() { check("const std::string &a(const std::string& str) {\n" " return \"foo\" + str;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("int& operator<<(int out, int path) {\n" " return out << path;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("std::ostream& operator<<(std::ostream& out, const std::string& path) {\n" " return out << path;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::ostream& operator<<(std::ostream* out, const std::string& path) {\n" " return *out << path;\n" "}"); ASSERT_EQUALS("", errout.str()); check("Unknown1& operator<<(Unknown1 out, Unknown2 path) {\n" " return out << path;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& a(int b) {\n" " return 2*(b+1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("const std::string &a(const std::string& str) {\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("const std::string &a(int bar) {\n" " return foo(bar + 1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("const std::string a(const std::string& str) {\n" " return \"foo\" + str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int& incValue(int& value) {\n" " return ++value;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReferenceLambda() { // #6787 check("const Item& foo(const Container& items) const {\n" " return bar(items.begin(), items.end(),\n" " [](const Item& lhs, const Item& rhs) {\n" " return false;\n" " });\n" "}"); ASSERT_EQUALS("", errout.str()); // #5844 check("map const &getVariableTable() {\n" "static map const s_var = []{\n" " map var;\n" " return var;\n" " }();\n" "return s_var;\n" "}"); ASSERT_EQUALS("", errout.str()); // #7583 check("Command& foo() {\n" " return f([]() -> int { return 1; });\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReferenceInnerScope() { // #6951 check("const Callback& make() {\n" " struct _Wrapper {\n" " static ulong call(void* o, const void* f, const void*[]) {\n" " return 1;\n" " }\n" " };\n" " return _make(_Wrapper::call, pmf);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnReferenceRecursive() { check("int& f() { return f(); }"); ASSERT_EQUALS("", errout.str()); check("int& g(int& i) { return i; }\n" "int& f() { return g(f()); }\n"); ASSERT_EQUALS("", errout.str()); } void extendedLifetime() { check("void g(int*);\n" "int h();\n" "auto f() {\n" " const int& x = h();\n" " return [&] { return x; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning lambda that captures local variable 'x' that will be invalid when returning.\n", errout.str()); check("void g(int*);\n" "int h();\n" "int* f() {\n" " const int& x = h();\n" " return &x;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("void g(int*);\n" "int h();\n" "void f() {\n" " int& x = h();\n" " g(&x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:5]: (error) Using pointer to temporary.\n", errout.str()); check("void g(int*);\n" "int h();\n" "void f() {\n" " const int& x = h();\n" " g(&x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingReference() { check("int f( int k )\n" "{\n" " static int &r = k;\n" " return r;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Non-local reference variable 'r' to local variable 'k'\n", errout.str()); check("int &f( int & k )\n" "{\n" " static int &r = k;\n" " return r;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testglobalnamespace() { check("class SharedPtrHolder\n" "{\n" " ::std::tr1::shared_ptr pNum;\n" "public:\n" " void SetNum(const ::std::tr1::shared_ptr & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnParameterAddress() { check("int* foo(int y)\n" "{\n" " return &y;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'y' that will be invalid when returning.\n", errout.str()); check("int ** foo(int * y)\n" "{\n" " return &y;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'y' that will be invalid when returning.\n", errout.str()); check("const int * foo(const int & y)\n" "{\n" " return &y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int * foo(int * y)\n" "{\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct s { void *p; };\n" "extern struct s* f(void);\n" "void g(void **q)\n" "{\n" " struct s *r = f();\n" " *q = &r->p;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testconstructor() { // Ticket #5478 - crash while checking a constructor check("class const_tree_iterator {\n" " const_tree_iterator(bool (*_incream)(node_type*&)) {}\n" " const_tree_iterator& parent() {\n" " return const_tree_iterator(foo);\n" " }\n" "};"); } void variableIsUsedInScope() { check("void removed_cb (GList *uids) {\n" "for (; uids; uids = uids->next) {\n" "}\n" "}\n" "void opened_cb () {\n" " g_signal_connect (G_CALLBACK (removed_cb));\n" "}"); } void danglingLifetimeLambda() { check("auto f() {\n" " int a = 1;\n" " auto l = [&](){ return a; };\n" " return l;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " int a = 1;\n" " return [&](){ return a; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f(int a) {\n" " return [&](){ return a; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f(int a) {\n" " auto p = &a;\n" " return [=](){ return p; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto g(int& a) {\n" " int p = a;\n" " return [&](){ return p; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'p' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " return [=](){\n" " int a = 1;\n" " return [&](){ return a; };\n" " };\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f(int b) {\n" " return [=](int a){\n" " a += b;\n" " return [&](){ return a; };\n" " };\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto g(int& a) {\n" " return [&](){ return a; };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto g(int a) {\n" " auto p = a;\n" " return [=](){ return p; };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto g(int& a) {\n" " auto p = a;\n" " return [=](){ return p; };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto g(int& a) {\n" " int& p = a;\n" " return [&](){ return p; };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("template\n" "void g(F);\n" "auto f() {\n" " int x;\n" " return g([&]() { return x; });\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeContainer() { check("auto f(const std::vector& x) {\n" " auto it = x.begin();\n" " return it;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto it = x.begin();\n" " return it;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto p = x.data();\n" " return p;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto p = &x[0];\n" " return p;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("struct A { int foo; };\n" "int* f(std::vector v) {\n" " auto it = v.begin();\n" " return &it->foo;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", errout.str()); check("auto f(std::vector x) {\n" " auto it = x.begin();\n" " return it;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto it = x.begin();\n" " return std::next(it);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto it = x.begin();\n" " return it + 1;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " std::vector x;\n" " auto it = x.begin();\n" " return std::next(it + 1);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout.str()); check("std::vector f() {\n" " int i = 0;\n" " std::vector v;\n" " v.push_back(&i);\n" " return v;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:5]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("std::vector f() {\n" " std::vector r;\n" " int i = 0;\n" " std::vector v;\n" " v.push_back(&i);\n" " r.assign(v.begin(), v.end());\n" " return r;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:3] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " std::vector v;\n" " void f() {\n" " int i;\n" " v.push_back(&i);\n" " }\n" "};\n"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", errout.str()); check("struct A {\n" " std::vector v;\n" " void f() {\n" " int i;\n" " int * p = &i;\n" " v.push_back(p);\n" " }\n" "};\n"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:6]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", errout.str()); check("struct A {\n" " std::vector m;\n" " void f() {\n" " int x;\n" " std::vector v;\n" " v.push_back(&x);\n" " m.insert(m.end(), v.begin(), v.end());\n" " }\n" "};\n"); ASSERT_EQUALS( "[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:7]: (error) Non-local variable 'm' will use object that points to local variable 'x'.\n", errout.str()); check("std::vector::iterator f(std::vector v) {\n" " for(auto it = v.begin();it != v.end();it++) {\n" " return it;\n" " }\n" " return {};\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 'v' that will be invalid when returning.\n", errout.str()); check("const char * f() {\n" " std::string ba(\"hello\");\n" " return ba.c_str();\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'ba' that will be invalid when returning.\n", errout.str()); check("template \n" "const V* get_default(const T& t, const K& k, const V* v) {\n" " auto it = t.find(k);\n" " if (it == t.end()) return v;\n" " return &it->second;\n" "}\n" "const int* bar(const std::unordered_map& m, int k) {\n" " auto x = 0;\n" " return get_default(m, k, &x);\n" "}\n", true); ASSERT_EQUALS( "[test.cpp:9] -> [test.cpp:9] -> [test.cpp:8] -> [test.cpp:9]: (error, inconclusive) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout.str()); check("std::vector g();\n" "auto f() {\n" " return g().begin();\n" "}\n"); TODO_ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3]: (error) Returning iterator that will be invalid when returning.\n", "", errout.str()); check("std::vector f();\n" "auto f() {\n" " auto it = g().begin();\n" " return it;\n" "}\n"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("std::vector f();\n" "int& f() {\n" " return *g().begin();\n" "}\n"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct A {\n" " std::vector v;\n" " void f() {\n" " char s[3];\n" " v.push_back(s);\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("std::vector f() {\n" " const char * s = \"hello\";\n" " std::vector v;\n" " v.push_back(s);\n" " return v;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto f() {\n" " static std::vector x;\n" " return x.begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::string g() {\n" " std::vector v;\n" " return v.data();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::vector::iterator f(std::vector* v) {\n" " return v->begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::vector::iterator f(std::vector* v) {\n" " std::vector* v = new std::vector();\n" " return v->begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector v) {\n" " return *v.begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector v) {\n" " return v.end() - v.begin();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("auto g() {\n" " std::vector v;\n" " return {v, [v]() { return v.data(); }};\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("template\n" "void g(F);\n" "auto f() {\n" " std::vector v;\n" " return g([&]() { return v.data(); });\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::vector g();\n" "struct A {\n" " std::vector m;\n" " void f() {\n" " std::vector v = g();\n" " m.insert(m.end(), v.begin(), v.end());\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " std::vector v = {1};\n" " if (b) {\n" " int a[] = {0};\n" " v.insert(a, a+1);\n" " }\n" " return v.back() == 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " int f( P p ) {\n" " std::vector< S > maps;\n" " m2.insert( m1.begin(), m1.end() );\n" " }\n" " struct B {};\n" " std::map< S, B > m1;\n" " std::map< S, B > m2;\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector v;\n" " int x;\n" " void f() {\n" " v.push_back(&x);\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("size_t f(const std::string& x) {\n" " std::string y = \"x\";\n" " return y.find(x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::string* f();\n" "const char* g() {\n" " std::string* var = f();\n" " return var->c_str();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::string f() {\n" " std::vector data{};\n" " data.push_back('a');\n" " return std::string{ data.data(), data.size() };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::vector f() {\n" " char a = 0;\n" " return std::vector{&a};\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", errout.str()); } void danglingLifetime() { check("auto f() {\n" " std::vector a;\n" " auto it = a.begin();\n" " return [=](){ return it; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f(std::vector a) {\n" " auto it = a.begin();\n" " return [=](){ return it; };\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout.str()); check("struct e {};\n" "e * j() {\n" " e c[20];\n" " return c;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'c' that will be invalid when returning.\n", errout.str()); check("auto f(std::vector& a) {\n" " auto it = a.begin();\n" " return [=](){ return it; };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int * f(int a[]) {\n" " return a;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct b {\n" " uint32_t f[6];\n" " } d;\n" " uint32_t *a = d.f;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Don't decay std::array check("std::array f() {\n" " std::array x;\n" " return x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Make sure we don't hang check("struct A;\n" "void f() {\n" " using T = A[3];\n" " A &&a = T{1, 2, 3}[1];\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Make sure we don't hang check("struct A;\n" "void f() {\n" " using T = A[3];\n" " A &&a = T{1, 2, 3}[1]();\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Make sure we don't hang check("struct A;\n" "void f() {\n" " using T = A[3];\n" " A &&a = T{1, 2, 3}[1];\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Make sure we don't hang check("struct A;\n" "void f() {\n" " using T = A[3];\n" " A &&a = T{1, 2, 3}[1]();\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Crash #8872 check("struct a {\n" " void operator()(b c) override {\n" " d(c, [&] { c->e });\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " void operator()(b c) override {\n" " d(c, [=] { c->e });\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " a(char* b) {}\n" "};\n" "a f() {\n" " char c[20];\n" " return c;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " a(char* b) {}\n" "};\n" "a g() {\n" " char c[20];\n" " a d = c;\n" " return d;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct a {\n" " std::vector v;\n" " auto g() { return v.end(); }\n" " };\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int * f(std::vector& v) {\n" " for(int & x : v)\n" " return &x;\n" " return nullptr;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9275 check("struct S {\n" " void f();\n" " std::string m;\n" "}\n" "void S::f() {\n" " char buf[1024];\n" " const char* msg = buf;\n" " m = msg;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9201 check("int* f() {\n" " struct a { int m; };\n" " static a b{0};\n" " return &b.m;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9453 check("int *ptr;\n" "void foo(int arr[]) {\n" " ptr = &arr[2];\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeFunction() { check("auto f() {\n" " int a;\n" " return std::ref(a);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", errout.str()); check("auto f() {\n" " int a;\n" " return std::make_tuple(std::ref(a));\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", errout.str()); check("template\n" "auto by_value(T x) {\n" " return [=] { return x; };\n" "}\n" "auto g() {\n" " std::vector v;\n" " return by_value(v.begin());\n" "}\n"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:7] -> [test.cpp:3] -> [test.cpp:3] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", errout.str()); check("auto by_ref(int& x) {\n" " return [&] { return x; };\n" "}\n" "auto f() {\n" " int i = 0;\n" " return by_ref(i);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:5] -> [test.cpp:6]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("auto f(int x) {\n" " int a;\n" " std::tie(a) = x;\n" " return a;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeAggegrateConstructor() { check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f() {\n" " int i = 0;\n" " return A{i, i};\n" "}\n"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f() {\n" " int i = 0;\n" " return {i, i};\n" "}\n"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); // TODO: Ast is missing for this case check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f() {\n" " int i = 0;\n" " A r{i, i};\n" " return r;\n" "}\n"); TODO_ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", "", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f() {\n" " int i = 0;\n" " A r = {i, i};\n" " return r;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:8]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f(int& x) {\n" " int i = 0;\n" " return A{i, x};\n" "}\n"); ASSERT_EQUALS( "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f(int& x) {\n" " int i = 0;\n" " return A{x, i};\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " const int& x;\n" " int y;\n" "};\n" "A f(int& x) {\n" " return A{x, x};\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int i; const int& j; };\n" "A f(int& x) {\n" " int y = 0;\n" " return A{y, x};\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeInitList() { check("std::vector f() {\n" " int i = 0;\n" " std::vector v = {&i, &i};\n" " return v;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); // TODO: Ast is missing for this case check("std::vector f() {\n" " int i = 0;\n" " std::vector v{&i, &i};\n" " return v;\n" "}\n"); TODO_ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", "", errout.str()); check("std::vector f() {\n" " int i = 0;\n" " return {&i, &i};\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", errout.str()); check("std::vector f(int& x) {\n" " return {&x, &x};\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingLifetimeImplicitConversion() { check("struct A { A(const char *a); };\n" "A f() {\n" " std::string ba(\"hello\");\n" " return ba.c_str();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { A(const char *a); };\n" "A f() {\n" " std::string ba(\"hello\");\n" " A bp = ba.c_str();\n" " return bp;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { A(const char *a); };\n" "std::vector f() {\n" " std::string ba(\"hello\");\n" " std::vector v;\n" " v.push_back(ba.c_str());\n" " return v;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::string f(const std::string& x) {\n" " const char c[] = \"\";\n" " if (!x.empty())\n" " return x + c;\n" " return \"\";\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::string f(const std::string& x) {\n" " const char c[] = \"123\";\n" " if (!x.empty())\n" " return c + 1;\n" " return \"\";\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void danglingTemporaryLifetime() { check("const int& g(const int& x) {\n" " return x;\n" "}\n" "void f(int& i) {\n" " int* x = &g(0);\n" " i += *x;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer to temporary.\n", errout.str()); check("QString f() {\n" " QString a(\"dummyValue\");\n" " const char* b = a.toStdString().c_str();\n" " QString c = b;\n" " return c;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (error) Using pointer to temporary.\n", errout.str()); check("auto f(std::string s) {\n" " const char *x = s.substr(1,2).c_str();\n" " auto i = s.substr(4,5).begin();\n" " return *i;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (error) Using iterator to temporary.\n", errout.str()); } void invalidLifetime() { check("void foo(int a) {\n" " std::function f;\n" " if (a > 0) {\n" " int b = a + 1;\n" " f = [&]{ return b; };\n" " }\n" " f();\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using lambda that captures local variable 'b' that is out of scope.\n", errout.str()); check("void f(bool b) {\n" " int* x;\n" " if(b) {\n" " int y[6] = {0,1,2,3,4,5};\n" " x = y;\n" " }\n" " x[3];\n" "}\n"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'y' that is out of scope.\n", errout.str()); check("void foo(int a) {\n" " std::function f;\n" " if (a > 0) {\n" " int b = a + 1;\n" " f = [&]{ return b; };\n" " f();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " b();\n" " std::list c;\n" "};\n" "void a::b() {\n" " c.end()\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void b(char f[], char c[]) {\n" " std::string d(c); {\n" " std::string e;\n" " b(f, e.c_str())\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " std::string s;\n" " if(b) {\n" " char buf[3];\n" " s = buf;\n" " }\n" " std::cout << s;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int &a[];\n" "void b(){int *c = a};\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int x;\n" "};\n" "struct B {\n" " std::function x;\n" " void f() {\n" " this->x = [&] {\n" " B y;\n" " return y.x;\n" " };\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void deadPointer() { check("void f() {\n" " int *p = p1;\n" " if (cond) {\n" " int x;\n" " p = &x;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'x' that is out of scope.\n", errout.str()); // FP: don't warn in subfunction check("void f(struct KEY *key) {\n" " key->x = 0;\n" "}\n" "\n" "int main() {\n" " struct KEY *tmp = 0;\n" " struct KEY k;\n" "\n" " if (condition) {\n" " tmp = &k;\n" " } else {\n" " }\n" " f(tmp);\n" "}"); ASSERT_EQUALS("", errout.str()); // Don't warn about references (#6399) check("void f() {\n" " wxAuiToolBarItem* former_hover = NULL;\n" " for (i = 0, count = m_items.GetCount(); i < count; ++i) {\n" " wxAuiToolBarItem& item = m_items.Item(i);\n" " former_hover = &item;\n" " }\n" " if (former_hover != pitem)\n" " dosth();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " wxAuiToolBarItem* former_hover = NULL;\n" " for (i = 0, count = m_items.GetCount(); i < count; ++i) {\n" " wxAuiToolBarItem item = m_items.Item(i);\n" " former_hover = &item;\n" " }\n" " if (former_hover != pitem)\n" " dosth();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'item' that is out of scope.\n", errout.str()); // #6575 check("void trp_deliver_signal() {\n" " union {\n" " Uint32 theData[25];\n" " EventReport repData;\n" " };\n" " EventReport * rep = &repData;\n" " rep->setEventType(NDB_LE_Connected);\n" "}"); ASSERT_EQUALS("", errout.str()); // #8785 check("int f(bool a, bool b) {\n" " int *iPtr = 0;\n" " if(b) {\n" " int x = 42;\n" " iPtr = &x;\n" " }\n" " if(b && a)\n" " return *iPtr;\n" " return 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:8]: (error) Using pointer to local variable 'x' that is out of scope.\n", errout.str()); } }; REGISTER_TEST(TestAutoVariables) cppcheck-1.90/test/testbool.cpp000066400000000000000000001347751357737443600166050ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkbool.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestBool : public TestFixture { public: TestBool() : TestFixture("TestBool") { } private: Settings settings; void run() OVERRIDE { settings.addEnabled("style"); settings.addEnabled("warning"); settings.inconclusive = true; TEST_CASE(bitwiseOnBoolean); // if (bool & bool) TEST_CASE(incrementBoolean); TEST_CASE(assignBoolToPointer); TEST_CASE(assignBoolToFloat); TEST_CASE(comparisonOfBoolExpressionWithInt1); TEST_CASE(comparisonOfBoolExpressionWithInt2); TEST_CASE(comparisonOfBoolExpressionWithInt3); TEST_CASE(comparisonOfBoolExpressionWithInt4); TEST_CASE(comparisonOfBoolWithInt1); TEST_CASE(comparisonOfBoolWithInt2); TEST_CASE(comparisonOfBoolWithInt3); TEST_CASE(comparisonOfBoolWithInt4); TEST_CASE(comparisonOfBoolWithInt5); TEST_CASE(comparisonOfBoolWithInt6); // #4224 - integer is casted to bool TEST_CASE(comparisonOfBoolWithInt7); // #4846 - (!x == true) TEST_CASE(comparisonOfBoolWithInt8); // #9165 TEST_CASE(comparisonOfBoolWithInt9); // #9304 TEST_CASE(checkComparisonOfFuncReturningBool1); TEST_CASE(checkComparisonOfFuncReturningBool2); TEST_CASE(checkComparisonOfFuncReturningBool3); TEST_CASE(checkComparisonOfFuncReturningBool4); TEST_CASE(checkComparisonOfFuncReturningBool5); TEST_CASE(checkComparisonOfFuncReturningBool6); // Integration tests.. TEST_CASE(checkComparisonOfFuncReturningBoolIntegrationTest1); // #7798 overloaded functions TEST_CASE(checkComparisonOfBoolWithBool); // Converting pointer addition result to bool TEST_CASE(pointerArithBool1); TEST_CASE(returnNonBool); TEST_CASE(returnNonBoolLambda); TEST_CASE(returnNonBoolLogicalOp); TEST_CASE(returnNonBoolClass); } void check(const char code[], bool experimental = false, const char filename[] = "test.cpp") { // Clear the error buffer.. errout.str(""); settings.experimental = experimental; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); // Check... CheckBool checkBool(&tokenizer, &settings, this); checkBool.runChecks(&tokenizer, &settings, this); } void assignBoolToPointer() { check("void foo(bool *p) {\n" " p = false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Boolean value assigned to pointer.\n", errout.str()); check("void foo(bool *p) {\n" " p = (x[rSize];\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #6588 (c mode) check("struct MpegEncContext { int *q_intra_matrix, *q_chroma_intra_matrix; };\n" "void dnxhd_10bit_dct_quantize(MpegEncContext *ctx, int n, int qscale) {\n" " const int *qmat = n < 4;\n" /* KO */ " const int *rmat = n < 4 ? " /* OK */ " ctx->q_intra_matrix :" " ctx->q_chroma_intra_matrix;\n" "}", /*experimental=*/false, "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Boolean value assigned to pointer.\n", errout.str()); // ticket #6588 (c++ mode) check("struct MpegEncContext { int *q_intra_matrix, *q_chroma_intra_matrix; };\n" "void dnxhd_10bit_dct_quantize(MpegEncContext *ctx, int n, int qscale) {\n" " const int *qmat = n < 4;\n" /* KO */ " const int *rmat = n < 4 ? " /* OK */ " ctx->q_intra_matrix :" " ctx->q_chroma_intra_matrix;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Boolean value assigned to pointer.\n", errout.str()); // ticket #6665 check("void pivot_big(char *first, int compare(const void *, const void *)) {\n" " char *a = first, *b = first + 1, *c = first + 2;\n" " char* m1 = compare(a, b) < 0\n" " ? (compare(b, c) < 0 ? b : (compare(a, c) < 0 ? c : a))\n" " : (compare(a, c) < 0 ? a : (compare(b, c) < 0 ? c : b));\n" "}", /*experimental=*/false, "test.c"); ASSERT_EQUALS("", errout.str()); // #7381 check("void foo(bool *p, bool b) {\n" " p = b;\n" " p = &b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Boolean value assigned to pointer.\n", errout.str()); } void assignBoolToFloat() { check("void foo1() {\n" " double d = false;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout.str()); check("void foo2() {\n" " float d = true;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout.str()); check("void foo3() {\n" " long double d = (2>1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout.str()); // stability - don't crash: check("void foo4() {\n" " unknown = false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " float p;\n" "};\n" "void f() {\n" " S s = {0};\n" " s.p = true;\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (style) Boolean value assigned to floating point variable.\n", errout.str()); check("struct S {\n" " float* p[1];\n" "};\n" "void f() {\n" " S s = {0};\n" " *s.p[0] = true;\n" "}\n"); ASSERT_EQUALS("[test.cpp:6]: (style) Boolean value assigned to floating point variable.\n", errout.str()); } void comparisonOfBoolExpressionWithInt1() { check("void f(int x) {\n" " if ((x && 0x0f)==6)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((x && 0x0f)==0)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x || 0x0f)==6)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((x || 0x0f)==0)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x & 0x0f)==6)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x | 0x0f)==6)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((5 && x)==3)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x)==3 || (8 && x)==9)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x)!=3)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x) > 3)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x) > 0)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((5 && x) < 0)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if ((5 && x) < 1)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((5 && x) > 1)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if (0 < (5 && x))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (0 > (5 && x))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int x) {\n" " if (1 > (5 && x))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (1 < (5 && x))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x ) {\n" " if ( x > false )\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( false < x )\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( x < false )\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( false > x )\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( x >= false )\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( false >= x )\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( x <= false )\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(bool x ) {\n" " if ( false <= x )\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("typedef int (*func)(bool invert);\n" "void x(int, func f);\n" "void foo(int error) {\n" " if (error == ABC) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() { return !a+b1){} }"); ASSERT_EQUALS("",errout.str()); check("void f(int a, int b, int c) { if (a != !b || c) {} }"); ASSERT_EQUALS("",errout.str()); check("void f(int a, int b, int c) { if (1 < !!a + !!b + !!c) {} }"); ASSERT_EQUALS("",errout.str()); check("void f(int a, int b, int c) { if (1 < !(a+b)) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n",errout.str()); } void comparisonOfBoolExpressionWithInt3() { check("int f(int x) {\n" " return t<0>() && x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolExpressionWithInt4() { // #5016 check("void f() {\n" " for(int i = 4; i > -1 < 5 ; --i) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(int a, int b, int c) {\n" " return (a > b) < c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("void f(int a, int b, int c) {\n" " return x(a > b) < c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int b, int c) {\n" " return a > b == c;\n" "}"); ASSERT_EQUALS("", errout.str()); // templates check("struct Tokenizer { TokenList list; };\n" "void Tokenizer::f() {\n" " std::list locationList;\n" "}"); ASSERT_EQUALS("", errout.str()); // #5063 - or check("void f() {\n" " return a > b or c < d;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " return (a < b) != 0U;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " return (a < b) != 0x0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " return (a < b) != 42U;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); } void checkComparisonOfFuncReturningBool1() { check("void f(){\n" " int temp = 4;\n" " if(compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare1(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n" "bool compare2(int temp){\n" " if(temp==4){\n" " return false;\n" " }\n" " else\n" " return true;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool2() { check("void leftOfComparison(){\n" " int temp = 4;\n" " bool a = true;\n" " if(compare(temp) > a){\n" " printf(\"foo\");\n" " }\n" "}\n" "void rightOfComparison(){\n" " int temp = 4;\n" " bool a = true;\n" " if(a < compare(temp)){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" "[test.cpp:11]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool3() { check("void f(){\n" " int temp = 4;\n" " if(compare(temp) > temp){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Comparison of a boolean expression with an integer.\n" "[test.cpp:3]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool4() { check("void f(){\n" " int temp = 4;\n" " bool b = compare2(6);\n" " if(compare1(temp)> b){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare1(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n" "bool compare2(int temp){\n" " if(temp == 5){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool5() { check("void f(){\n" " int temp = 4;\n" " if(compare1(temp) > !compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare1(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n" "bool compare2(int temp){\n" " if(temp==4){\n" " return false;\n" " }\n" " else\n" " return true;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); } void checkComparisonOfFuncReturningBool6() { check("int compare1(int temp);\n" "namespace Foo {\n" " bool compare1(int temp);\n" "}\n" "void f(){\n" " int temp = 4;\n" " if(compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace Foo {\n" " bool compare1(int temp);\n" "}\n" "int compare1(int temp);\n" "void f(){\n" " int temp = 4;\n" " if(compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int compare1(int temp);\n" "namespace Foo {\n" " bool compare1(int temp);\n" " void f(){\n" " int temp = 4;\n" " if(compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); check("int compare1(int temp);\n" "namespace Foo {\n" " bool compare1(int temp);\n" " void f(){\n" " int temp = 4;\n" " if(::compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool compare1(int temp);\n" "void f(){\n" " int temp = 4;\n" " if(foo.compare1(temp) > compare2(temp)){\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkComparisonOfFuncReturningBoolIntegrationTest1() { // #7798 check("bool eval(double *) { return false; }\n" "double eval(char *) { return 1.0; }\n" "int main(int argc, char *argv[])\n" "{\n" " if ( eval(argv[1]) > eval(argv[2]) )\n" " return 1;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkComparisonOfBoolWithBool() { const char code[] = "void f(){\n" " int temp = 4;\n" " bool b = compare2(6);\n" " bool a = compare1(4);\n" " if(b > a){\n" " printf(\"foo\");\n" " }\n" "}\n" "bool compare1(int temp){\n" " if(temp==4){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n" "bool compare2(int temp){\n" " if(temp == 5){\n" " return true;\n" " }\n" " else\n" " return false;\n" "}\n"; check(code, true); ASSERT_EQUALS("[test.cpp:5]: (style) Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n", errout.str()); check(code, false); ASSERT_EQUALS("", errout.str()); } void bitwiseOnBoolean() { // 3062 check("void f(_Bool a, _Bool b) {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(_Bool a, _Bool b) {\n" " if(a | b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a & !b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a | !b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("bool a, b;\n" "void f() {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("bool a, b;\n" "void f() {\n" " if(a & !b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("bool a, b;\n" "void f() {\n" " if(a | b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("bool a, b;\n" "void f() {\n" " if(a | !b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout.str()); check("void f(bool a, int b) {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(int a, bool b) {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'b' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(int a, int b) {\n" " if((a > 0) & (b < 0)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a>0' is used in bitwise operation. Did you mean '&&'?\n", errout.str()); check("void f(int a, int b) {\n" " if(a & b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" " foo(bar, &b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool b) {\n" // #9405 " class C { void foo(bool &b) {} };\n" "}"); ASSERT_EQUALS("", errout.str()); } void incrementBoolean() { check("bool bValue = true;\n" "void f() { bValue++; }"); ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout.str()); check("void f(bool test){\n" " test++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout.str()); check("void f(bool* test){\n" " (*test)++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout.str()); check("void f(bool* test){\n" " test[0]++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout.str()); check("void f(int test){\n" " test++;\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt1() { check("void f(bool x) {\n" " if (x < 10) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x) {\n" " if (10 >= x) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x) {\n" " if (x != 0) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x) {\n" // #3356 " if (x == 1) {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x) {\n" " if (x != 10) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x) {\n" " if (x == 10) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); check("void f(bool x) {\n" " if (x == 0) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("DensePropertyMap visited;"); // #4075 ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt2() { check("void f(bool x, int y) {\n" " if (x == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("void f(int x, bool y) {\n" " if (x == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("void f(bool x, bool y) {\n" " if (x == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool x, fooClass y) {\n" " if (x == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt3() { check("void f(int y) {\n" " if (y > false) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n" "[test.cpp:2]: (warning) Comparison of a boolean value using relational operator (<, >, <= or >=).\n", errout.str()); check("void f(int y) {\n" " if (true == y) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("void f(bool y) {\n" " if (y == true) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(bool y) {\n" " if (false < 5) {\n" " printf(\"foo\");\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout.str()); } void comparisonOfBoolWithInt4() { check("void f(int x) {\n" " if (!x == 1) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt5() { check("void SetVisible(int index, bool visible) {\n" " bool (SciTEBase::*ischarforsel)(char ch);\n" " if (visible != GetVisible(index)) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt6() { // #4224 - integer is casted to bool check("void SetVisible(bool b, int i) {\n" " if (b == (bool)i) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt7() { // #4846 - (!x==true) check("void f(int x) {\n" " if (!x == true) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparisonOfBoolWithInt8() { // #9165 check("bool Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (expectedResult == res)\n" " throw 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (expectedResult == res)\n" " throw 2;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("bool Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (5 + expectedResult == res)\n" " throw 2;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); check("int Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (5 + expectedResult == res)\n" " throw 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int Fun();\n" "void Test(bool expectedResult) {\n" " auto res = Fun();\n" " if (expectedResult == res + 5)\n" " throw 2;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Comparison of a boolean expression with an integer.\n", errout.str()); } void comparisonOfBoolWithInt9() { // #9304 check("bool f(int a, bool b)\n" "{\n" " if ((a == 0 ? false : true) != b) {\n" " b = !b;\n" " }\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void pointerArithBool1() { // #5126 check("void f(char *p) {\n" " if (p+1){}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " do {} while (p+1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " while (p-1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " for (int i = 0; p+1; i++) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " if (p && p+1){}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); check("void f(char *p) {\n" " if (p+2 || p) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout.str()); } void returnNonBool() { check("bool f(void) {\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " return 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " return 2;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " return -1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " return 1 + 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " int x = 0;\n" " return x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " int x = 10;\n" " return x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " return 2 < 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " int ret = 0;\n" " if (a)\n" " ret = 1;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " int ret = 0;\n" " if (a)\n" " ret = 3;\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " if (a)\n" " return 3;\n" " return 4;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n" "[test.cpp:4]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnNonBoolLambda() { check("bool f(void) {\n" " auto x = [](void) { return -1; };\n" " return false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " auto x = [](void) { return -1; };\n" " return 2;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f(void) {\n" " auto x = [](void) -> int { return -1; };\n" " return false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool f(void) {\n" " auto x = [](void) -> int { return -1; };\n" " return 2;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); } void returnNonBoolLogicalOp() { check("bool f(int x) {\n" " return x & 0x4;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x, int y) {\n" " return x | y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " return (x & 0x2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void returnNonBoolClass() { check("class X {\n" " public:\n" " bool f() { return -1;}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout.str()); check("bool f() {\n" " struct X {\n" " public:\n" " int f() { return -1;}\n" " };\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " class X {\n" " public:\n" " int f() { return -1;}\n" " };\n" " return false;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " class X {\n" " public:\n" " bool f() { return -1;}\n" " };\n" " return -1;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Non-boolean value returned from function returning bool\n" "[test.cpp:4]: (style) Non-boolean value returned from function returning bool\n", errout.str()); } }; REGISTER_TEST(TestBool) cppcheck-1.90/test/testboost.cpp000066400000000000000000000071551357737443600167670ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkboost.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestBoost : public TestFixture { public: TestBoost() : TestFixture("TestBoost") { } private: Settings settings; void run() OVERRIDE { settings.addEnabled("style"); settings.addEnabled("performance"); TEST_CASE(BoostForeachContainerModification) } void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check.. CheckBoost checkBoost; checkBoost.runChecks(&tokenizer, &settings, this); } void BoostForeachContainerModification() { check("void f() {\n" " vector data;\n" " BOOST_FOREACH(int i, data) {\n" " data.push_back(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout.str()); check("void f() {\n" " set data;\n" " BOOST_FOREACH(int i, data) {\n" " data.insert(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout.str()); check("void f() {\n" " set data;\n" " BOOST_FOREACH(const int &i, data) {\n" " data.erase(123);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout.str()); // Check single line usage check("void f() {\n" " set data;\n" " BOOST_FOREACH(const int &i, data)\n" " data.clear();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout.str()); // Container returned as result of a function -> Be quiet check("void f() {\n" " BOOST_FOREACH(const int &i, get_data())\n" " data.insert(i);\n" "}"); ASSERT_EQUALS("", errout.str()); // Break after modification (#4788) check("void f() {\n" " vector data;\n" " BOOST_FOREACH(int i, data) {\n" " data.push_back(123);\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestBoost) cppcheck-1.90/test/testbufferoverrun.cpp000066400000000000000000004701071357737443600205340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkbufferoverrun.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include #include #include #include #include class TestBufferOverrun : public TestFixture { public: TestBufferOverrun() : TestFixture("TestBufferOverrun") { } private: Settings settings0; void check(const char code[], bool experimental = true, const char filename[] = "test.cpp") { // Clear the error buffer.. errout.str(""); settings0.inconclusive = true; settings0.experimental = experimental; // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); // Check for buffer overruns.. CheckBufferOverrun checkBufferOverrun; checkBufferOverrun.runChecks(&tokenizer, &settings0, this); } void check(const char code[], const Settings &settings, const char filename[] = "test.cpp") { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); // Clear the error buffer.. errout.str(""); // Check for buffer overruns.. CheckBufferOverrun checkBufferOverrun(&tokenizer, &settings, this); checkBufferOverrun.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { LOAD_LIB_2(settings0.library, "std.cfg"); settings0.addEnabled("warning"); settings0.addEnabled("style"); settings0.addEnabled("portability"); TEST_CASE(noerr1); TEST_CASE(noerr2); TEST_CASE(noerr3); TEST_CASE(noerr4); TEST_CASE(sizeof3); TEST_CASE(array_index_1); TEST_CASE(array_index_2); TEST_CASE(array_index_3); TEST_CASE(array_index_4); TEST_CASE(array_index_6); TEST_CASE(array_index_7); TEST_CASE(array_index_11); TEST_CASE(array_index_12); TEST_CASE(array_index_13); TEST_CASE(array_index_14); TEST_CASE(array_index_15); TEST_CASE(array_index_16); TEST_CASE(array_index_17); TEST_CASE(array_index_18); TEST_CASE(array_index_19); TEST_CASE(array_index_20); TEST_CASE(array_index_21); TEST_CASE(array_index_22); TEST_CASE(array_index_23); TEST_CASE(array_index_24); // ticket #1492 and #1539 TEST_CASE(array_index_25); // ticket #1536 TEST_CASE(array_index_26); TEST_CASE(array_index_27); TEST_CASE(array_index_28); // ticket #1418 TEST_CASE(array_index_29); // ticket #1734 TEST_CASE(array_index_30); // ticket #2086 - out of bounds when type is unknown TEST_CASE(array_index_31); // ticket #2120 - out of bounds in subfunction when type is unknown TEST_CASE(array_index_32); TEST_CASE(array_index_33); // ticket #3044 TEST_CASE(array_index_34); // ticket #3063 TEST_CASE(array_index_35); // ticket #2889 TEST_CASE(array_index_36); // ticket #2960 TEST_CASE(array_index_37); TEST_CASE(array_index_38); // ticket #3273 TEST_CASE(array_index_39); TEST_CASE(array_index_40); // loop variable calculation, taking address TEST_CASE(array_index_41); // structs with the same name TEST_CASE(array_index_42); TEST_CASE(array_index_43); // struct with array TEST_CASE(array_index_44); // #3979 TEST_CASE(array_index_45); // #4207 - calling function with variable number of parameters (...) TEST_CASE(array_index_46); // #4840 - two-statement for loop TEST_CASE(array_index_47); // #5849 TEST_CASE(array_index_48); // #9478 TEST_CASE(array_index_49); // #8653 TEST_CASE(array_index_multidim); TEST_CASE(array_index_switch_in_for); TEST_CASE(array_index_for_in_for); // FP: #2634 TEST_CASE(array_index_calculation); TEST_CASE(array_index_negative1); TEST_CASE(array_index_negative2); // ticket #3063 TEST_CASE(array_index_negative3); TEST_CASE(array_index_for_decr); TEST_CASE(array_index_varnames); // FP: struct member. #1576 TEST_CASE(array_index_for_continue); // for,continue TEST_CASE(array_index_for); // FN: for,if TEST_CASE(array_index_for_neq); // #2211: Using != in condition TEST_CASE(array_index_for_question); // #2561: for, ?: TEST_CASE(array_index_for_andand_oror); // FN: using && or || in the for loop condition TEST_CASE(array_index_for_varid0); // #4228: No varid for counter variable TEST_CASE(array_index_vla_for); // #3221: access VLA inside for TEST_CASE(array_index_extern); // FP when using 'extern'. #1684 TEST_CASE(array_index_cast); // FP after cast. #2841 TEST_CASE(array_index_string_literal); TEST_CASE(array_index_same_struct_and_var_name); // #4751 - not handled well when struct name and var name is same TEST_CASE(array_index_valueflow); TEST_CASE(array_index_valueflow_pointer); TEST_CASE(array_index_function_parameter); TEST_CASE(array_index_enum_array); // #8439 TEST_CASE(array_index_container); // #9386 TEST_CASE(buffer_overrun_2_struct); TEST_CASE(buffer_overrun_3); TEST_CASE(buffer_overrun_4); TEST_CASE(buffer_overrun_5); // TODO strcat TEST_CASE(buffer_overrun_6); TEST_CASE(buffer_overrun_7); TEST_CASE(buffer_overrun_8); TEST_CASE(buffer_overrun_9); TEST_CASE(buffer_overrun_10); TEST_CASE(buffer_overrun_11); TEST_CASE(buffer_overrun_15); // ticket #1787 TEST_CASE(buffer_overrun_16); TEST_CASE(buffer_overrun_18); // ticket #2576 - for, calculation with loop variable TEST_CASE(buffer_overrun_19); // #2597 - class member with unknown type TEST_CASE(buffer_overrun_21); TEST_CASE(buffer_overrun_24); // index variable is changed in for-loop TEST_CASE(buffer_overrun_26); // #4432 (segmentation fault) TEST_CASE(buffer_overrun_27); // #4444 (segmentation fault) TEST_CASE(buffer_overrun_29); // #7083: false positive: typedef and initialization with strings TEST_CASE(buffer_overrun_30); // #6367 TEST_CASE(buffer_overrun_31); TEST_CASE(buffer_overrun_errorpath); TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch // TODO TEST_CASE(buffer_overrun_function_array_argument); // TODO alloca TEST_CASE(possible_buffer_overrun_1); // #3035 TEST_CASE(buffer_overrun_readSizeFromCfg); TEST_CASE(valueflow_string); // using ValueFlow string values in checking // It is undefined behaviour to point out of bounds of an array // the address beyond the last element is in bounds // char a[10]; // char *p1 = a + 10; // OK // char *p2 = a + 11 // UB TEST_CASE(pointer_out_of_bounds_1); // TODO TEST_CASE(pointer_out_of_bounds_2); TEST_CASE(pointer_out_of_bounds_3); TEST_CASE(pointer_out_of_bounds_4); // TODO TEST_CASE(pointer_out_of_bounds_sub); // TODO TEST_CASE(strncat1); // TODO TEST_CASE(strncat2); // TODO TEST_CASE(strncat3); // TODO TEST_CASE(strcat1); // TODO TEST_CASE(strcat2); // TODO TEST_CASE(strcat3); TEST_CASE(varid1); TEST_CASE(varid2); // ticket #4764 TEST_CASE(assign1); // TODO new TEST_CASE(alloc_new); // Buffer allocated with new TEST_CASE(alloc_malloc); // Buffer allocated with malloc TEST_CASE(alloc_string); // statically allocated buffer // TODO TEST_CASE(alloc_alloca); // Buffer allocated with alloca // TODO TEST_CASE(countSprintfLength); TEST_CASE(minsize_argvalue); TEST_CASE(minsize_sizeof); TEST_CASE(minsize_strlen); TEST_CASE(minsize_mul); TEST_CASE(unknownType); TEST_CASE(terminateStrncpy1); TEST_CASE(terminateStrncpy2); TEST_CASE(terminateStrncpy3); TEST_CASE(terminateStrncpy4); TEST_CASE(recursive_long_time); TEST_CASE(crash1); // Ticket #1587 - crash TEST_CASE(crash2); // Ticket #3034 - crash TEST_CASE(crash3); // Ticket #5426 - crash TEST_CASE(crash4); // Ticket #8679 - crash TEST_CASE(crash5); // Ticket #8644 - crash TEST_CASE(crash6); // Ticket #9024 - crash TEST_CASE(crash7); // Ticket #9073 - crash // TODO TEST_CASE(insecureCmdLineArgs); // TODO TEST_CASE(checkBufferAllocatedWithStrlen); TEST_CASE(scope); // handling different scopes TEST_CASE(getErrorMessages); // Access array and then check if the used index is within bounds TEST_CASE(arrayIndexThenCheck); // TODO TEST_CASE(bufferNotZeroTerminated); // TODO TEST_CASE(negativeMemoryAllocationSizeError) // #389 TEST_CASE(negativeArraySize); TEST_CASE(pointerAddition1); TEST_CASE(ctu_malloc); TEST_CASE(ctu_array); TEST_CASE(ctu_variable); TEST_CASE(ctu_arithmetic); TEST_CASE(objectIndex); } void noerr1() { check("extern int ab;\n" "void f()\n" "{\n" " if (ab)\n" " {\n" " char str[50];\n" " }\n" " if (ab)\n" " {\n" " char str[50];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void noerr2() { check("static char buf[2];\n" "void f1(char *str)\n" "{\n" " strcpy(buf,str);\n" "}\n" "void f2(char *str)\n" "{\n" " strcat(buf,str);\n" "}\n" "void f3(char *str)\n" "{\n" " sprintf(buf,\"%s\",str);\n" "}\n" "void f4(const char str[])\n" "{\n" " strcpy(buf, str);\n" "}"); ASSERT_EQUALS("", errout.str()); } void noerr3() { check("struct { char data[10]; } abc;\n" "static char f()\n" "{\n" " char data[1];\n" " return abc.data[1];\n" "}"); ASSERT_EQUALS("", errout.str()); } void noerr4() { // The memory isn't read or written and therefore there is no error. check("static void f() {\n" " char data[100];\n" " const char *p = data + 100;\n" "}"); ASSERT_EQUALS("", errout.str()); } void sizeof3() { check("struct group { int gr_gid; };\n" "void f()\n" "{\n" " char group[32];\n" " snprintf(group, 32, \"%u\", 0);\n" " struct group *gr;\n" " snprintf(group, 32, \"%u\", gr->gr_gid);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_1() { check("void f()\n" "{\n" " char str[0x10];\n" " str[15] = 0;\n" " str[16] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'str[16]' accessed at index 16, which is out of bounds.\n", errout.str()); check("char f()\n" "{\n" " char str[16];\n" " return str[16];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[16]' accessed at index 16, which is out of bounds.\n", errout.str()); // test stack array check("int f()\n" "{\n" " int x[ 3 ] = { 0, 1, 2 };\n" " int y;\n" " y = x[ 4 ];\n" " return y;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'x[3]' accessed at index 4, which is out of bounds.\n", errout.str()); check("int f()\n" "{\n" " int x[ 3 ] = { 0, 1, 2 };\n" " int y;\n" " y = x[ 2 ];\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int x[5];\n" "int a = x[10];\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Array 'x[5]' accessed at index 10, which is out of bounds.\n", errout.str()); check("int x[5];\n" "int a = (x)[10];\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Array 'x[5]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_2() { check("void a(int i)\n" // valueflow "{\n" " char *str = new char[0x10];\n" " str[i] = 0;\n" "}\n" "void b() { a(16); }"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[16]' accessed at index 16, which is out of bounds.\n", "", errout.str()); } void array_index_4() { check("char c = \"abc\"[4];"); ASSERT_EQUALS("[test.cpp:1]: (error) Array '\"abc\"[4]' accessed at index 4, which is out of bounds.\n", errout.str()); check("p = &\"abc\"[4];"); ASSERT_EQUALS("", errout.str()); check("char c = \"\\0abc\"[2];"); ASSERT_EQUALS("", errout.str()); check("char c = L\"abc\"[4];"); ASSERT_EQUALS("[test.cpp:1]: (error) Array 'L\"abc\"[4]' accessed at index 4, which is out of bounds.\n", errout.str()); } void array_index_3() { check("void f()\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = 0; i < 100; i++)\n" " sum += val[i];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = 1; i < 100; i++)\n" " sum += val[i];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str()); check("void f(int a)\n" "{\n" " int val[50];\n" " int i, sum=0;\n" " for (i = a; i < 100; i++)\n" " sum += val[i];\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout.str()); check("typedef struct g g2[3];\n" "void foo(char *a)\n" "{\n" " for (int i = 0; i < 4; i++)\n" " {\n" " a[i]=0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int argc)\n" "{\n" " char a[2];\n" " for (int i = 4; i < argc; i++){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a[10]) {\n" " for (int i=0;i<50;++i) {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[10]' accessed at index 49, which is out of bounds.\n", errout.str()); } void array_index_6() { check("struct ABC\n" "{\n" " char str[10];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC abc;\n" " abc.str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("struct ABC\n" "{\n" " char str[10];\n" "};\n" "\n" "static char f()\n" "{\n" " struct ABC abc;\n" " return abc.str[10];\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); // This is not out of bounds because it is a variable length array check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" " x->str[1] = 0;" "}"); ASSERT_EQUALS("", errout.str()); // This is not out of bounds because it is not a variable length array check("struct ABC\n" "{\n" " char str[1];\n" " int x;\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" " x->str[1] = 0;" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // This is not out of bounds because it is a variable length array // and the index is within the memory allocated. /** @todo this works by accident because we ignore any access to this array */ check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" " x->str[10] = 0;" "}"); ASSERT_EQUALS("", errout.str()); // This is out of bounds because it is outside the memory allocated. /** @todo this doesn't work because of a bug in sizeof(struct) */ check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" " x->str[11] = 0;" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Array 'str[1]' accessed at index 11, which is out of bounds.\n", "", errout.str()); // This is out of bounds if 'sizeof(ABC)' is 1 (No padding) check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(ABC) + 10);\n" " x->str[11] = 0;" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // This is out of bounds because it is outside the memory allocated /** @todo this doesn't work because of a bug in sizeof(struct) */ check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(struct ABC));\n" " x->str[1] = 0;" "}"); TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Array 'str[1]' accessed at index 1, which is out of bounds.\n", "", errout.str()); // This is out of bounds because it is outside the memory allocated // But only if 'sizeof(ABC)' is 1 (No padding) check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC* x = malloc(sizeof(ABC));\n" " x->str[1] = 0;" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // This is out of bounds because it is not a variable array check("struct ABC\n" "{\n" " char str[1];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC x;\n" " x.str[1] = 0;" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'x.str[1]' accessed at index 1, which is out of bounds.\n", errout.str()); check("struct foo\n" "{\n" " char str[10];\n" "};\n" "\n" "void x()\n" "{\n" " foo f;\n" " for ( unsigned int i = 0; i < 64; ++i )\n" " f.str[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'f.str[10]' accessed at index 63, which is out of bounds.\n", errout.str()); check("struct AB { char a[NUM]; char b[NUM]; }\n" "void f(struct AB *ab) {\n" " ab->a[0] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("union { char a[1]; int b; } ab;\n" "void f() {\n" " ab.a[2] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'ab.a[1]' accessed at index 2, which is out of bounds.\n", errout.str()); } void array_index_7() { check("struct ABC\n" "{\n" " char str[10];\n" "};\n" "\n" "static void f(struct ABC *abc)\n" "{\n" " abc->str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'abc->str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_11() { check("class ABC\n" "{\n" "public:\n" " ABC();\n" " char *str[10];\n" " struct ABC *next();" "};\n" "\n" "static void f()\n" "{\n" " ABC *abc1;\n" " for ( ABC *abc = abc1; abc; abc = abc->next() )\n" " {\n" " abc->str[10] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:13]: (error) Array 'abc->str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_12() { check("class Fred\n" "{\n" "private:\n" " char str[10];\n" "public:\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{\n" " str[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " char str[10];\n" "public:\n" " char c();\n" "};\n" "char Fred::c()\n" "{\n" " return str[10];\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'str[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_13() { check("void f()\n" "{\n" " char buf[10];\n" " for (int i = 0; i < 100; i++)\n" " {\n" " if (i < 10)\n" " int x = buf[i];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_14() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i+10] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str()); } void array_index_15() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[10+i] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str()); } void array_index_16() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i+1] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_17() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i*2] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index 18, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " int a[12];\n" " for (int i = 0; i < 12; i+=6)\n" " a[i+5] = i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[12];\n" " for (int i = 0; i < 12; i+=6)\n" " a[i+6] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[12]' accessed at index 12, which is out of bounds.\n", errout.str()); check("void f() {\n" // #4398 " int a[2];\n" " for (int i = 0; i < 4; i+=2)\n" " a[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.\n", errout.str()); check("void f() {\n" // #4398 " int a[2];\n" " for (int i = 0; i < 4; i+=2)\n" " do_stuff(a+i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_18() { check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i+=1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " ++i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[5];\n" " for (int i = 0; i < 6; i++)\n" " {\n" " a[i] = i;\n" " i=4;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int a[6];\n" " for (int i = 0; i < 7; i++)\n" " {\n" " a[i] = i;\n" " i+=1;\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Buffer overrun\n", "", errout.str()); } void array_index_19() { // "One Past the End" is legal, as long as pointer is not dereferenced. check("void f()\n" "{\n" " char a[2];\n" " char *end = &(a[2]);\n" "}"); ASSERT_EQUALS("", errout.str()); // Getting more than one past the end is not legal check("void f()\n" "{\n" " char a[2];\n" " char *end = &(a[3]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 3, which is out of bounds.\n", errout.str()); } void array_index_20() { check("void f()\n" "{\n" " char a[8];\n" " int b[10];\n" " for ( int i = 0; i < 9; i++ )\n" " b[i] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_21() { check("class A {\n" " int indices[2];\n" " void foo(int indices[3]);\n" "};\n" "\n" "void A::foo(int indices[3]) {\n" " for(int j=0; j<3; ++j) {\n" " int b = indices[j];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_22() { check("int main() {\n" " size_t indices[2];\n" " int b = indices[2];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'indices[2]' accessed at index 2, which is out of bounds.\n", errout.str()); } void array_index_23() { check("void foo()\n" "{\n" " char c[10];\n" " c[1<<23]='a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'c[10]' accessed at index 8388608, which is out of bounds.\n", errout.str()); } void array_index_24() { // ticket #1492 and #1539 const std::string charMaxPlusOne(settings0.defaultSign == 'u' ? "256" : "128"); check(("void f(char n) {\n" " int a[n];\n" // n <= CHAR_MAX " a[-1] = 0;\n" // negative index " a[" + charMaxPlusOne + "] = 0;\n" // 128/256 > CHAR_MAX "}\n").c_str()); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[" + charMaxPlusOne + "]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[" + charMaxPlusOne + "]' accessed at index " + charMaxPlusOne + ", which is out of bounds.\n", errout.str()); check("void f(signed char n) {\n" " int a[n];\n" // n <= SCHAR_MAX " a[-1] = 0;\n" // negative index " a[128] = 0;\n" // 128 > SCHAR_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[128]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[128]' accessed at index 128, which is out of bounds.\n", errout.str()); check("void f(unsigned char n) {\n" " int a[n];\n" // n <= UCHAR_MAX " a[-1] = 0;\n" // negative index " a[256] = 0;\n" // 256 > UCHAR_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[256]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[256]' accessed at index 256, which is out of bounds.\n", errout.str()); check("void f(short n) {\n" " int a[n];\n" // n <= SHRT_MAX " a[-1] = 0;\n" // negative index " a[32768] = 0;\n" // 32768 > SHRT_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[32768]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[32768]' accessed at index 32768, which is out of bounds.\n", errout.str()); check("void f(unsigned short n) {\n" " int a[n];\n" // n <= USHRT_MAX " a[-1] = 0;\n" // negative index " a[65536] = 0;\n" // 65536 > USHRT_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[65536]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[65536]' accessed at index 65536, which is out of bounds.\n", errout.str()); check("void f(signed short n) {\n" " int a[n];\n" // n <= SHRT_MAX " a[-1] = 0;\n" // negative index " a[32768] = 0;\n" // 32768 > SHRT_MAX "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[32768]' accessed at index -1, which is out of bounds.\n" "[test.cpp:4]: (error) Array 'a[32768]' accessed at index 32768, which is out of bounds.\n", errout.str()); check("void f(int n) {\n" " int a[n];\n" // n <= INT_MAX " a[-1] = 0;\n" // negative index "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[2147483648]' accessed at index -1, which is out of bounds.\n", errout.str()); check("void f(unsigned int n) {\n" " int a[n];\n" // n <= UINT_MAX " a[-1] = 0;\n" // negative index "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[4294967296]' accessed at index -1, which is out of bounds.\n", errout.str()); check("void f(signed int n) {\n" " int a[n];\n" // n <= INT_MAX " a[-1] = 0;\n" // negative index "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[2147483648]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_25() { // #1536 check("void foo()\n" "{\n" " long l[SOME_SIZE];\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_26() { check("void f()\n" "{\n" " int a[3];\n" " for (int i = 3; 0 <= i; i--)\n" " a[i] = i;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[3]' accessed at index 3, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " int a[4];\n" " for (int i = 3; 0 <= i; i--)\n" " a[i] = i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_27() { check("void f()\n" "{\n" " int a[10];\n" " for (int i = 0; i < 10; i++)\n" " a[i-1] = a[i];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_28() { // ticket #1418 check("void f()\n" "{\n" " int i[2];\n" " int *ip = i + 1;\n" " ip[-10] = 1;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Array ip[-10] out of bounds.\n", "", errout.str()); } void array_index_29() { // ticket #1724 check("void f()\n" "{\n" " int iBuf[10];" " int *i = iBuf + 9;" " int *ii = i + -5;" " ii[10] = 0;" "}"); TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Array ii[10] out of bounds.\n", "", errout.str()); } void array_index_30() { // ticket #2086 - unknown type check("void f() {\n" " UINT8 x[2];\n" " x[5] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'x[2]' accessed at index 5, which is out of bounds.\n", errout.str()); } void array_index_31() { // ticket #2120 - sub function, unknown type check("struct s1 {\n" " unknown_type_t delay[3];\n" "};\n" "\n" "void x(unknown_type_t *delay, const int *net) {\n" " delay[0] = 0;\n" "}\n" "\n" "void y() {\n" " struct s1 obj;\n" " x(obj.delay, 123);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct s1 {\n" " unknown_type_t delay[3];\n" "};\n" "\n" "void x(unknown_type_t *delay, const int *net) {\n" " delay[4] = 0;\n" "}\n" "\n" "void y() {\n" " struct s1 obj;\n" " x(obj.delay, 123);\n" "}"); // TODO CTU ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:6]: (error) Array 'obj.delay[3]' accessed at index 4, which is out of bounds.\n", errout.str()); check("struct s1 {\n" " float a[0];\n" "};\n" "\n" "void f() {\n" " struct s1 *obj;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_32() { check("class X\n" "{\n" " public:\n" " X()\n" " {\n" " m_x[0] = 0;\n" " m_x[1] = 0;\n" " }\n" " int m_x[1];\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'm_x[1]' accessed at index 1, which is out of bounds.\n", errout.str()); } void array_index_33() { check("void foo(char bar[][4]) {\n" " baz(bar[5]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_34() { // ticket #3063 check("void foo() {\n" " int y[2][2][2];\n" " y[0][2][0] = 0;\n" " y[0][0][2] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'y[2][2][2]' accessed at index y[0][2][0], which is out of bounds.\n" "[test.cpp:4]: (error) Array 'y[2][2][2]' accessed at index y[0][0][2], which is out of bounds.\n", errout.str()); check("struct TEST\n" "{\n" " char a[10];\n" " char b[10][5];\n" "};\n" "void foo()\n" "{\n" " TEST test;\n" " test.a[10] = 3;\n" " test.b[10][2] = 4;\n" " test.b[0][19] = 4;\n" " TEST *ptest;\n" " ptest = &test;\n" " ptest->a[10] = 3;\n" " ptest->b[10][2] = 4;\n" " ptest->b[0][19] = 4;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'test.a[10]' accessed at index 10, which is out of bounds.\n" "[test.cpp:10]: (error) Array 'test.b[10][5]' accessed at index test.b[10][2], which is out of bounds.\n" "[test.cpp:11]: (error) Array 'test.b[10][5]' accessed at index test.b[0][19], which is out of bounds.\n" "[test.cpp:14]: (error) Array 'ptest->a[10]' accessed at index 10, which is out of bounds.\n" "[test.cpp:15]: (error) Array 'ptest->b[10][5]' accessed at index ptest->b[10][2], which is out of bounds.\n" "[test.cpp:16]: (error) Array 'ptest->b[10][5]' accessed at index ptest->b[0][19], which is out of bounds.\n", errout.str()); check("struct TEST\n" "{\n" " char a[10][5];\n" "};\n" "void foo()\n" "{\n" " TEST test;\n" " test.a[9][5] = 4;\n" " test.a[0][50] = 4;\n" " TEST *ptest;\n" " ptest = &test;\n" " ptest->a[9][5] = 4;\n" " ptest->a[0][50] = 4;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'test.a[10][5]' accessed at index test.a[9][5], which is out of bounds.\n" "[test.cpp:9]: (error) Array 'test.a[10][5]' accessed at index test.a[0][50], which is out of bounds.\n" "[test.cpp:12]: (error) Array 'ptest->a[10][5]' accessed at index ptest->a[9][5], which is out of bounds.\n" "[test.cpp:13]: (error) Array 'ptest->a[10][5]' accessed at index ptest->a[0][50], which is out of bounds.\n", errout.str()); } void array_index_35() { // ticket #2889 check("void f() {\n" " struct Struct { unsigned m_Var[1]; } s;\n" " s.m_Var[1] = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 's.m_Var[1]' accessed at index 1, which is out of bounds.\n", errout.str()); check("struct Struct { unsigned m_Var[1]; };\n" "void f() {\n" " struct Struct s;\n" " s.m_Var[1] = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's.m_Var[1]' accessed at index 1, which is out of bounds.\n", errout.str()); check("struct Struct { unsigned m_Var[1]; };\n" "void f() {\n" " struct Struct * s = calloc(40);\n" " s->m_Var[1] = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_36() { // ticket #2960 check("class Fred {\n" " Fred(const Fred &);\n" "private:\n" " bool m_b[2];\n" "};\n" "Fred::Fred(const Fred & rhs) {\n" " m_b[2] = rhs.m_b[2];\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'm_b[2]' accessed at index 2, which is out of bounds.\n" "[test.cpp:7]: (error) Array 'rhs.m_b[2]' accessed at index 2, which is out of bounds.\n", errout.str()); } void array_index_37() { check("class Fred {\n" " char x[X];\n" " Fred() {\n" " for (unsigned int i = 0; i < 15; i++)\n" " i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_38() { //ticket #3273 check("void aFunction() {\n" " double aDoubleArray[ 10 ];\n" " unsigned int i; i = 0;\n" " for( i = 0; i < 6; i++ )\n" " {\n" " unsigned int j; j = 0;\n" " for( j = 0; j < 5; j++ )\n" " {\n" " unsigned int x; x = 0;\n" " for( x = 0; x < 4; x++ )\n" " {\n" " }\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_39() { // ticket 3387 check("void aFunction()\n" "{\n" " char a[10];\n" " a[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_40() { check("void f() {\n" " char a[10];\n" " for (int i = 0; i < 10; ++i)\n" " f2(&a[i + 1]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_41() { // Don't generate false positives when structs have the same name check("void a() {\n" " struct Fred { char data[6]; } fred;\n" " fred.data[4] = 0;\n" // <- no error "}\n" "\n" "void b() {\n" " struct Fred { char data[3]; } fred;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void a() {\n" " struct Fred { char data[6]; } fred;\n" " fred.data[4] = 0;\n" // <- no error "}\n" "\n" "void b() {\n" " struct Fred { char data[3]; } fred;\n" " fred.data[4] = 0;\n" // <- error "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'fred.data[3]' accessed at index 4, which is out of bounds.\n", errout.str()); } void array_index_42() { // ticket #3569 check("void f()\n" "{\n" " char *p; p = malloc(10);\n" " p[10] = 7;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'p[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char *p; p = malloc(10);\n" " p[0] = 0;\n" " p[9] = 9;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *p; p = new char[10];\n" " p[0] = 0;\n" " p[9] = 9;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *p(new char[10]);\n" " p[0] = 0;\n" " p[9] = 9;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *p = NULL;" " try{\n" " p = new char[10];\n" " }\n" " catch(...){\n" " return;\n" " }" " p[0] = 0;\n" " p[9] = 9;\n" " delete [] p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_43() { // #3838 check("int f( )\n" "{\n" " struct {\n" " int arr[ 3 ];\n" " } var[ 1 ];\n" " int y;\n" " var[ 0 ].arr[ 0 ] = 0;\n" " var[ 0 ].arr[ 1 ] = 1;\n" " var[ 0 ].arr[ 2 ] = 2;\n" " y = var[ 0 ].arr[ 3 ];\n" // <-- array access out of bounds " return y;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'var[0].arr[3]' accessed at index 3, which is out of bounds.\n", errout.str()); check("int f( )\n" "{\n" " struct {\n" " int arr[ 3 ];\n" " } var[ 1 ];\n" " int y=1;\n" " var[ 0 ].arr[ 0 ] = 0;\n" " var[ 0 ].arr[ 1 ] = 1;\n" " var[ 0 ].arr[ 2 ] = 2;\n" " y = var[ 0 ].arr[ 2 ];\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f( ){ \n" "struct Struct{\n" " int arr[ 3 ];\n" "};\n" "int y;\n" "Struct var;\n" "var.arr[ 0 ] = 0;\n" "var.arr[ 1 ] = 1;\n" "var.arr[ 2 ] = 2;\n" "var.arr[ 3 ] = 3;\n" // <-- array access out of bounds "y=var.arr[ 3 ];\n" // <-- array access out of bounds "return y;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Array 'var.arr[3]' accessed at index 3, which is out of bounds.\n" "[test.cpp:11]: (error) Array 'var.arr[3]' accessed at index 3, which is out of bounds.\n", errout.str()); check("void f( ) {\n" "struct S{\n" " int var[ 3 ];\n" "} ;\n" "S var[2];\n" "var[0].var[ 0 ] = 0;\n" "var[0].var[ 1 ] = 1;\n" "var[0].var[ 2 ] = 2;\n" "var[0].var[ 4 ] = 4;\n" // <-- array access out of bounds "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'var[0].var[3]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void f( ) {\n" "struct S{\n" " int var[ 3 ];\n" "} ;\n" "S var[2];\n" "var[0].var[ 0 ] = 0;\n" "var[0].var[ 1 ] = 1;\n" "var[0].var[ 2 ] = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid FPs (modified examples taken from #3838) check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab;\n" " int * p = &ab.a[10]; \n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab[1];\n" " int * p = &ab[0].a[10]; \n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a[10]; int b[10]; };\n" "int main() {\n" " struct AB ab[1];\n" " int * p = &ab[10].a[0]; \n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'ab[1]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_44() { // #3979 (false positive) check("void f()\n" "{\n" " char buf[2];\n" " int i;\n" " for (i = 2; --i >= 0; )\n" " {\n" " buf[i] = 1;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " double buf[2];\n" " for (int i = 2; i--; )\n" " {\n" " buf[i] = 2.;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_45() { // #4207 - handling of function with variable number of parameters / unnamed arguments // Variable number of arguments check("void f(const char *format, ...) {\n" " va_args args;\n" " va_start(args, format);\n" "}\n" "void test() {\n" " CHAR buffer[1024];\n" " f(\"%s\", buffer);\n" "}"); ASSERT_EQUALS("", errout.str()); // Unnamed argument check("void f(char *) {\n" " dostuff();\n" "}\n" "void test() {\n" " char buffer[1024];\n" " f(buffer);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Two statement for-loop void array_index_46() { // #4840 check("void bufferAccessOutOfBounds2() {\n" " char *buffer[]={\"a\",\"b\",\"c\"};\n" " for(int i=3; i--;) {\n" " printf(\"files(%i): %s\", 3-i, buffer[3-i]);\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 'buffer[3]' accessed at index 3, which is out of bounds.\n", "", errout.str()); check("void f() {\n" " int buffer[9];\n" " long int i;\n" " for(i=10; i--;) {\n" " buffer[i] = i;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'buffer[9]' accessed at index 9, which is out of bounds.\n", errout.str()); // Correct access limits -> i from 9 to 0 check("void f() {\n" " int buffer[10];\n" " for(unsigned long int i=10; i--;) {\n" " buffer[i] = i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_47() { // #5849 check("int s[4];\n" "void f() {\n" " for (int i = 2; i < 0; i++)\n" " s[i] = 5; \n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_48() { // #9478 check("void test(void)\n" "{\n" " int array[4] = { 1,2,3,4 };\n" " for (int i = 1; i <= 4; i++) {\n" " printf(\" %i\", i);\n" " array[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'array[4]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void test(void)\n" "{\n" " int array[4] = { 1,2,3,4 };\n" " for (int i = 1; i <= 4; i++) {\n" " scanf(\"%i\", &i);\n" " array[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_49() { // #8653 check("void f() {\n" " int i, k;\n" " int arr[34] = {};\n" " i = 1;\n" " for (k = 0; k < 34 && i < 34; k++) {\n" " i++;\n" " }\n" " arr[k];\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void array_index_multidim() { check("void f()\n" "{\n" " char a[2][2];\n" " a[1][1] = 'a';\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char a[2][2][2];\n" " a[1][1][1] = 'a';\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char a[2][2];\n" " a[2][1] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2]' accessed at index a[2][1], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2];\n" " a[1][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2]' accessed at index a[1][2], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2][2];\n" " a[2][1][1] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[2][1][1], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2][2];\n" " a[1][2][1] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[1][2][1], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2][2][2];\n" " a[1][2][1][1] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2][2]' accessed at index a[1][2][1][1], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[2][2][2];\n" " a[1][1][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[1][1][2], which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char a[10][10][10];\n" " a[2*3][4*3][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10][10][10]' accessed at index a[6][12][2], which is out of bounds.\n", errout.str()); check("void f() {\n" " char a[10][10][10];\n" " a[6][40][10] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[10][10][10]' accessed at index a[6][40][10], which is out of bounds.\n", errout.str()); check("void f() {\n" " char a[1][1][1];\n" " a[2][2][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[1][1][1]' accessed at index a[2][2][2], which is out of bounds.\n", errout.str()); check("void f() {\n" " char a[6][6][6];\n" " a[6][6][2] = 'a';\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[6][6][6]' accessed at index a[6][6][2], which is out of bounds.\n", errout.str()); check("void f() {\n" " int a[2][2];\n" " p = &a[2][0];\n" "}"); ASSERT_EQUALS("", errout.str()); // unknown dim.. check("void f()\n" "{\n" " int a[2][countof(x)] = {{1,2},{3,4}};\n" " a[0][0] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void draw_quad(float z) {\n" " int i;\n" " float (*vertices)[2][4];\n" " vertices[0][0][0] = z;\n" " vertices[0][0][1] = z;\n" " vertices[1][0][0] = z;\n" " vertices[1][0][1] = z;\n" " vertices[2][0][0] = z;\n" " vertices[2][0][1] = z;\n" " vertices[3][0][0] = z;\n" " vertices[3][0][1] = z;\n" " for (i = 0; i < 4; i++) {\n" " vertices[i][0][2] = z;\n" " vertices[i][0][3] = 1.0;\n" " vertices[i][1][0] = 2.0;\n" " vertices[i][1][1] = 3.0;\n" " vertices[i][1][2] = 4.0;\n" " vertices[i][1][3] = 5.0;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); { check("int foo() {\n" " const size_t A = 4;\n" " const size_t B = 2;\n" " extern int stuff[A][B];\n" " return stuff[0][1];\n" "}"); ASSERT_EQUALS("", errout.str()); // TODO: better handling of VLAs in symboldatabase. should be // possible to use ValueFlow values. check("int foo() {\n" " const size_t A = 4;\n" " const size_t B = 2;\n" " extern int stuff[A][B];\n" " return stuff[0][1];\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } } void array_index_switch_in_for() { check("void f()\n" "{\n" " int ar[10];\n" " for (int i = 0; i < 10; ++i)\n" " {\n" " switch(i)\n" " {\n" " case 9:\n" " ar[i] = 0;\n" " break;\n" " default:\n" " ar[i] = ar[i+1];\n" " break;\n" " };\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int ar[10];\n" " for (int i = 0; i < 10; ++i)\n" " {\n" " switch(i)\n" " {\n" " case 8:\n" " ar[i] = 0;\n" " break;\n" " default:\n" " ar[i] = ar[i+1];\n" " break;\n" " };\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:12]: (error) Array index out of bounds.\n", "", errout.str()); } void array_index_for_in_for() { check("void f() {\n" " int a[5];\n" " for (int i = 0; i < 10; ++i) {\n" " for (int j = i; j < 5; ++j) {\n" " a[i] = 0;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_calculation() { // #1193 - false negative: array out of bounds in loop when there is calculation check("void f()\n" "{\n" " char data[8];\n" " for (int i = 19; i < 36; ++i) {\n" " data[i/2] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[8]' accessed at index 17, which is out of bounds.\n", errout.str()); // #2199 - false negative: array out of bounds in loop when there is calculation check("void f()\n" "{\n" " char arr[5];\n" " for (int i = 0; i < 5; ++i) {\n" " arr[i + 7] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'arr[5]' accessed at index 11, which is out of bounds.\n", errout.str()); } void array_index_negative1() { // #948 - array index out of bound not detected 'a[-1] = 0' check("void f()\n" "{\n" " char data[8];\n" " data[-1] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'data[8]' accessed at index -1, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char data[8][4];\n" " data[5][-1] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'data[8][4]' accessed at index data[*][-1], which is out of bounds.\n", errout.str()); // #1614 - negative index is ok for pointers check("void foo(char *p)\n" "{\n" " p[-1] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " char s[] = \"abc\";\n" " char *p = s + strlen(s);\n" " if (p[-1]);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #1850 check("int f(const std::map > &m)\n" "{\n" " return m[0][-1];\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_negative2() { // ticket #3063 check("struct TEST { char a[10]; };\n" "void foo() {\n" " TEST test;\n" " test.a[-1] = 3;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'test.a[10]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_negative3() { check("int f(int i) {\n" " int p[2] = {0, 0};\n" " if(i >= 2)\n" " return 0;\n" " else if(i == 0)\n" " return 0;\n" " return p[i - 1];\n" "}\n" "void g(int i) {\n" " if( i == 0 )\n" " return f(i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_for_decr() { check("void f()\n" "{\n" " char data[8];\n" " for (int i = 10; i > 0; --i) {\n" " data[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[8]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " char val[5];\n" " for (unsigned int i = 3; i < 5; --i) {\n" " val[i+1] = val[i];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char val[5];\n" " for (int i = 3; i < 5; --i) {\n" " val[i+1] = val[i];\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'val[5]' accessed at index -9994, which is out of bounds.\n" "[test.cpp:5]: (error) Array 'val[5]' accessed at index -9995, which is out of bounds.\n", errout.str()); } void array_index_varnames() { check("struct A {\n" " char data[4];\n" " struct B { char data[3]; };\n" " B b;\n" "};\n" "\n" "void f()\n" "{\n" " A a;\n" " a.data[3] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_for_andand_oror() { // #3907 - using && or || check("void f() {\n" " char data[2];\n" " int x;\n" " for (x = 0; x < 10 && y; x++) {\n" " data[x] = 0;\n" " }" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 9, which is out of bounds.\n", errout.str()); check("void f() {\n" " char data[2];\n" " int x;\n" " for (x = 0; x < 10 || y; x++) {\n" " data[x] = 0;\n" " }" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 9, which is out of bounds.\n", errout.str()); check("void f() {\n" " char data[2];\n" " int x;\n" " for (x = 0; x <= 10 && y; x++) {\n" " data[x] = 0;\n" " }" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f() {\n" " char data[2];\n" " int x;\n" " for (x = 0; y && x <= 10; x++) {\n" " data[x] = 0;\n" " }" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_for_continue() { // #3913 check("void f() {\n" " int a[2];\n" " for (int i = 0; i < 2; ++i) {\n" " if (i == 0) {\n" " continue;\n" " }\n" " a[i - 1] = 0;\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[2];\n" " for (int i = 0; i < 2; ++i) {\n" " if (somecondition) {\n" " continue;\n" " }\n" " a[i - 1] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'a[2]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_for() { // Ticket #2370 - No false negative when there is no "break" check("void f() {\n" " int a[10];\n" " for (int i = 0; i < 20; ++i) {\n" " if (i==1) {\n" " }\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout.str()); // Ticket #2385 - No false positive check("void f() {\n" " int a[10];\n" " for (int i = 0; i < 20; ++i) {\n" " if (i<10) {\n" " } else {\n" " a[i-10] = 0;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #3893 - start value out of bounds check("void f() {\n" " int a[10];\n" " for (int i = 10; somecondition; dosomething) {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void array_index_for_neq() { // Ticket #2211 - for loop using != in the condition check("void f() {\n" " int a[5];\n" " for (int i = 0; i != 10; ++i) {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[5]' accessed at index 9, which is out of bounds.\n", errout.str()); } void array_index_for_question() { // Ticket #2561 - using ?: inside for loop check("void f() {\n" " int a[10];\n" " for (int i = 0; i != 10; ++i) {\n" " i == 0 ? 0 : a[i-1];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[10];\n" " for (int i = 0; i != 10; ++i) {\n" " some_condition ? 0 : a[i-1];\n" " }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array index -1 is out of bounds.\n", "", errout.str()); check("void f() {\n" " int a[10];\n" " for (int i = 0; i != 10; ++i) {\n" " i==0 ? 0 : a[i-1];\n" " a[i-1] = 0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n", errout.str()); } void array_index_for_varid0() { // #4228: No varid for counter variable check("void f() {\n" " char a[10];\n" " for (i=0; i<10; i++);\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_vla_for() { // #3221 - access VLA inside for check("void f(int len) {\n" " char a[len];\n" " for (int i=0; i<7; ++i) {\n" " a[0] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_extern() { // Ticket #1684. FP when using 'extern'. check("extern char arr[15];\n" "char arr[15] = \"abc\";"); ASSERT_EQUALS("", errout.str()); } void array_index_cast() { // Ticket #2841. FP when using cast. // Different types => no error check("void f1(char *buf) {\n" " buf[4] = 0;\n" "}\n" "void f2() {\n" " int x[2];\n" " f1(x);\n" "}"); ASSERT_EQUALS("", errout.str()); // Same type => error check("void f1(const char buf[]) {\n" " char c = buf[4];\n" "}\n" "void f2() {\n" " char x[2];\n" " f1(x);\n" "}"); // TODO CTU ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array 'x[2]' accessed at index 4, which is out of bounds.\n", errout.str()); } void array_index_string_literal() { check("void f() {\n" " const char *str = \"abc\";\n" " bar(str[10]);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'str[4]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " const char *str = \"abc\";\n" " bar(str[4]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[4]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void f()\n" "{\n" " const char *str = \"abc\";\n" " bar(str[3]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " const char *str = \"a\tc\";\n" " bar(str[4]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[4]' accessed at index 4, which is out of bounds.\n", errout.str()); check("void f() {\n" // #6973 " const char *name = \"\";\n" " if ( name[0] == 'U' ? name[1] : 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char **argv) {\n" " char str[6] = \"\\0\";\n" " unsigned short port = 65535;\n" " snprintf(str, sizeof(str), \"%hu\", port);\n" "}", settings0, "test.c"); ASSERT_EQUALS("", errout.str()); } void array_index_same_struct_and_var_name() { // don't throw internal error check("struct tt {\n" " char name[21];\n" "} ;\n" "void doswitch(struct tt *x)\n" "{\n" " struct tt *tt=x;\n" " tt->name;\n" "}"); ASSERT_EQUALS("", errout.str()); // detect error check("struct tt {\n" " char name[21];\n" "} ;\n" "void doswitch(struct tt *x)\n" "{\n" " struct tt *tt=x;\n" " tt->name[22] = 123;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'tt->name[21]' accessed at index 22, which is out of bounds.\n", errout.str()); } void array_index_valueflow() { check("void f(int i) {\n" " char str[3];\n" " str[i] = 0;\n" " if (i==10) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'i==10' is redundant or the array 'str[3]' is accessed at index 10, which is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char str[3];\n" " str[i] = 0;\n" " switch (i) {\n" " case 10: break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning) Either the switch case 'case 10' is redundant or the array 'str[3]' is accessed at index 10, which is out of bounds.\n", errout.str()); check("void f() {\n" " char str[3];\n" " str[((unsigned char)3) - 1] = 0;\n" "}", false, "test.cpp"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #5416 FP " char *str[3];\n" " do_something(&str[0][5]);\n" "}", false, "test.cpp"); ASSERT_EQUALS("", errout.str()); check("class X { static const int x[100]; };\n" // #6070 "const int X::x[100] = {0};"); ASSERT_EQUALS("", errout.str()); check("namespace { class X { static const int x[100]; };\n" // #6232 "const int X::x[100] = {0}; }"); ASSERT_EQUALS("", errout.str()); check("class ActorSprite { static ImageSet * targetCursorImages[2][10]; };\n" "ImageSet *ActorSprite::targetCursorImages[2][10];\n"); ASSERT_EQUALS("", errout.str()); } void array_index_valueflow_pointer() { check("void f() {\n" " int a[10];\n" " int *p = a;\n" " p[20] = 0;\n" "}"); // TODO pointer ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Array 'a[10]' accessed at index 20, which is out of bounds.\n", errout.str()); { // address of check("void f() {\n" " int a[10];\n" " int *p = a;\n" " p[10] = 0;\n" "}"); // TODO pointer ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f() {\n" " int a[10];\n" " int *p = a;\n" " dostuff(&p[10]);\n" "}"); ASSERT_EQUALS("", errout.str()); } check("void f() {\n" " int a[X];\n" // unknown size " int *p = a;\n" " p[20] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[2];\n" " char *p = (char *)a;\n" // cast " p[4] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_function_parameter() { check("void f(char a[10]) {\n" " a[20] = 0;\n" // <- cppcheck warn here even though it's not a definite access out of bounds "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Array 'a[10]' accessed at index 20, which is out of bounds.\n", errout.str()); check("void f(char a[10]) {\n" // #6353 - reassign 'a' " a += 4;\n" " a[-1] = 0;\n" "}"); // TODO ASSERT_EQUALS("", errout.str()); } void array_index_enum_array() { // #8439 check("enum E : unsigned int { e1, e2 };\n" "void f() {\n" " E arrE[] = { e1, e2 };\n" " arrE[sizeof(arrE)] = e1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'arrE[2]' accessed at index 8, which is out of bounds.\n", errout.str()); } void array_index_container() { // #9386 check("constexpr int blockLen = 10;\n" "void foo(std::array& a) {\n" " a[2] = 2;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_2_struct() { check("struct ABC\n" "{\n" " char str[5];\n" "};\n" "\n" "static void f(struct ABC *abc)\n" "{\n" " strcpy( abc->str, \"abcdef\" );\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Buffer is accessed out of bounds: abc->str\n", errout.str()); check("struct ABC\n" "{\n" " char str[5];\n" "};\n" "\n" "static void f()\n" "{\n" " struct ABC abc;\n" " strcpy( abc.str, \"abcdef\" );\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Buffer is accessed out of bounds: abc.str\n", errout.str()); check("struct ABC\n" "{\n" " char str[5];\n" "};\n" "\n" "static void f(struct ABC &abc)\n" "{\n" " strcpy( abc.str, \"abcdef\" );\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Buffer is accessed out of bounds: abc.str\n", errout.str()); check("static void f()\n" "{\n" " struct ABC\n" " {\n" " char str[5];\n" " } abc;\n" " strcpy( abc.str, \"abcdef\" );\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: abc.str\n", errout.str()); check("static void f()\n" "{\n" " struct ABC\n" " {\n" " char str[5];\n" " };\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " strcpy( abc->str, \"abcdef\" );\n" " free(abc);\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Buffer is accessed out of bounds: abc->str\n", errout.str()); } void buffer_overrun_3() { check("int a[10];\n" "\n" "void foo()\n" "{\n" " int i;\n" " for (i = 0; i <= 10; ++i)\n" " a[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void buffer_overrun_4() { check("void foo()\n" "{\n" " const char *p[2];\n" " for (int i = 0; i < 8; ++i)\n" " p[i] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'p[2]' accessed at index 7, which is out of bounds.\n", errout.str()); // No false positive check("void foo(int x, int y)\n" "{\n" " const char *p[2];\n" " const char *s = y + p[1];\n" " p[1] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // There is no error here check("void f1(char *s,int size)\n" "{\n" " if( size > 10 ) strcpy(s,\"abc\");\n" "}\n" "void f2()\n" "{\n" " char s[3];\n" " f1(s,20);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout.str()); check("void f1(char *s,int size)\n" "{\n" " if( size > 10 ) strcpy(s,\"abc\");\n" "}\n" "void f2()\n" "{\n" " char s[3];\n" " f1(s,3);\n" "}\n", false); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_5() { check("void f()\n" "{\n" " char n[5];\n" " sprintf(n, \"d\");\n" " printf(\"hello!\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_6() { check("void f()\n" "{\n" " char n[5];\n" " strcat(n, \"abc\");\n" " strcat(n, \"def\");\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: n\n", errout.str()); } void buffer_overrun_7() { // ticket #731 check("void f()\n" "{\n" " char a[2];\n" " strcpy(a, \"a\\0\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_8() { // ticket #714 check("void f()\n" "{\n" " char a[5];\n" " for (int i = 0; i < 20; i = i + 100)\n" " {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char a[5];\n" " for (int i = 0; i < 20; i = 100 + i)\n" " {\n" " a[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_9() { // ticket #738 check("void f()\n" "{\n" " char a[5];\n" " for (int i = 0; i < 20; )\n" " {\n" " a[i] = 0;\n" " i += 100;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_10() { // ticket #740 check("void f()\n" "{\n" " char a[4];\n" " for (int i = 0; i < 4; i++)\n" " {\n" " char b = a[i];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_11() { check("void f()\n" "{\n" " char a[4];\n" " for (float i=0; i<10.0;i=i+0.1)\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char a[4];\n" " for (float i=0; i<10.0;i=0.1+i)\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_15() { // ticket #1787 check("class A : public B {\n" " char val[2];\n" " void f(int i, int ii);\n" "};\n" "void A::f(int i, int ii)\n" "{\n" " strcpy(val, \"ab\") ;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: val\n", errout.str()); } void buffer_overrun_16() { // unknown types check("void f() {\n" " struct Foo foo[5];\n" " memset(foo, 0, sizeof(foo));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // ticket #2093 " gchar x[3];\n" " strcpy(x, \"12\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("extern char a[10];\n" "void f() {\n" " char b[25] = {0};\n" " std::memcpy(b, a, 10);\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_18() { // ticket #2576 check("class A {\n" " void foo();\n" " bool b[7];\n" "};\n" "\n" "void A::foo() {\n" " for (int i=0; i<6; i++) {\n" " b[i] = b[i+1];\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " void foo();\n" " bool b[7];\n" "};\n" "\n" "void A::foo() {\n" " for (int i=0; i<7; i++) {\n" " b[i] = b[i+1];\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Array 'b[7]' accessed at index 7, which is out of bounds.\n", errout.str()); } void buffer_overrun_19() { // #2597 - class member with unknown type check("class A {\n" "public:\n" " u8 buf[10];\n" " A();" "};\n" "\n" "A::A() {\n" " memset(buf, 0, 10);\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_21() { check("void foo()\n" "{ { {\n" " char dst[4];\n" " const char *src = \"AAAAAAAAAAAAAAAAAAAAA\";\n" " for (size_t i = 0; i <= 4; i++)\n" " dst[i] = src[i];\n" "} } }\n"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'dst[4]' accessed at index 4, which is out of bounds.\n", errout.str()); } void buffer_overrun_24() { // index variable is changed in for-loop // ticket #4106 check("void main() {\n" " int array[] = {1,2};\n" " int x = 0;\n" " for( int i = 0; i<6; ) {\n" " x += array[i];\n" " i++; }\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // ticket #4096 check("void main() {\n" " int array[] = {1,2};\n" " int x = 0;\n" " for( int i = 0; i<6; ) {\n" " x += array[i++];\n" " }\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void buffer_overrun_26() { // ticket #4432 (segmentation fault) check("extern int split();\n" "void regress() {\n" " char inbuf[1000];\n" " char *f[10];\n" " split(inbuf, f, 10, \"\t\t\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_27() { // ticket #4444 (segmentation fault) check("void abc(struct foobar[5]);\n" "void main() {\n" "struct foobar x[5];\n" "abc(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } // #7083: false positive: typedef and initialization with strings void buffer_overrun_29() { check("typedef char testChar[10]; \n" "int main(){ \n" " testChar tc1 = \"\"; \n" " tc1[5]='a'; \n" "} \n" ); ASSERT_EQUALS("", errout.str()); } // #6367 void buffer_overrun_30() { check("struct S { int m[9]; };\n" "int f(S * s) {\n" " return s->m[sizeof(s->m)];\n" "}\n" ); ASSERT_EQUALS("[test.cpp:3]: (error) Array 's->m[9]' accessed at index 36, which is out of bounds.\n", errout.str()); } void buffer_overrun_31() { check("void f(WhereInfo *pWInfo, int *aiCur) {\n" " memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_errorpath() { setMultiline(); settings0.templateLocation = "{file}:{line}:note:{info}"; check("void f() {\n" " char *p = malloc(10);\n" " memset(p, 0, 20);\n" "}"); ASSERT_EQUALS("test.cpp:3:error:Buffer is accessed out of bounds: p\n" "test.cpp:2:note:Assign p, buffer with size 10\n" "test.cpp:3:note:Buffer overrun\n", errout.str()); } void buffer_overrun_bailoutIfSwitch() { // No false positive check("void f1(char *s) {\n" " if (x) s[100] = 0;\n" "}\n" "\n" "void f2() {\n" " char a[10];\n" " f1(a);" "}"); ASSERT_EQUALS("", errout.str()); // No false positive check("void f1(char *s) {\n" " if (x) return;\n" " s[100] = 0;\n" "}\n" "\n" "void f2() {\n" " char a[10];\n" " f1(a);" "}"); ASSERT_EQUALS("", errout.str()); // No false negative check("void f1(char *s) {\n" " if (x) { }\n" " s[100] = 0;\n" "}\n" "\n" "void f2() {\n" " char a[10];\n" " f1(a);" "}"); TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (error) Array 'a[10]' accessed at index 100, which is out of bounds.\n", "", errout.str()); } void buffer_overrun_function_array_argument() { check("void f(char a[10]);\n" "void g() {\n" " char a[2];\n" " f(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) The array 'a' is too small, the function 'f' expects a bigger one.\n", errout.str()); check("void f(float a[10][20]);\n" "void g() {\n" " float a[2][3];\n" " f(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) The array 'a' is too small, the function 'f' expects a bigger one.\n", errout.str()); check("void f(char a[20]);\n" "void g() {\n" " int a[2];\n" " f(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) The array 'a' is too small, the function 'f' expects a bigger one.\n", errout.str()); check("void f(char a[20]);\n" "void g() {\n" " int a[5];\n" " f(a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a[]) {\n" " switch (2) {\n" " case 1:\n" " a[1] = 1;\n" " }\n" "}\n" "int a[1];\n" "f(a);\n" ""); ASSERT_EQUALS("", errout.str()); check("void CreateLeafTex(unsigned char buf[256][2048][4]);\n" "void foo() {\n" " unsigned char(* tree)[2048][4] = new unsigned char[256][2048][4];\n" " CreateLeafTex(tree);\n" "}"); ASSERT_EQUALS("", errout.str()); } void possible_buffer_overrun_1() { // #3035 check("void foo() {\n" " char * data = alloca(50);\n" " char src[100];\n" " memset(src, 'C', 99);\n" " src[99] = '\\0';\n" " strcat(data, src);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (warning) Possible buffer overflow if strlen(src) is larger than sizeof(data)-strlen(data).\n", errout.str()); check("void foo() {\n" " char * data = alloca(100);\n" " char src[100];\n" " memset(src, 'C', 99);\n" " src[99] = '\\0';\n" " strcat(data, src);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char src[100]) {\n" " char * data = alloca(50);\n" " strcat(data, src);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Possible buffer overflow if strlen(src) is larger than sizeof(data)-strlen(data).\n", errout.str()); check("void foo(char src[100]) {\n" " char * data = alloca(100);\n" " strcat(data, src);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " char * data = alloca(50);\n" " char src[100];\n" " memset(src, 'C', 99);\n" " src[99] = '\\0';\n" " strcpy(data, src);\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (warning) Possible buffer overflow if strlen(src) is larger than or equal to sizeof(data).\n", errout.str()); check("void foo() {\n" " char * data = alloca(100);\n" " char src[100];\n" " memset(src, 'C', 99);\n" " src[99] = '\\0';\n" " strcpy(data, src);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char src[100]) {\n" " char * data = alloca(50);\n" " strcpy(data, src);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Possible buffer overflow if strlen(src) is larger than or equal to sizeof(data).\n", errout.str()); check("void foo(char src[100]) {\n" " char * data = alloca(100);\n" " strcpy(data, src);\n" "}"); ASSERT_EQUALS("", errout.str()); } void buffer_overrun_readSizeFromCfg() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); // Attempt to get size from Cfg files, no false positives if size is not specified check("void f() {\n" " u8 str[256];\n" " mystrcpy(str, \"abcd\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " u8 str[2];\n" " mystrcpy(str, \"abcd\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); // The same for structs, where the message comes from a different check check("void f() {\n" " struct { u8 str[256]; } ms;\n" " mystrcpy(ms.str, \"abcd\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct { u8 str[2]; } ms;\n" " mystrcpy(ms.str, \"abcd\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: ms.str\n", errout.str()); } void valueflow_string() { // using ValueFlow string values in checking check("char f() {\n" " const char *x = s;\n" " if (cond) x = \"abcde\";\n" " return x[20];\n" // <- array index out of bounds when x is "abcde" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'x[6]' accessed at index 20, which is out of bounds.\n", errout.str()); } void pointer_out_of_bounds_1() { check("void f() {\n" " char a[10];\n" " char *p = a + 100;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'a+100' is out of bounds.\n", errout.str()); check("void f() {\n" " char a[10];\n" " return a + 100;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'a+100' is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char x[10];\n" " if (i == 123) {}\n" " dostuff(x+i);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (portability) Undefined behaviour, when 'i' is 123 the pointer arithmetic 'x+i' is out of bounds.\n", errout.str()); check("void f() {\n" // #6350 - fp when there is cast of buffer " wchar_t buf[64];\n" " p = (unsigned char *) buf + sizeof (buf);\n" "}", false, "6350.c"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " const char d[] = \"0123456789\";\n" " char *cp = d + 3;\n" " return cp - d;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void pointer_out_of_bounds_2() { check("void f() {\n" " char *p = malloc(10);\n" " p += 100;\n" " free(p);" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'p+100' is out of bounds.\n", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " p += 10;\n" " *p = 0;\n" " free(p);" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) p is out of bounds.\n", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " p += 10;\n" " p -= 10;\n" " *p = 0;\n" " free(p);" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " p += 10;\n" " p = p - 1;\n" " *p = 0;\n" " free(p);" "}"); ASSERT_EQUALS("", errout.str()); } void pointer_out_of_bounds_3() { check("struct S { int a[10]; };\n" "void f(struct S *s) {\n" " char *p = s->a + 100;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 's->a+100' is out of bounds.\n", errout.str()); } void pointer_out_of_bounds_4() { check("const char* f() {\n" " g(\"Hello\" + 6);\n" "}"); ASSERT_EQUALS("", errout.str()); check("const char* f() {\n" " g(\"Hello\" + 7);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Undefined behaviour, pointer arithmetic '\"Hello\"+7' is out of bounds.\n", errout.str()); check("const char16_t* f() {\n" " g(u\"Hello\" + 6);\n" "}"); ASSERT_EQUALS("", errout.str()); check("const char16_t* f() {\n" " g(u\"Hello\" + 7);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Undefined behaviour, pointer arithmetic 'u\"Hello\"+7' is out of bounds.\n", errout.str()); } void pointer_out_of_bounds_sub() { check("void f() {\n" " char x[10];\n" " return x-1;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'x-1' is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char x[10];\n" " if (i == 123) {}\n" " dostuff(x-i);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) Undefined behaviour, when 'i' is 123 the pointer arithmetic 'x-i' is out of bounds.\n", errout.str()); check("void f(int i) {\n" " char x[10];\n" " if (i == -20) {}\n" " dostuff(x-i);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) Undefined behaviour, when 'i' is -20 the pointer arithmetic 'x-i' is out of bounds.\n", errout.str()); } void strncat1() { check("void f(char *a, char *b) {\n" " char str[16];\n" " strncpy(str, a, 10);\n" " strncat(str, b, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.\n", errout.str()); } void strncat2() { check("void f(char *a) {\n" " char str[5];\n" " strncat(str, a, 5);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.\n", errout.str()); } void strncat3() { check("void f(char *a) {\n" " char str[5];\n" " strncat(str, \"foobar\", 5);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.\n", errout.str()); } void strcat1() { check("struct Foo { char a[4]; };\n" "void f() {\n" " struct Foo x;\n" " strcat(x.a, \"aa\");\n" " strcat(x.a, \"aa\");\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds.\n", errout.str()); } void strcat2() { check("struct Foo { char a[5]; };\n" "void f() {\n" " struct Foo x;\n" " strcat(x.a, \"aa\");\n" " strcat(x.a, \"aa\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void strcat3() { check("void f() {\n" " INT str[10];\n" " strcat(str, \"aa\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void varid1() { check("void foo()\n" "{\n" " char str[10];\n" " if (str[0])\n" " {\n" " char str[50];\n" " str[30] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varid2() { // #4764 check("struct foo {\n" " void bar() { return; }\n" " type<> member[1];\n" "};"); ASSERT_EQUALS("", errout.str()); } void assign1() { check("char str[3] = {'a', 'b', 'c'};\n" "\n" "void foo()\n" "{\n" " str[3] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'str[3]' accessed at index 3, which is out of bounds.\n", errout.str()); } void alloc_new() { check("void foo()\n" "{\n" " char *s; s = new char[10];\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout.str()); // ticket #1670 - false negative when using return check("char f()\n" "{\n" " char *s; s = new int[10];\n" " return s[10];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("struct Fred { char c[10]; };\n" "char f()\n" "{\n" " Fred *f; f = new Fred;\n" " return f->c[10];\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'f->c[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("static const size_t MAX_SIZE = UNAVAILABLE_TO_CPPCHECK;\n" "struct Thing { char data[MAX_SIZE]; };\n" "char f4(const Thing& t) { return !t.data[0]; }"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " char * buf; buf = new char[8];\n" " buf[7] = 0;\n" " delete [] buf;\n" " buf = new char[9];\n" " buf[8] = 0;\n" " delete [] buf;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " char * buf; buf = new char[8];\n" " buf[7] = 0;\n" " delete [] buf;\n" " buf = new char[9];\n" " buf[9] = 0;\n" " delete [] buf;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Array 'buf[9]' accessed at index 9, which is out of bounds.\n", errout.str()); check("void foo()\n" "{\n" " enum E { Size = 10 };\n" " char *s; s = new char[Size];\n" " s[Size] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void foo()\n" "{\n" " enum E { };\n" " E *e; e = new E[10];\n" " e[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'e[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } // data is allocated with malloc void alloc_malloc() { check("void foo()\n" "{\n" " char *s; s = malloc(10);\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout.str()); // ticket #842 check("void f() {\n" " int *tab4 = malloc(20 * sizeof(int));\n" " tab4[20] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout.str()); // ticket #1478 check("void foo() {\n" " char *p = malloc(10);\n" " free(p);\n" " p = malloc(10);\n" " p[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Array 'p[10]' accessed at index 10, which is out of bounds.\n", errout.str()); // ticket #1134 check("void f() {\n" " int *x, i;\n" " x = malloc(10 * sizeof(int));\n" " x[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'x[10]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void f() {\n" " int *tab4; tab4 = malloc(20 * sizeof(int));\n" " tab4[19] = 0;\n" " free(tab4);\n" " tab4 = malloc(21 * sizeof(int));\n" " tab4[20] = 0;\n" " free(tab4);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *tab4 = malloc(20 * sizeof(int));\n" " tab4[19] = 0;\n" " tab4 = realloc(tab4,21 * sizeof(int));\n" " tab4[20] = 0;\n" " free(tab4);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum E { Size = 20 };\n" " E *tab4 = malloc(Size * 4);\n" " tab4[Size] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout.str()); check("void f() {\n" " enum E { Size = 20 };\n" " E *tab4 = malloc(4 * Size);\n" " tab4[Size] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout.str()); check("void f() {\n" " enum E { };\n" " E *tab4 = malloc(20 * sizeof(E));\n" " tab4[20] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout.str()); check("void f() {\n" // #8721 " unsigned char **cache = malloc(32);\n" " cache[i] = malloc(65536);\n" " cache[i][0xFFFF] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } // statically allocated buffer void alloc_string() { check("void foo()\n" "{\n" " const char *s = \"123\";\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[4]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void foo()\n" "{\n" " char *s; s = \"\";\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[1]' accessed at index 10, which is out of bounds.\n", errout.str()); check("void foo() {\n" " const char *s = \"\";\n" " s = y();\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" // #7718 "{\n" " std::string s = \"123\";\n" " s.resize(100);\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } // data is allocated with alloca void alloc_alloca() { check("void foo()\n" "{\n" " char *s = alloca(10);\n" " s[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } /* void countSprintfLength() const { std::list unknownParameter(1, nullptr); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("Hello", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("s", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("i", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%d", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%1d", unknownParameter)); ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("%2.2d", unknownParameter)); ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("%s", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("f%s", unknownParameter)); ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("%-s", unknownParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-5s", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("\\\"", unknownParameter)); ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("Hello \\0Text", unknownParameter)); ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("\\0", unknownParameter)); ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%%", unknownParameter)); ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("%d%d", unknownParameter)); ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("\\\\a%s\\0a", unknownParameter)); ASSERT_EQUALS(10, CheckBufferOverrun::countSprintfLength("\\\\\\\\Hello%d \\0Text\\\\\\\\", unknownParameter)); ASSERT_EQUALS(4, CheckBufferOverrun::countSprintfLength("%%%%%d", unknownParameter)); Token strTok; std::list stringAsParameter(1, &strTok); strTok.str("\"\""); ASSERT_EQUALS(4, CheckBufferOverrun::countSprintfLength("str%s", stringAsParameter)); strTok.str("\"12345\""); ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("str%s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-4s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-5s", stringAsParameter)); ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("%-6s", stringAsParameter)); ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%.4s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.5s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.6s", stringAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5.6s", stringAsParameter)); ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("%6.6s", stringAsParameter)); Token numTok; numTok.str("12345"); std::list intAsParameter(1, &numTok); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%02ld", intAsParameter)); ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%08ld", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.2d", intAsParameter)); ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%08.2d", intAsParameter)); TODO_ASSERT_EQUALS(5, 2, CheckBufferOverrun::countSprintfLength("%x", intAsParameter)); ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%4x", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5x", intAsParameter)); ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%.4x", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.5x", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%1.5x", intAsParameter)); ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5.1x", intAsParameter)); Token floatTok; floatTok.str("1.12345f"); std::list floatAsParameter(1, &floatTok); TODO_ASSERT_EQUALS(5, 3, CheckBufferOverrun::countSprintfLength("%.2f", floatAsParameter)); ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%8.2f", floatAsParameter)); TODO_ASSERT_EQUALS(5, 3, CheckBufferOverrun::countSprintfLength("%2.2f", floatAsParameter)); Token floatTok2; floatTok2.str("100.12345f"); std::list floatAsParameter2(1, &floatTok2); TODO_ASSERT_EQUALS(7, 3, CheckBufferOverrun::countSprintfLength("%2.2f", floatAsParameter2)); TODO_ASSERT_EQUALS(7, 3, CheckBufferOverrun::countSprintfLength("%.2f", floatAsParameter)); TODO_ASSERT_EQUALS(7, 5, CheckBufferOverrun::countSprintfLength("%4.2f", floatAsParameter)); std::list multipleParams = { &strTok, nullptr, &numTok }; ASSERT_EQUALS(15, CheckBufferOverrun::countSprintfLength("str%s%d%d", multipleParams)); ASSERT_EQUALS(26, CheckBufferOverrun::countSprintfLength("str%-6s%08ld%08ld", multipleParams)); } */ void minsize_argvalue() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); settings.addEnabled("warning"); settings.sizeof_wchar_t = 4; check("void f() {\n" " char c[10];\n" " mymemset(c, 0, 10);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c[10];\n" " mymemset(c, 0, 11);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); check("struct S {\n" " char a[5];\n" "};\n" "void f() {\n" " S s;\n" " mymemset(s.a, 0, 10);\n" "}", settings); ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: s.a\n", errout.str()); check("void foo() {\n" " char s[10];\n" " mymemset(s, 0, '*');\n" "}", settings); // TODO ASSERT_EQUALS("[test.cpp:3]: (warning) The size argument is given as a char constant.\n", errout.str()); // ticket #836 check("void f(void) {\n" " char a[10];\n" " mymemset(a+5, 0, 10);\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", "", errout.str()); // Ticket #909 check("void f(void) {\n" " char str[] = \"abcd\";\n" " mymemset(str, 0, 6);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("void f(void) {\n" " char str[] = \"abcd\";\n" " mymemset(str, 0, 5);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f(void) {\n" " wchar_t str[] = L\"abcd\";\n" " mymemset(str, 0, 21);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("void f(void) {\n" " wchar_t str[] = L\"abcd\";\n" " mymemset(str, 0, 20);\n" "}", settings); ASSERT_EQUALS("", errout.str()); // ticket #1659 - overflowing variable when using memcpy check("void f(void) { \n" " char c;\n" " mymemset(&c, 0, 4);\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", "", errout.str()); // ticket #2121 - buffer access out of bounds when using uint32_t check("void f(void) {\n" " unknown_type_t buf[4];\n" " mymemset(buf, 0, 100);\n" "}", settings); ASSERT_EQUALS("", errout.str()); // #3124 - multidimension array check("int main() {\n" " char b[5][6];\n" " mymemset(b, 0, 5 * 6);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("int main() {\n" " char b[5][6];\n" " mymemset(b, 0, 6 * 6);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: b\n", errout.str()); check("int main() {\n" " char b[5][6];\n" " mymemset(b, 0, 31);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: b\n", errout.str()); // #4968 - not standard function check("void f() {\n" " char str[3];\n" " foo.mymemset(str, 0, 100);\n" " foo::mymemset(str, 0, 100);\n" " std::mymemset(str, 0, 100);\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", "", errout.str()); // #5257 - check strings check("void f() {\n" " mymemset(\"abc\", 0, 20);\n" "}", settings); // TODO ASSERT_EQUALS("[test.cpp:2]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f() {\n" " mymemset(temp, \"abc\", 4);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #6816 - fp when array has known string value " char c[10] = \"c\";\n" " mymemset(c, 0, 10);\n" "}", settings); ASSERT_EQUALS("", errout.str()); } void minsize_sizeof() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); check("void f() {\n" " char c[7];\n" " mystrncpy(c, \"hello\", 7);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c[6];\n" " mystrncpy(c,\"hello\",6);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c[5];\n" " mystrncpy(c,\"hello\",6);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); check("void f() {\n" " char c[6];\n" " mystrncpy(c,\"hello!\",7);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); check("void f(unsigned int addr) {\n" " memset((void *)addr, 0, 1000);\n" "}", settings0); ASSERT_EQUALS("", errout.str()); check("struct AB { char a[10]; };\n" "void foo(AB *ab) {\n" " mystrncpy(x, ab->a, 100);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void a(char *p) { mystrncpy(p,\"hello world!\",10); }\n" // #3168 "void b() {\n" " char buf[5];\n" " a(buf);" "}", settings); // TODO CTU ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Buffer is accessed out of bounds: buf\n", errout.str()); } void minsize_strlen() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); // formatstr.. check("void f() {\n" " char str[3];\n" " mysprintf(str, \"test\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("void f() {\n" " char str[5];\n" " mysprintf(str, \"%s\", \"abcde\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("int getnumber();\n" "void f()\n" "{\n" " char str[5];\n" " mysprintf(str, \"%d: %s\", getnumber(), \"abcde\");\n" "}", settings); ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", errout.str()); check("void f() {\n" " char str[5];\n" " mysprintf(str, \"test%s\", \"\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char *str = new char[5];\n" " mysprintf(str, \"abcde\");\n" "}", settings); // TODO ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f(int condition) {\n" " char str[5];\n" " mysprintf(str, \"test%s\", condition ? \"12\" : \"34\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f(int condition) {\n" " char str[5];\n" " mysprintf(str, \"test%s\", condition ? \"12\" : \"345\");\n" "}", settings); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct Foo { char a[1]; };\n" "void f() {\n" " struct Foo x;\n" " mysprintf(x.a, \"aa\");\n" "}", settings); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: x.a\n", "", errout.str()); // ticket #900 check("void f() {\n" " char *a = new char(30);\n" " mysprintf(a, \"a\");\n" "}", settings); // TODO ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f(char value) {\n" " char *a = new char(value);\n" " mysprintf(a, \"a\");\n" "}", settings); // TODO ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); // This is out of bounds if 'sizeof(ABC)' is 1 (No padding) check("struct Foo { char a[1]; };\n" "void f() {\n" " struct Foo *x = malloc(sizeof(Foo));\n" " mysprintf(x.a, \"aa\");\n" "}", settings); // TODO ASSERT_EQUALS("", errout.str()); check("struct Foo { char a[1]; };\n" "void f() {\n" " struct Foo *x = malloc(sizeof(Foo) + 10);\n" " mysprintf(x.a, \"aa\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("struct Foo {\n" // #6668 - unknown size " char a[LEN];\n" " void f();\n" "};" "void Foo::f() {\n" " mysprintf(a, \"abcd\");\n" "}", settings); ASSERT_EQUALS("", errout.str()); } void minsize_mul() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); check("void f() {\n" " char c[5];\n" " myfread(c, 1, 5, stdin);\n" "}", settings); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char c[5];\n" " myfread(c, 1, 6, stdin);\n" "}", settings); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str()); } void unknownType() { check("void f()\n" "{\n" " UnknownType *a = malloc(4);\n" "}"); ASSERT_EQUALS("", errout.str()); } void terminateStrncpy1() { check("void foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " strncpy(baz, bar, 100);\n" " baz[99] = 0;\n" " strncpy(baz, bar, 100);\n" " baz[99] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " baz[99] = '\\0';\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " baz[x+1] = '\\0';\n" "}"); ASSERT_EQUALS("", errout.str()); // Test with invalid code that there is no segfault check("char baz[100];\n" "strncpy(baz, \"var\", 100)\n"); ASSERT_EQUALS("", errout.str()); // Test that there are no duplicate error messages check("void foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " foo(baz);\n" " foo(baz);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'baz' may not be null-terminated after the call to strncpy().\n", errout.str()); } void terminateStrncpy2() { check("char *foo ( char *bar ) {\n" " char baz[100];\n" " strncpy(baz, bar, 100);\n" " bar[99] = 0;\n" " return baz;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'baz' may not be null-terminated after the call to strncpy().\n", errout.str()); } void terminateStrncpy3() { // Ticket #2170 - false positive // The function bar is risky. But it might work that way intentionally. check("char str[100];\n" "\n" "void foo(char *a) {\n" " strncpy(str, a, 100);\n" "}\n" "\n" "void bar(char *p) {\n" " strncpy(p, str, 100);\n" "}\n", false); ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) The buffer 'str' may not be null-terminated after the call to strncpy().\n", errout.str()); } void terminateStrncpy4() { check("void bar() {\n" " char buf[4];\n" " strncpy(buf, \"ab\", 4);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void bar() {\n" " char buf[4];\n" " strncpy(buf, \"abcde\", 4);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'buf' may not be null-terminated after the call to strncpy().\n", errout.str()); } void recursive_long_time() { // Just test that recursive check doesn't take long time check("char *f2 ( char *b )\n" "{\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" " f2( b );\n" "}\n" "void f()\n" "{\n" " char a[10];\n" " f2(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Ticket #1587 - crash void crash1() { check("struct struct A\n" "{\n" " int alloclen;\n" "};\n" "\n" "void foo()\n" "{\n" " struct A *str;\n" " str = malloc(4);\n" "}"); ASSERT_EQUALS("", errout.str()); } void crash2() { check("void a(char *p) {\n" " f( { if(finally_arg); } );\n" "}\n" "\n" "void b() {\n" " char arr[64];\n" " a(arr);\n" "}"); } void crash3() { check("struct b { unknown v[0]; };\n" "void d() { struct b *f; f = malloc(108); }"); } void crash4() { // #8679 check("__thread void *thread_local_var; " "int main() { " " thread_local_var = malloc(1337); " " return 0; " "}"); check("thread_local void *thread_local_var; " "int main() { " " thread_local_var = malloc(1337); " " return 0; " "}"); } void crash5() { // 8644 - token has varId() but variable() is null check("int a() {\n" " void b(char **dst) {\n" " *dst = malloc(50);\n" " }\n" "}"); } void crash6() { check("void start(char* name) {\n" "char snapname[64] = { 0 }; \n" "strncpy(snapname, \"snapshot\", arrayLength(snapname)); \n" "}"); } void crash7() { // 9073 - [ has no astParent check("char x[10];\n" "void f() { x[10]; }"); } void insecureCmdLineArgs() { check("int main(int argc, char *argv[])\n" "{\n" " if(argc>1)\n" " {\n" " char buf[2];\n" " char *p = strdup(argv[1]);\n" " strcpy(buf,p);\n" " free(p);\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char *argv[])\n" "{\n" " if(argc>1)\n" " {\n" " char buf[2] = {'\\0','\\0'};\n" " char *p = strdup(argv[1]);\n" " strcat(buf,p);\n" " free(p);\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(const int argc, char* argv[])\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, const char* argv[])\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(const int argc, const char* argv[])\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char* argv[])\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, const char *const *const argv, char **envp)\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(const int argc, const char *const *const argv, const char *const *const envp)\n" "{\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(const int argc, const char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, const char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(const int argc, char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char **options)\n" "{\n" " char prog[10];\n" " strcpy(prog, options[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char **options)\n" "{\n" " char prog[10] = {'\\0'};\n" " strcat(prog, options[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char **options)\n" "{\n" " char prog[10];\n" " strcpy(prog, *options);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char **options)\n" "{\n" " char prog[10];\n" " strcpy(prog+3, *options);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10];\n" " if (strlen(argv[0]) < 10)\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10] = {'\\0'};\n" " if (10 > strlen(argv[0]))\n" " strcat(prog, argv[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char **argv, char **envp)\n" "{\n" " char prog[10];\n" " argv[0][0] = '\\0';\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); // #5835 check("int main(int argc, char* argv[]) {\n" " char prog[10];\n" " strcpy(prog, argv[0]);\n" " strcpy(prog, argv[0]);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer overrun possible for long command line arguments.\n" "[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", errout.str()); // #7964 check("int main(int argc, char *argv[]) {\n" " char *strcpy();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int main(int argc, char *argv[]) {\n" " char *strcat();\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkBufferAllocatedWithStrlen() { check("void f(char *a) {\n" " char *b = new char[strlen(a)];\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f(char *a) {\n" " char *b = new char[strlen(a) + 1];\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a) {\n" " char *b = new char[strlen(a)];\n" " a[0] = '\\0';\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a) {\n" " char *b = malloc(strlen(a));\n" " b = realloc(b, 10000);\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a) {\n" " char *b = malloc(strlen(a));\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f(char *a) {\n" " char *b = malloc(strlen(a));\n" " {\n" " strcpy(b, a);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f(char *a) {\n" " char *b = malloc(strlen(a) + 1);\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a, char *c) {\n" " char *b = realloc(c, strlen(a));\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); check("void f(char *a, char *c) {\n" " char *b = realloc(c, strlen(a) + 1);\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *a) {\n" " char *b = malloc(strlen(a));\n" " strcpy(b, a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str()); } void scope() { check("class A {\n" "private:\n" " struct X { char buf[10]; };\n" "}\n" "\n" "void f()\n" "{\n" " X x;\n" " x.buf[10] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A {\n" "public:\n" " struct X { char buf[10]; };\n" "}\n" "\n" "void f()\n" "{\n" " A::X x;\n" " x.buf[10] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Array 'x.buf[10]' accessed at index 10, which is out of bounds.\n", errout.str()); } void getErrorMessages() { // Ticket #2292: segmentation fault when using --errorlist CheckBufferOverrun c; c.getErrorMessages(this, nullptr); } void arrayIndexThenCheck() { check("void f(const char s[]) {\n" " if (s[i] == 'x' && i < y) {\n" " }" "}"); ASSERT_EQUALS("", errout.str()); // No message because i is unknown and thus gets no varid. Avoid an internalError here. check("void f(const char s[], int i) {\n" " if (s[i] == 'x' && i < y) {\n" " }" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(const char s[]) {\n" " for (int i = 0; s[i] == 'x' && i < y; ++i) {\n" " }" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(const int a[], unsigned i) {\n" " if((a[i] < 2) && (i <= 42)) {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(const int a[], unsigned i) {\n" " if((a[i] < 2) && (42 >= i)) {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(char* e, int y) {\n" " if (e[y] == '/' && elen > y + 1 && e[y + 1] == '?') {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const int a[], unsigned i) {\n" " if(a[i] < func(i) && i <= 42) {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(const int a[], unsigned i) {\n" " if (i <= 42 && a[i] < func(i)) {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const int a[], unsigned i) {\n" " if (foo(a[i] + 3) < func(i) && i <= 42) {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout.str()); check("void f(int i) {\n" // sizeof " sizeof(a)/sizeof(a[i]) && i < 10;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" // ?: " if ((i < 10 ? buf[i] : 1) && (i < 5 ? buf[i] : 5)){}\n" "}"); ASSERT_EQUALS("", errout.str()); } void bufferNotZeroTerminated() { check("void f() {\n" " char c[6];\n" " strncpy(c,\"hello!\",6);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' is not null-terminated after the call to strncpy().\n", errout.str()); check("void f() {\n" " char c[6];\n" " memcpy(c,\"hello!\",6);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' is not null-terminated after the call to memcpy().\n", errout.str()); check("void f() {\n" " char c[6];\n" " memmove(c,\"hello!\",6);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' is not null-terminated after the call to memmove().\n", errout.str()); } void negativeMemoryAllocationSizeError() { // #389 check("void f()\n" "{\n" " int *a;\n" " a = new int[-1];\n" " delete [] a;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", errout.str()); check("void f()\n" "{\n" " int *a;\n" " a = malloc( -10 );\n" " free(a);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", errout.str()); check("void f()\n" "{\n" " int *a;\n" " a = malloc( -10);\n" " free(a);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", errout.str()); check("void f()\n" "{\n" " int *a;\n" " a = alloca( -10 );\n" " free(a);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", errout.str()); } void negativeArraySize() { check("void f(int sz) {\n" // #1760 - VLA " int a[sz];\n" "}\n" "void x() { f(-100); }"); TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Declaration of array 'a' with negative size is undefined behaviour\n", "", errout.str()); // don't warn for constant sizes -> this is a compiler error so this is used for static assertions for instance check("int x, y;\n" "int a[-1];\n" "int b[x?1:-1];\n" "int c[x?y:-1];\n"); ASSERT_EQUALS("", errout.str()); } void pointerAddition1() { check("void f() {\n" " char arr[10];\n" " p = arr + 20;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'arr+20' is out of bounds.\n", errout.str()); } void ctu(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); CTU::FileInfo *ctu = CTU::getFileInfo(&tokenizer); // Check code.. std::list fileInfo; CheckBufferOverrun check(&tokenizer, &settings0, this); fileInfo.push_back(check.getFileInfo(&tokenizer, &settings0)); check.analyseWholeProgram(ctu, fileInfo, settings0, *this); while (!fileInfo.empty()) { delete fileInfo.back(); fileInfo.pop_back(); } delete ctu; } void ctu_malloc() { ctu("void dostuff(char *p) {\n" " p[-3] = 0;\n" "}\n" "\n" "int main() {\n" " char *s = malloc(4);\n" " dostuff(s);\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7] -> [test.cpp:2]: (error) Array index out of bounds; buffer 'p' is accessed at offset -3.\n", errout.str()); ctu("void dostuff(char *p) {\n" " p[4] = 0;\n" "}\n" "\n" "int main() {\n" " char *s = malloc(4);\n" " dostuff(s);\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 4.\n", errout.str()); } void ctu_array() { ctu("void dostuff(char *p) {\n" " p[10] = 0;\n" "}\n" "int main() {\n" " char str[4];\n" " dostuff(str);\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 10.\n", errout.str()); ctu("static void memclr( char *data )\n" "{\n" " data[10] = 0;\n" "}\n" "\n" "static void f()\n" "{\n" " char str[5];\n" " memclr( str );\n" "}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (error) Array index out of bounds; 'data' buffer size is 5 and it is accessed at offset 10.\n", errout.str()); ctu("static void memclr( int i, char *data )\n" "{\n" " data[10] = 0;\n" "}\n" "\n" "static void f()\n" "{\n" " char str[5];\n" " memclr( 0, str );\n" "}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (error) Array index out of bounds; 'data' buffer size is 5 and it is accessed at offset 10.\n", errout.str()); ctu("static void memclr( int i, char *data )\n" "{\n" " data[i] = 0;\n" "}\n" "\n" "static void f()\n" "{\n" " char str[5];\n" " memclr( 10, str );\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (possible error) Array index out of bounds.\n", "", errout.str()); // This is not an error ctu("static void memclr( char *data, int size )\n" "{\n" " if( size > 10 )" " data[10] = 0;\n" "}\n" "\n" "static void f()\n" "{\n" " char str[5];\n" " memclr( str, 5 );\n" "}"); ASSERT_EQUALS("", errout.str()); // #2097 ctu("void foo(int *p)\n" "{\n" " --p;\n" " p[2] = 0;\n" "}\n" "\n" "void bar()\n" "{\n" " int p[3];\n" " foo(p+1);\n" "}"); ASSERT_EQUALS("", errout.str()); // #9112 ctu("static void get_mac_address(const u8 *strbuf)\n" "{\n" " (strbuf[2]);\n" "}\n" "\n" "static void program_mac_address(u32 mem_base)\n" "{\n" " u8 macstrbuf[17] = { 0 };\n" " get_mac_address(macstrbuf);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ctu_variable() { ctu("void dostuff(int *p) {\n" " p[10] = 0;\n" "}\n" "int main() {\n" " int x = 4;\n" " dostuff(&x);\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 40.\n", errout.str()); } void ctu_arithmetic() { ctu("void dostuff(int *p) { x = p + 10; }\n" "int main() {\n" " int x[3];\n" " dostuff(x);\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Pointer arithmetic overflow; 'p' buffer size is 12\n", errout.str()); } void objectIndex() { check("int f() { \n" " int i;\n" " return (&i)[1]; \n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3]: (error) The address of local variable 'i' is accessed at non-zero index.\n", errout.str()); check("int f(int j) { \n" " int i;\n" " return (&i)[j]; \n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:3]: (warning) The address of local variable 'i' might be accessed at non-zero index.\n", errout.str()); check("int f() { \n" " int i;\n" " return (&i)[0]; \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(int * i) { \n" " return i[1]; \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector i) { \n" " return i[1]; \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector i) { \n" " return i.data()[1]; \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int* f(std::vector& i) { \n" " return &(i[1]); \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int i; int j; };\n" "int f() { \n" " A x;\n" " return (&x.i)[0]; \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int i; int j; };\n" "int f() { \n" " A x;\n" " int * i = &x.i;\n" " return i[0]; \n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestBufferOverrun) cppcheck-1.90/test/testcharvar.cpp000066400000000000000000000140301357737443600172550ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkother.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestCharVar : public TestFixture { public: TestCharVar() : TestFixture("TestCharVar") { } private: Settings settings; void run() OVERRIDE { settings.platform(Settings::Unspecified); settings.addEnabled("warning"); settings.addEnabled("portability"); TEST_CASE(array_index_1); TEST_CASE(array_index_2); TEST_CASE(bitop); } void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check char variable usage.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkCharVariable(); } void array_index_1() { check("int buf[256];\n" "void foo()\n" "{\n" " unsigned char ch = 0x80;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int buf[256];\n" "void foo()\n" "{\n" " char ch = 0x80;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (portability) 'char' type used as array index.\n", errout.str()); check("int buf[256];\n" "void foo()\n" "{\n" " char ch = 0;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int buf[256];\n" "void foo()\n" "{\n" " signed char ch = 0;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int buf[256];\n" "void foo()\n" "{\n" " char ch = 0x80;\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (portability) 'char' type used as array index.\n", errout.str()); check("int buf[256];\n" "void foo(signed char ch)\n" "{\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int buf[256];\n" "void foo(char ch)\n" "{\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* buf)\n" "{\n" " char ch = 0x80;" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) 'char' type used as array index.\n", errout.str()); check("void foo(char* buf)\n" "{\n" " char ch = 0;" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* buf)\n" "{\n" " buf['A'] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* buf, char ch)\n" "{\n" " buf[ch] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int flags[256];\n" "void foo(const char* str)\n" "{\n" " flags[*str] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int flags[256];\n" "void foo(const char* str)\n" "{\n" " flags[(unsigned char)*str] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const char str[])\n" "{\n" " map[str] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void array_index_2() { // #3282 - False positive check("void foo(char i);\n" "void bar(int i) {\n" " const char *s = \"abcde\";\n" " foo(s[i]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void bitop() { check("void foo(int *result) {\n" " signed char ch = -1;\n" " *result = a | ch;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout.str()); check("void foo(int *result) {\n" " unsigned char ch = -1;\n" " *result = a | ch;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *result) {\n" " signed char ch = -1;\n" " *result = a | ch;\n" "}"); ASSERT_EQUALS("", errout.str()); // 0x03 & .. check("void foo(int *result) {\n" " signed char ch = -1;\n" " *result = 0x03 | ch;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout.str()); check("void foo(int *result) {\n" " signed char ch = -1;\n" " *result = 0x03 & ch;\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestCharVar) cppcheck-1.90/test/testclass.cpp000066400000000000000000011017631357737443600167470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "checkclass.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestClass : public TestFixture { public: TestClass() : TestFixture("TestClass") { } private: Settings settings0; Settings settings1; void run() OVERRIDE { settings0.addEnabled("style"); settings1.addEnabled("warning"); // Load std.cfg configuration { const char xmldata[] = "\n" "\n" " \n" " malloc\n" " free\n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings0.library.load(doc); settings1.library.load(doc); } TEST_CASE(virtualDestructor1); // Base class not found => no error TEST_CASE(virtualDestructor2); // Base class doesn't have a destructor TEST_CASE(virtualDestructor3); // Base class has a destructor, but it's not virtual TEST_CASE(virtualDestructor4); // Derived class doesn't have a destructor => no error TEST_CASE(virtualDestructor5); // Derived class has empty destructor => no error TEST_CASE(virtualDestructor6); // only report error if base class pointer that points at derived class is deleted TEST_CASE(virtualDestructorProtected); TEST_CASE(virtualDestructorInherited); TEST_CASE(virtualDestructorTemplate); TEST_CASE(virtualDestructorInconclusive); // ticket # 5807 TEST_CASE(copyConstructor1); TEST_CASE(copyConstructor2); // ticket #4458 TEST_CASE(copyConstructor3); // defaulted/deleted TEST_CASE(copyConstructor4); // base class with private constructor TEST_CASE(noOperatorEq); // class with memory management should have operator eq TEST_CASE(noDestructor); // class with memory management should have destructor TEST_CASE(operatorEq1); TEST_CASE(operatorEq2); TEST_CASE(operatorEq3); // ticket #3051 TEST_CASE(operatorEq4); // ticket #3114 TEST_CASE(operatorEq5); // ticket #3296 TEST_CASE(operatorEqRetRefThis1); TEST_CASE(operatorEqRetRefThis2); // ticket #1323 TEST_CASE(operatorEqRetRefThis3); // ticket #1405 TEST_CASE(operatorEqRetRefThis4); // ticket #1451 TEST_CASE(operatorEqRetRefThis5); // ticket #1550 TEST_CASE(operatorEqRetRefThis6); // ticket #2479 TEST_CASE(operatorEqRetRefThis7); // ticket #5782 endless recursion TEST_CASE(operatorEqToSelf1); // single class TEST_CASE(operatorEqToSelf2); // nested class TEST_CASE(operatorEqToSelf3); // multiple inheritance TEST_CASE(operatorEqToSelf4); // nested class with multiple inheritance TEST_CASE(operatorEqToSelf5); // ticket # 1233 TEST_CASE(operatorEqToSelf6); // ticket # 1550 TEST_CASE(operatorEqToSelf7); TEST_CASE(operatorEqToSelf8); // ticket #2179 TEST_CASE(operatorEqToSelf9); // ticket #2592 TEST_CASE(memsetOnStruct); TEST_CASE(memsetVector); TEST_CASE(memsetOnClass); TEST_CASE(memsetOnInvalid); // Ticket #5425: Crash upon invalid TEST_CASE(memsetOnStdPodType); // Ticket #5901 - std::uint8_t TEST_CASE(memsetOnFloat); // Ticket #5421 TEST_CASE(memsetOnUnknown); // Ticket #7183 TEST_CASE(mallocOnClass); TEST_CASE(this_subtraction); // warn about "this-x" // can member function be made const TEST_CASE(const1); TEST_CASE(const2); TEST_CASE(const3); TEST_CASE(const4); TEST_CASE(const5); // ticket #1482 TEST_CASE(const6); // ticket #1491 TEST_CASE(const7); TEST_CASE(const8); // ticket #1517 TEST_CASE(const9); // ticket #1515 TEST_CASE(const10); // ticket #1522 TEST_CASE(const11); // ticket #1529 TEST_CASE(const12); // ticket #1552 TEST_CASE(const13); // ticket #1519 TEST_CASE(const14); TEST_CASE(const15); TEST_CASE(const16); // ticket #1551 TEST_CASE(const17); // ticket #1552 TEST_CASE(const18); TEST_CASE(const19); // ticket #1612 TEST_CASE(const20); // ticket #1602 TEST_CASE(const21); // ticket #1683 TEST_CASE(const22); TEST_CASE(const23); // ticket #1699 TEST_CASE(const24); // ticket #1708 TEST_CASE(const25); // ticket #1724 TEST_CASE(const26); // ticket #1847 TEST_CASE(const27); // ticket #1882 TEST_CASE(const28); // ticket #1883 TEST_CASE(const29); // ticket #1922 TEST_CASE(const30); TEST_CASE(const31); TEST_CASE(const32); // ticket #1905 - member array is assigned TEST_CASE(const33); TEST_CASE(const34); // ticket #1964 TEST_CASE(const35); // ticket #2001 TEST_CASE(const36); // ticket #2003 TEST_CASE(const37); // ticket #2081 and #2085 TEST_CASE(const38); // ticket #2135 TEST_CASE(const39); TEST_CASE(const40); // ticket #2228 TEST_CASE(const41); // ticket #2255 TEST_CASE(const42); // ticket #2282 TEST_CASE(const43); // ticket #2377 TEST_CASE(const44); // ticket #2595 TEST_CASE(const45); // ticket #2664 TEST_CASE(const46); // ticket #2636 TEST_CASE(const47); // ticket #2670 TEST_CASE(const48); // ticket #2672 TEST_CASE(const49); // ticket #2795 TEST_CASE(const50); // ticket #2943 TEST_CASE(const51); // ticket #3040 TEST_CASE(const52); // ticket #3048 TEST_CASE(const53); // ticket #3049 TEST_CASE(const54); // ticket #3052 TEST_CASE(const55); TEST_CASE(const56); // ticket #3149 TEST_CASE(const57); // tickets #2669 and #2477 TEST_CASE(const58); // ticket #2698 TEST_CASE(const59); // ticket #4646 TEST_CASE(const60); // ticket #3322 TEST_CASE(const61); // ticket #5606 TEST_CASE(const62); // ticket #5701 TEST_CASE(const63); // ticket #5983 TEST_CASE(const64); // ticket #6268 TEST_CASE(const65); // ticket #8693 TEST_CASE(const66); // ticket #7714 TEST_CASE(const67); // ticket #9193 TEST_CASE(const_handleDefaultParameters); TEST_CASE(const_passThisToMemberOfOtherClass); TEST_CASE(assigningPointerToPointerIsNotAConstOperation); TEST_CASE(assigningArrayElementIsNotAConstOperation); TEST_CASE(constoperator1); // operator< can often be const TEST_CASE(constoperator2); // operator<< TEST_CASE(constoperator3); TEST_CASE(constoperator4); TEST_CASE(constoperator5); // ticket #3252 TEST_CASE(constoperator6); // ticket #8669 TEST_CASE(constincdec); // increment/decrement => non-const TEST_CASE(constassign1); TEST_CASE(constassign2); TEST_CASE(constincdecarray); // increment/decrement array element => non-const TEST_CASE(constassignarray); TEST_CASE(constReturnReference); TEST_CASE(constDelete); // delete member variable => not const TEST_CASE(constLPVOID); // a function that returns LPVOID can't be const TEST_CASE(constFunc); // a function that calls const functions can be const TEST_CASE(constVirtualFunc); TEST_CASE(constIfCfg); // ticket #1881 - fp when there are #if TEST_CASE(constFriend); // ticket #1921 - fp for friend function TEST_CASE(constUnion); // ticket #2111 - fp when there is a union TEST_CASE(constArrayOperator); // #4406 TEST_CASE(constRangeBasedFor); // #5514 TEST_CASE(const_shared_ptr); TEST_CASE(constPtrToConstPtr); TEST_CASE(initializerListOrder); TEST_CASE(initializerListUsage); TEST_CASE(selfInitialization); TEST_CASE(virtualFunctionCallInConstructor); TEST_CASE(pureVirtualFunctionCall); TEST_CASE(pureVirtualFunctionCallOtherClass); TEST_CASE(pureVirtualFunctionCallWithBody); TEST_CASE(pureVirtualFunctionCallPrevented); TEST_CASE(duplInheritedMembers); TEST_CASE(explicitConstructors); TEST_CASE(copyCtorAndEqOperator); TEST_CASE(override1); TEST_CASE(overrideCVRefQualifiers); TEST_CASE(unsafeClassRefMember); } void checkCopyCtorAndEqOperator(const char code[]) { // Clear the error log errout.str(""); Settings settings; settings.addEnabled("warning"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.checkCopyCtorAndEqOperator(); } void copyCtorAndEqOperator() { checkCopyCtorAndEqOperator("class A \n" "{ \n" " A(const A& other) { } \n" " A& operator=(const A& other) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A \n" "{ \n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A \n" "{ \n" " A(const A& other) { } \n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A \n" "{ \n" " A& operator=(const A& other) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A \n" "{ \n" " A(const A& other) { } \n" " int x;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:1]: (warning) The class 'A' has 'copy constructor' but lack of 'operator='.\n", "", errout.str()); // TODO the error message should be clarified. It should say something like 'copy constructor is empty and will not assign i and therefore the behaviour is different to the default assignment operator' checkCopyCtorAndEqOperator("class A \n" "{ \n" " A& operator=(const A& other) { return *this; }\n" " int x;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:1]: (warning) The class 'A' has 'operator=' but lack of 'copy constructor'.\n", "", errout.str()); // TODO the error message should be clarified. It should say something like 'assignment operator does not assign i and therefore the behaviour is different to the default copy constructor' checkCopyCtorAndEqOperator("class A \n" "{ \n" " A& operator=(const int &x) { this->x = x; return *this; }\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyCtorAndEqOperator("class A {\n" "public:\n" " A() : x(0) { }\n" " A(const A & a) { x = a.x; }\n" " A & operator = (const A & a) {\n" " x = a.x;\n" " return *this;\n" " }\n" "private:\n" " int x;\n" "};\n" "class B : public A {\n" "public:\n" " B() { }\n" " B(const B & b) :A(b) { }\n" "private:\n" " static int i;\n" "};"); ASSERT_EQUALS("", errout.str()); // #7987 - Don't show warning when there is a move constructor checkCopyCtorAndEqOperator("struct S {\n" " std::string test;\n" " S(S&& s) : test(std::move(s.test)) { }\n" " S& operator = (S &&s) {\n" " test = std::move(s.test);\n" " return *this;\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); // #8337 - False positive in copy constructor detection checkCopyCtorAndEqOperator("struct StaticListNode {\n" " StaticListNode(StaticListNode*& prev) : m_next(0) {}\n" " StaticListNode* m_next;\n" "};"); ASSERT_EQUALS("", errout.str()); } void checkExplicitConstructors(const char code[]) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); checkClass.checkExplicitConstructors(); } void explicitConstructors() { checkExplicitConstructors("class Class {\n" " Class() = delete;\n" " Class(const Class& other) { }\n" " Class(Class&& other) { }\n" " explicit Class(int i) { }\n" " explicit Class(const std::string&) { }\n" " Class(int a, int b) { }\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " Class() = delete;\n" " explicit Class(const Class& other) { }\n" " explicit Class(Class&& other) { }\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " Class() = delete;\n" " Class(const Class& other) = delete;\n" " Class(Class&& other) = delete;\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " Class(int i) { }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (style) Class 'Class' has a constructor with 1 argument that is not explicit.\n", errout.str()); checkExplicitConstructors("class Class {\n" " Class(const Class& other) { }\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " Class(Class&& other) { }\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); // #6585 checkExplicitConstructors("class Class {\n" " private: Class(const Class&);\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("class Class {\n" " public: Class(const Class&);\n" " virtual int i() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); // #7465: Error properly reported in templates checkExplicitConstructors("template struct Test {\n" " Test(int) : fData(0) {}\n" " T fData;\n" "};\n" "int main() {\n" " Test test;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Struct 'Test < int >' has a constructor with 1 argument that is not explicit.\n", errout.str()); // #7465: No error for copy or move constructors checkExplicitConstructors("template struct Test {\n" " Test() : fData(0) {}\n" " Test (const Test& aOther) : fData(aOther.fData) {}\n" " Test (Test&& aOther) : fData(std::move(aOther.fData)) {}\n" " T fData;\n" "};\n" "int main() {\n" " Test test;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #8600 checkExplicitConstructors("struct A { struct B; };\n" "struct A::B {\n" " B() = default;\n" " B(const B&) {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkExplicitConstructors("struct A{" " A(int, int y=2) {}" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) Struct 'A' has a constructor with 1 argument that is not explicit.\n", errout.str()); } void checkDuplInheritedMembers(const char code[]) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings1, this); checkClass.checkDuplInheritedMembers(); } void duplInheritedMembers() { checkDuplInheritedMembers("class Base {\n" " int x;\n" "};\n" "struct Derived : Base {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkDuplInheritedMembers("class Base {\n" " protected:\n" " int x;\n" "};\n" "struct Derived : Base {\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base'.\n", errout.str()); checkDuplInheritedMembers("class Base {\n" " protected:\n" " int x;\n" "};\n" "struct Derived : public Base {\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base'.\n", errout.str()); checkDuplInheritedMembers("class Base0 {\n" " int x;\n" "};\n" "class Base1 {\n" " int x;\n" "};\n" "struct Derived : Base0, Base1 {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkDuplInheritedMembers("class Base0 {\n" " protected:\n" " int x;\n" "};\n" "class Base1 {\n" " int x;\n" "};\n" "struct Derived : Base0, Base1 {\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:9]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base0'.\n", errout.str()); checkDuplInheritedMembers("class Base0 {\n" " protected:\n" " int x;\n" "};\n" "class Base1 {\n" " public:\n" " int x;\n" "};\n" "struct Derived : Base0, Base1 {\n" " int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:10]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base0'.\n" "[test.cpp:7] -> [test.cpp:10]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base1'.\n", errout.str()); checkDuplInheritedMembers("class Base {\n" " int x;\n" "};\n" "struct Derived : Base {\n" " int y;\n" "};"); ASSERT_EQUALS("", errout.str()); checkDuplInheritedMembers("class A {\n" " int x;\n" "};\n" "struct B {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); // Unknown 'Base' class checkDuplInheritedMembers("class Derived : public UnknownBase {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkDuplInheritedMembers("class Base {\n" " int x;\n" "};\n" "class Derived : public Base {\n" "};"); ASSERT_EQUALS("", errout.str()); // #6692 checkDuplInheritedMembers("namespace test1 {\n" " struct SWibble{};\n" " typedef SWibble wibble;\n" "}\n" "namespace test2 {\n" " struct SWibble : public test1::wibble {\n" " int Value;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkCopyConstructor(const char code[]) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); checkClass.copyconstructors(); } void copyConstructor1() { checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c,*p,*d;\n" " F(const F &f) : p(f.p), c(f.c)\n" " {\n" " p=(char *)malloc(strlen(f.p)+1);\n" " strcpy(p,f.p);\n" " }\n" " F(char *str)\n" " {\n" " p=(char *)malloc(strlen(str)+1);\n" " strcpy(p,str);\n" " }\n" " F&operator=(const F&);\n" " ~F();\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n", errout.str()); checkCopyConstructor("class F {\n" " char *p;\n" " F(const F &f) {\n" " p = f.p;\n" " }\n" " F(char *str) {\n" " p = malloc(strlen(str)+1);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" "[test.cpp:3] -> [test.cpp:7]: (warning) Copy constructor does not allocate memory for member 'p' although memory has been allocated in other constructors.\n", "[test.cpp:4]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" , errout.str()); checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c,*p,*d;\n" " F(const F &f) :p(f.p)\n" " {\n" " }\n" " F(char *str)\n" " {\n" " p=(char *)malloc(strlen(str)+1);\n" " strcpy(p,str);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:5]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" "[test.cpp:5] -> [test.cpp:10]: (warning) Copy constructor does not allocate memory for member 'p' although memory has been allocated in other constructors.\n", "[test.cpp:5]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" , errout.str()); checkCopyConstructor("class kalci\n" "{\n" " public:\n" " char *c,*p,*d;\n" " kalci()\n" " {\n" " p=(char *)malloc(100);\n" " strcpy(p,\"hello\");\n" " c=(char *)malloc(100);\n" " strcpy(p,\"hello\");\n" " d=(char *)malloc(100);\n" " strcpy(p,\"hello\");\n" " }\n" " kalci(const kalci &f)\n" " {\n" " p=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " c=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " d=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " }\n" " ~kalci();\n" " kalci& operator=(const kalci&kalci);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c,*p,*d;\n" " F(char *str,char *st,char *string)\n" " {\n" " p=(char *)malloc(100);\n" " strcpy(p,str);\n" " c=(char *)malloc(100);\n" " strcpy(p,st);\n" " d=(char *)malloc(100);\n" " strcpy(p,string);\n" " }\n" " F(const F &f)\n" " {\n" " p=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " c=(char *)malloc(strlen(str)+1);\n" " strcpy(p,f.p);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:14] -> [test.cpp:11]: (warning) Copy constructor does not allocate memory for member 'd' although memory has been allocated in other constructors.\n", "", errout.str()); checkCopyConstructor("class F {\n" " char *c;\n" " F(char *str,char *st,char *string) {\n" " p=(char *)malloc(100);\n" " }\n" " F(const F &f)\n" " : p(malloc(size))\n" " {\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F {\n" " char *c;\n" " F(char *str,char *st,char *string)\n" " : p(malloc(size))\n" " {\n" " }\n" " F(const F &f)\n" " {\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (warning) Copy constructor does not allocate memory for member 'd' although memory has been allocated in other constructors.\n", "", errout.str()); checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c,*p,*d;\n" " F()\n" " {\n" " p=(char *)malloc(100);\n" " c=(char *)malloc(100);\n" " d=(char*)malloc(100);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); checkCopyConstructor("class F\n" "{\n" " public:\n" " char *c;\n" " const char *p,*d;\n" " F(char *str,char *st,char *string)\n" " {\n" " p=str;\n" " d=st;\n" " c=(char *)malloc(strlen(string)+1);\n" " strcpy(d,string);\n" " }\n" " F(const F &f)\n" " {\n" " p=f.p;\n" " d=f.d;\n" " c=(char *)malloc(strlen(str)+1);\n" " strcpy(d,f.p);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F : E\n" "{\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class E { E(E&); };\n" // non-copyable "class F : E\n" "{\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class E {};\n" "class F : E {\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); checkCopyConstructor("class F {\n" " char *p;\n" " F() {\n" " p = malloc(100);\n" " }\n" " F(F& f);\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("class F {\n" " char *p;\n" " F() : p(malloc(100)) {}\n" " ~F();\n" " F& operator=(const F&f);\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); // #7198 checkCopyConstructor("struct F {\n" " static char* c;\n" " F() {\n" " p = malloc(100);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void copyConstructor2() { // ticket #4458 checkCopyConstructor("template \n" "class Vector\n" "{\n" "public:\n" " Vector() {\n" " _M_finish = new _Tp[ 42 ];\n" " }\n" " Vector( const Vector<_Tp>& v ) {\n" " }\n" " ~Vector();\n" " Vector& operator=(const Vector&v);\n" " _Tp* _M_finish;\n" "};"); ASSERT_EQUALS("", errout.str()); } void copyConstructor3() { checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f) = delete;\n" " F&operator=(const F &f);\n" " ~F();\n" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f) = default;\n" " F&operator=(const F &f);\n" " ~F();\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The copy constructor is explicitly defaulted but the default copy constructor does not work well. It is recommended to define or delete the copy constructor.\n", errout.str()); } void copyConstructor4() { checkCopyConstructor("class noncopyable {\n" "protected:\n" " noncopyable() {}\n" " ~noncopyable() {}\n" "\n" "private:\n" " noncopyable( const noncopyable& );\n" " const noncopyable& operator=( const noncopyable& );\n" "};\n" "\n" "class Base : private noncopyable {};\n" "\n" "class Foo : public Base {\n" "public:\n" " Foo() : m_ptr(new int) {}\n" " ~Foo() { delete m_ptr; }\n" "private:\n" " int* m_ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noOperatorEq() { checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " ~F();\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a operator= which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); // defaulted operator= checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F &operator=(const F &f) = default;\n" " ~F();\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The operator= is explicitly defaulted but the default operator= does not work well. It is recommended to define or delete the operator=.\n", errout.str()); // deleted operator= checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F &operator=(const F &f) = delete;\n" " ~F();\n" "};"); ASSERT_EQUALS("", errout.str()); // base class deletes operator= checkCopyConstructor("struct F : NonCopyable {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " ~F();\n" "};"); ASSERT_EQUALS("", errout.str()); } void noDestructor() { checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F&operator=(const F&);" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); checkCopyConstructor("struct F {\n" " C* c;\n" " F() { c = new C; }\n" " F(const F &f);\n" " F&operator=(const F&);" "};"); ASSERT_EQUALS("", errout.str()); checkCopyConstructor("struct F {\n" " int* i;\n" " F() { i = new int(); }\n" " F(const F &f);\n" " F& operator=(const F&);" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); checkCopyConstructor("struct Data { int x; int y; };\n" "struct F {\n" " Data* c;\n" " F() { c = new Data; }\n" " F(const F &f);\n" " F&operator=(const F&);" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout.str()); // defaulted destructor checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F &operator=(const F &f);\n" " ~F() = default;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The destructor is explicitly defaulted but the default destructor does not work well. It is recommended to define the destructor.\n", errout.str()); // deleted destructor checkCopyConstructor("struct F {\n" " char* c;\n" " F() { c = malloc(100); }\n" " F(const F &f);\n" " F &operator=(const F &f);\n" " ~F() = delete;\n" "};"); ASSERT_EQUALS("", errout.str()); } // Check the operator Equal void checkOpertorEq(const char code[]) { // Clear the error log errout.str(""); settings0.inconclusive = true; // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); checkClass.operatorEq(); } void operatorEq1() { checkOpertorEq("class A\n" "{\n" "public:\n" " void goo() {}" " void operator=(const A&);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'A::operator=' should return 'A &'.\n", errout.str()); checkOpertorEq("class A\n" "{\n" "public:\n" " void goo() {}" " void operator=(const A&)=delete;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEq("class A\n" "{\n" "public:\n" " void goo() {}" " void operator=(A&);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'A::operator=' should return 'A &'.\n", errout.str()); checkOpertorEq("class A\n" "{\n" "private:\n" " void operator=(const A&);\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEq("class A\n" "{\n" "protected:\n" " void operator=(const A&);\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEq("class A\n" "{\n" "private:\n" " void operator=(const A&)=delete;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEq("class A\n" "{\n" " void operator=(const A&);\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEq("class A\n" "{\n" "public:\n" " void goo() {}\n" "private:\n" " void operator=(const A&);\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEq("class A\n" "{\n" "public:\n" " void operator=(const A&);\n" "};\n" "class B\n" "{\n" "public:\n" " void operator=(const B&);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'A::operator=' should return 'A &'.\n" "[test.cpp:9]: (style) 'B::operator=' should return 'B &'.\n", errout.str()); checkOpertorEq("struct A\n" "{\n" " void operator=(const A&);\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'A::operator=' should return 'A &'.\n", errout.str()); checkOpertorEq("struct A\n" "{\n" " void operator=(const A&)=delete;\n" "};"); ASSERT_EQUALS("", errout.str()); // Ticket #7017 checkOpertorEq("template struct X {\n" " inline X(const X& Rhs);\n" " inline X& operator =(const X& Rhs);\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEq2() { checkOpertorEq("class A\n" "{\n" "public:\n" " void * operator=(const A&);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'A::operator=' should return 'A &'.\n", errout.str()); checkOpertorEq("class A\n" "{\n" "public:\n" " A * operator=(const A&);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'A::operator=' should return 'A &'.\n", errout.str()); checkOpertorEq("class A\n" "{\n" "public:\n" " const A & operator=(const A&);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'A::operator=' should return 'A &'.\n", errout.str()); checkOpertorEq("class A\n" "{\n" "public:\n" " B & operator=(const A&);\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'A::operator=' should return 'A &'.\n", errout.str()); } void operatorEq3() { // ticket #3051 checkOpertorEq("class A\n" "{\n" "public:\n" " A * operator=(const A*);\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEq4() { // ticket #3114 (infinite loop) checkOpertorEq("struct A {\n" " A& operator=(A const& a) { return operator=(&a); }\n" " A& operator=(const A*) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEq5() { // ticket #3296 (virtual operator) checkOpertorEq( "class A {\n" " virtual A& operator=(const A &a) {return *this};\n" "};"); ASSERT_EQUALS("", errout.str()); } // Check that operator Equal returns reference to this void checkOpertorEqRetRefThis(const char code[]) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); checkClass.operatorEqRetRefThis(); } void operatorEqRetRefThis1() { checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &a) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &a) { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { return *this; }"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a) { return *this; }"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { return a; }"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a) { return a; }"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &b) { return *this; }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &b) { return b; }\n" " };\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { return *this; }"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { return b; }"); ASSERT_EQUALS("[test.cpp:10]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class B;\n" "};\n" "class A::B\n" "{\n" " B & operator=(const B & b) { return b; }\n" "};\n"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class B;\n" "};\n" "class A::B\n" "{\n" " B & operator=(const B &);\n" "};\n" "A::B & A::B::operator=(const A::B & b) { return b; }\n"); ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class B;\n" "};\n" "class A::B\n" "{\n" " A::B & operator=(const A::B & b) { return b; }\n" "};\n"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class B;\n" "};\n" "class A::B\n" "{\n" " A::B & operator=(const A::B &);\n" "};\n" "A::B & A::B::operator=(const A::B & b) { return b; }\n"); ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "namespace A {\n" " class B;\n" "}\n" "class A::B\n" "{\n" " B & operator=(const B & b) { return b; }\n" "};\n"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "namespace A {\n" " class B;\n" "}\n" "class A::B\n" "{\n" " B & operator=(const B &);\n" "};\n" "A::B & A::B::operator=(const A::B & b) { return b; }\n"); ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "namespace A {\n" " class B;\n" "}\n" "class A::B\n" "{\n" " A::B & operator=(const A::B & b) { return b; }\n" "};\n"); ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "namespace A {\n" " class B;\n" "}\n" "class A::B\n" "{\n" " A::B & operator=(const A::B &);\n" "};\n" "A::B & A::B::operator=(const A::B & b) { return b; }\n"); ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); } void operatorEqRetRefThis2() { // ticket # 1323 checkOpertorEqRetRefThis( "class szp\n" "{\n" " szp &operator =(int *other) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class szp\n" "{\n" " szp &operator =(int *other);\n" "};\n" "szp &szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "namespace NS {\n" " class szp;\n" "}\n" "class NS::szp\n" "{\n" " szp &operator =(int *other) {}\n" "};\n"); ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "namespace NS {\n" " class szp;\n" "}\n" "class NS::szp\n" "{\n" " szp &operator =(int *other);\n" "};\n" "NS::szp &NS::szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "namespace NS {\n" " class szp;\n" "}\n" "class NS::szp\n" "{\n" " NS::szp &operator =(int *other) {}\n" "};\n"); ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "namespace NS {\n" " class szp;\n" "}\n" "class NS::szp\n" "{\n" " NS::szp &operator =(int *other);\n" "};\n" "NS::szp &NS::szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class szp;\n" "};\n" "class A::szp\n" "{\n" " szp &operator =(int *other) {}\n" "};\n"); ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class szp;\n" "};\n" "class A::szp\n" "{\n" " szp &operator =(int *other);\n" "};\n" "A::szp &A::szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class szp;\n" "};\n" "class A::szp\n" "{\n" " A::szp &operator =(int *other) {}\n" "};\n"); ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" " class szp;\n" "};\n" "class A::szp\n" "{\n" " A::szp &operator =(int *other);\n" "};\n" "A::szp &A::szp::operator =(int *other) {}"); ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); } void operatorEqRetRefThis3() { // ticket # 1405 checkOpertorEqRetRefThis( "class A {\n" "public:\n" " inline A &operator =(int *other) { return (*this); };\n" " inline A &operator =(long *other) { return (*this = 0); };\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A &operator =(int *other);\n" " A &operator =(long *other);\n" "};\n" "A &A::operator =(int *other) { return (*this); };\n" "A &A::operator =(long *other) { return (*this = 0); };"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " inline A &operator =(int *other) { return (*this); };\n" " inline A &operator =(long *other) { return operator = (*(int *)other); };\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A &operator =(int *other);\n" " A &operator =(long *other);\n" "};\n" "A &A::operator =(int *other) { return (*this); };\n" "A &A::operator =(long *other) { return operator = (*(int *)other); };"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A &operator =(int *other);\n" " A &operator =(long *other);\n" "};\n" "A &A::operator =(int *other) { return (*this); };\n" "A &A::operator =(long *other) { return this->operator = (*(int *)other); };"); ASSERT_EQUALS("", errout.str()); checkOpertorEqRetRefThis( // #9045 "class V {\n" "public:\n" " V& operator=(const V& r) {\n" " if (this == &r) {\n" " return ( *this );\n" " }\n" " return *this;\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void operatorEqRetRefThis4() { // ticket # 1451 checkOpertorEqRetRefThis( "P& P::operator = (const P& pc)\n" "{\n" " return (P&)(*this += pc);\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqRetRefThis5() { // ticket # 1550 checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A & operator=(const A &a) { }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "protected:\n" " A & operator=(const A &a) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "private:\n" " A & operator=(const A &a) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A & operator=(const A &a) {\n" " rand();\n" " throw std::exception();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A & operator=(const A &a) {\n" " rand();\n" " abort();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.\n", errout.str()); checkOpertorEqRetRefThis( "class A {\n" "public:\n" " A & operator=(const A &a);\n" "};\n" "A & A :: operator=(const A &a) { }"); ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout.str()); } void operatorEqRetRefThis6() { // ticket #2478 (segmentation fault) checkOpertorEqRetRefThis( "class UString {\n" "public:\n" " UString& assign( const char* c_str );\n" " UString& operator=( const UString& s );\n" "};\n" "UString& UString::assign( const char* c_str ) {\n" " std::string tmp( c_str );\n" " return assign( tmp );\n" "}\n" "UString& UString::operator=( const UString& s ) {\n" " return assign( s );\n" "}"); } void operatorEqRetRefThis7() { // ticket #5782 Endless recursion in CheckClass::checkReturnPtrThis() checkOpertorEqRetRefThis( "class basic_fbstring {\n" " basic_fbstring& operator=(int il) {\n" " return assign();\n" " }\n" " basic_fbstring& assign() {\n" " return replace();\n" " }\n" " basic_fbstring& replaceImplDiscr() {\n" " return replace();\n" " }\n" " basic_fbstring& replace() {\n" " return replaceImplDiscr();\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } // Check that operator Equal checks for assignment to self void checkOpertorEqToSelf(const char code[]) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings1, this); checkClass.operatorEqToSelf(); } void operatorEqToSelf1() { // this test has an assignment test but it is not needed checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a) { if (&a != this) { } return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); // this test doesn't have an assignment test but it is not needed checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test and has it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if (&a != this)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // this class needs an assignment test but doesn't have it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test has an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { if (&a != this) { } return *this; }"); ASSERT_EQUALS("", errout.str()); // this test doesn't have an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { return *this; }"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test and has it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if (&a != this)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test but doesn’t have it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // ticket #1224 checkOpertorEqToSelf( "const SubTree &SubTree::operator= (const SubTree &b)\n" "{\n" " CodeTree *oldtree = tree;\n" " tree = new CodeTree(*b.tree);\n" " delete oldtree;\n" " return *this;\n" "}\n" "const SubTree &SubTree::operator= (const CodeTree &b)\n" "{\n" " CodeTree *oldtree = tree;\n" " tree = new CodeTree(b);\n" " delete oldtree;\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf2() { // this test has an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &b) { if (&b != this) { } return *this; }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test doesn't have an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &b) { return *this; }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test but has it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " char *s;\n" " B & operator=(const B &b)\n" " {\n" " if (&b != this)\n" " {\n" " }\n" " return *this;\n" " }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test but doesn't have it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " char *s;\n" " B & operator=(const B &b)\n" " {\n" " free(s);\n" " s = strdup(b.s);\n" " return *this;\n" " }\n" " };\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); // this test has an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { if (&b != this) { } return *this; }"); ASSERT_EQUALS("", errout.str()); // this test doesn't have an assignment test but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { return *this; }"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test and has it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " char * s;\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b)\n" "{\n" " if (&b != this)\n" " {\n" " free(s);\n" " s = strdup(b.s);\n" " }\n" " return *this;\n" " }"); ASSERT_EQUALS("", errout.str()); // this test needs an assignment test but doesn't have it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B\n" " {\n" " public:\n" " char * s;\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b)\n" "{\n" " free(s);\n" " s = strdup(b.s);\n" " return *this;\n" " }"); ASSERT_EQUALS("[test.cpp:11]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); } void operatorEqToSelf3() { // this test has multiple inheritance so there is no trivial way to test for self assignment but doesn't need it checkOpertorEqToSelf( "class A : public B, public C\n" "{\n" "public:\n" " A & operator=(const A &a) { return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance and needs an assignment test but there is no trivial way to test for it checkOpertorEqToSelf( "class A : public B, public C\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance so there is no trivial way to test for self assignment but doesn't need it checkOpertorEqToSelf( "class A : public B, public C\n" "{\n" "public:\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a) { return *this; }"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance and needs an assignment test but there is no trivial way to test for it checkOpertorEqToSelf( "class A : public B, public C\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf4() { // this test has multiple inheritance so there is no trivial way to test for self assignment but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B : public C, public D\n" " {\n" " public:\n" " B & operator=(const B &b) { return *this; }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance and needs an assignment test but there is no trivial way to test for it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B : public C, public D\n" " {\n" " public:\n" " char * s;\n" " B & operator=(const B &b)\n" " {\n" " free(s);\n" " s = strdup(b.s);\n" " return *this;\n" " }\n" " };\n" "};"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance so there is no trivial way to test for self assignment but doesn't need it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B : public C, public D\n" " {\n" " public:\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b) { return *this; }"); ASSERT_EQUALS("", errout.str()); // this test has multiple inheritance and needs an assignment test but there is no trivial way to test for it checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " class B : public C, public D\n" " {\n" " public:\n" " char * s;\n" " B & operator=(const B &);\n" " };\n" "};\n" "A::B & A::B::operator=(const A::B &b)\n" "{\n" " free(s);\n" " s = strdup(b.s);\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf5() { // ticket # 1233 checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if((&a!=this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if((this!=&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(!(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(!(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(false==(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(false==(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(true!=(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a)\n" " {\n" " if(true!=(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if((&a!=this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if((this!=&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(!(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(!(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(false==(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(false==(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(true!=(&a==this))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " char *s;\n" " A & operator=(const A &a);\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " if(true!=(this==&a))\n" " {\n" " free(s);\n" " s = strdup(a.s);\n" " }\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); checkOpertorEqToSelf( "struct A {\n" " char *s;\n" " A& operator=(const B &b);\n" "};\n" "A& A::operator=(const B &b) {\n" " free(s);\n" " s = strdup(a.s);\n" " return *this;\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf6() { // ticket # 1550 checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a)\n" " {\n" " delete [] data;\n" " data = new char[strlen(a.data) + 1];\n" " strcpy(data, a.data);\n" " return *this;\n" " }\n" "private:\n" " char * data;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a);\n" "private:\n" " char * data;\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " delete [] data;\n" " data = new char[strlen(a.data) + 1];\n" " strcpy(data, a.data);\n" " return *this;\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a)\n" " {\n" " delete data;\n" " data = new char;\n" " *data = *a.data;\n" " return *this;\n" " }\n" "private:\n" " char * data;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & operator=(const A &a);\n" "private:\n" " char * data;\n" "};\n" "A & A::operator=(const A &a)\n" "{\n" " delete data;\n" " data = new char;\n" " *data = *a.data;\n" " return *this;\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout.str()); } void operatorEqToSelf7() { checkOpertorEqToSelf( "class A\n" "{\n" "public:\n" " A & assign(const A & a)\n" " {\n" " return *this;\n" " }\n" " A & operator=(const A &a)\n" " {\n" " return assign(a);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf8() { checkOpertorEqToSelf( "class FMat\n" "{\n" "public:\n" " FMat& copy(const FMat& rhs);\n" " FMat& operator=(const FMat& in);\n" "};\n" "FMat& FMat::copy(const FMat& rhs)\n" "{\n" " return *this;\n" "}\n" "FMat& FMat::operator=(const FMat& in)\n" "{\n" " return copy(in);\n" "}"); ASSERT_EQUALS("", errout.str()); } void operatorEqToSelf9() { checkOpertorEqToSelf( "class Foo\n" "{\n" "public:\n" " Foo& operator=(Foo* pOther);\n" " Foo& operator=(Foo& other);\n" "};\n" "Foo& Foo::operator=(Foo* pOther)\n" "{\n" " return *this;\n" "}\n" "Foo& Foo::operator=(Foo& other)\n" "{\n" " return Foo::operator=(&other);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Check that base classes have virtual destructors void checkVirtualDestructor(const char code[], bool inconclusive = false) { // Clear the error log errout.str(""); settings0.inconclusive = inconclusive; settings0.addEnabled("warning"); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings0, this); checkClass.virtualDestructor(); } void virtualDestructor1() { // Base class not found checkVirtualDestructor("class Derived : public Base { };\n" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); checkVirtualDestructor("class Derived : Base { };\n" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); } void virtualDestructor2() { // Base class doesn't have a destructor checkVirtualDestructor("class Base { };\n" "class Derived : public Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { };\n" "class Derived : protected Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { };\n" "class Derived : private Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); checkVirtualDestructor("class Base { };\n" "class Derived : Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("", errout.str()); } void virtualDestructor3() { // Base class has a destructor, but it's not virtual checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : protected Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : private Fred, public Base { public: ~Derived() { (void)11; } };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); } void virtualDestructor4() { // Derived class doesn't have a destructor => undefined behaviour according to paragraph 3 in [expr.delete] checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : private Fred, public Base { };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); } void virtualDestructor5() { // Derived class has empty destructor => undefined behaviour according to paragraph 3 in [expr.delete] checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived() {} };" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived(); }; Derived::~Derived() {}" "Base *base = new Derived;\n" "delete base;"); ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); } void virtualDestructor6() { // Only report error if base class pointer is deleted that // points at derived class checkVirtualDestructor("class Base { public: ~Base(); };\n" "class Derived : public Base { public: ~Derived() { (void)11; } };"); ASSERT_EQUALS("", errout.str()); } void virtualDestructorProtected() { // Base class has protected destructor, it makes Base *p = new Derived(); fail // during compilation time, so error is not possible. => no error checkVirtualDestructor("class A\n" "{\n" "protected:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void virtualDestructorInherited() { // class A inherits virtual destructor from class Base -> no error checkVirtualDestructor("class Base\n" "{\n" "public:\n" "virtual ~Base() {}\n" "};\n" "class A : private Base\n" "{\n" "public:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // class A inherits virtual destructor from struct Base -> no error // also notice that public is not given, but destructor is public, because // we are using struct instead of class checkVirtualDestructor("struct Base\n" "{\n" "virtual ~Base() {}\n" "};\n" "class A : public Base\n" "{\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // Unknown Base class -> it could have virtual destructor, so ignore checkVirtualDestructor("class A : private Base\n" "{\n" "public:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // Virtual destructor is inherited -> no error checkVirtualDestructor("class Base2\n" "{\n" "virtual ~Base2() {}\n" "};\n" "class Base : public Base2\n" "{\n" "};\n" "class A : private Base\n" "{\n" "public:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // class A doesn't inherit virtual destructor from class Base -> error checkVirtualDestructor("class Base\n" "{\n" "public:\n" " ~Base() {}\n" "};\n" "class A : private Base\n" "{\n" "public:\n" " ~A() { }\n" "};\n" "\n" "class B : public A\n" "{\n" "public:\n" " ~B() { int a; }\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Class 'Base' which is inherited by class 'B' does not have a virtual destructor.\n", "", errout.str()); } void virtualDestructorTemplate() { checkVirtualDestructor("template class A\n" "{\n" " public:\n" " virtual ~A(){}\n" "};\n" "template class AA\n" "{\n" " public:\n" " ~AA(){}\n" "};\n" "class B : public A, public AA\n" "{\n" " public:\n" " ~B(){int a;}\n" "};\n" "\n" "AA *p = new B; delete p;"); ASSERT_EQUALS("[test.cpp:9]: (error) Class 'AA < double >' which is inherited by class 'B' does not have a virtual destructor.\n", errout.str()); } void virtualDestructorInconclusive() { checkVirtualDestructor("class Base {\n" "public:\n" " ~Base(){}\n" " virtual void foo(){}\n" "};\n", true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Class 'Base' which has virtual members does not have a virtual destructor.\n", errout.str()); checkVirtualDestructor("class Base {\n" "public:\n" " ~Base(){}\n" " virtual void foo(){}\n" "};\n" "class Derived : public Base {\n" "public:\n" " ~Derived() { bar(); }\n" "};\n" "void foo() {\n" " Base * base = new Derived();\n" " delete base;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str()); } void checkNoMemset(const char code[]) { Settings settings; settings.addEnabled("warning"); settings.addEnabled("portability"); checkNoMemset(code,settings); } void checkNoMemset(const char code[], const Settings &settings) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.checkMemset(); } void memsetOnClass() { checkNoMemset("class Fred\n" "{\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class Fred\n" "{\n" " static std::string b;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class Fred\n" "{\n" " std::string * b;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class Fred\n" "{\n" " std::string b;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" " mutable std::string b;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred {\n" " std::string b;\n" " void f();\n" "};\n" "void Fred::f() {\n" " memset(this, 0, sizeof(*this));\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class Fred\n" "{\n" " std::string s;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" " std::string s;\n" "};\n" "class Pebbles: public Fred {};\n" "void f()\n" "{\n" " Pebbles pebbles;\n" " memset(&pebbles, 0, sizeof(pebbles));\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" " virtual ~Fred();\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a virtual function.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" " virtual ~Fred();\n" "};\n" "void f()\n" "{\n" " static Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a virtual function.\n", errout.str()); checkNoMemset("class Fred\n" "{\n" "};\n" "class Wilma\n" "{\n" " virtual ~Wilma();\n" "};\n" "class Pebbles: public Fred, Wilma {};\n" "void f()\n" "{\n" " Pebbles pebbles;\n" " memset(&pebbles, 0, sizeof(pebbles));\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (error) Using 'memset' on class that contains a virtual function.\n", errout.str()); // Fred not defined in scope checkNoMemset("namespace n1 {\n" " class Fred\n" " {\n" " std::string b;\n" " };\n" "}\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(Fred));\n" "}"); ASSERT_EQUALS("", errout.str()); // Fred with namespace qualifier checkNoMemset("namespace n1 {\n" " class Fred\n" " {\n" " std::string b;\n" " };\n" "}\n" "void f()\n" "{\n" " n1::Fred fred;\n" " memset(&fred, 0, sizeof(n1::Fred));\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); // Fred with namespace qualifier checkNoMemset("namespace n1 {\n" " class Fred\n" " {\n" " std::string b;\n" " };\n" "}\n" "void f()\n" "{\n" " n1::Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout.str()); checkNoMemset("class A {\n" " virtual ~A() { }\n" " std::string s;\n" "};\n" "int f() {\n" " const int N = 10;\n" " A** arr = new A*[N];\n" " memset(arr, 0, N * sizeof(A*));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class A {\n" // #5116 - nested class data is mixed in the SymbolDatabase " std::string s;\n" " struct B { int x; };\n" "};\n" "void f(A::B *b) {\n" " memset(b,0,4);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4461 Warn about memset/memcpy on class with references as members checkNoMemset("class A {\n" " std::string &s;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(a)); \n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a reference.\n", errout.str()); checkNoMemset("class A {\n" " const B&b;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(a)); \n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a reference.\n", errout.str()); // #7456 checkNoMemset("struct A {\n" " A() {}\n" " virtual ~A() {}\n" "};\n" "struct B {\n" " A* arr[4];\n" "};\n" "void func() {\n" " B b[4];\n" " memset(b, 0, sizeof(b));\n" "}"); ASSERT_EQUALS("", errout.str()); } void memsetOnInvalid() { // Ticket #5425 checkNoMemset("union ASFStreamHeader {\n" " struct AVMPACKED {\n" " union {\n" " struct AVMPACKED {\n" " int width;\n" " } vid;\n" " };\n" " } hdr;\n" "};" "void parseHeader() {\n" " ASFStreamHeader strhdr;\n" " memset(&strhdr, 0, sizeof(strhdr));\n" "}"); } void memsetOnStruct() { checkNoMemset("struct A\n" "{\n" "};\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct A\n" "{\n" "};\n" "void f()\n" "{\n" " struct A a;\n" " memset(&a, 0, sizeof(struct A));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct A\n" "{\n" "};\n" "void f()\n" "{\n" " struct A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("void f()\n" "{\n" " struct sockaddr_in6 fail;\n" " memset(&fail, 0, sizeof(struct sockaddr_in6));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct A\n" "{\n" " void g( struct sockaddr_in6& a);\n" "private:\n" " std::string b;\n" "};\n" "void f()\n" "{\n" " struct A fail;\n" " memset(&fail, 0, sizeof(struct A));\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout.str()); checkNoMemset("struct Fred\n" "{\n" " std::string s;\n" "};\n" "void f()\n" "{\n" " Fred fred;\n" " memset(&fred, 0, sizeof(fred));\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout.str()); checkNoMemset("struct Stringy {\n" " std::string inner;\n" "};\n" "struct Foo {\n" " Stringy s;\n" "};\n" "int main() {\n" " Foo foo;\n" " memset(&foo, 0, sizeof(Foo));\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout.str()); } void memsetVector() { checkNoMemset("class A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on class that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(struct A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(a));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("class A\n" "{ std::vector< std::vector > ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on class that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector< std::vector > ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector< std::vector > ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(a));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A\n" "{ std::vector ints; };\n" "\n" "void f()\n" "{\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout.str()); checkNoMemset("struct A {\n" " std::vector buf;\n" " operator int*() {return &buf[0];}\n" "};\n" "void f() {\n" " A a;\n" " memset(a, 0, 100);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4460 checkNoMemset("struct C {\n" " std::string s;\n" "};\n" "int foo() {\n" " C* c1[10][10];\n" " C* c2[10];\n" " C c3[10][10];\n" " C** c4 = new C*[10];\n" " memset(**c1, 0, 10);\n" " memset(*c1, 0, 10);\n" " memset(*c2, 0, 10);\n" " memset(*c3, 0, 10);\n" " memset(*c4, 0, 10);\n" " memset(c2, 0, 10);\n" " memset(c3, 0, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on struct that contains a 'std::string'.\n" "[test.cpp:11]: (error) Using 'memset' on struct that contains a 'std::string'.\n" "[test.cpp:12]: (error) Using 'memset' on struct that contains a 'std::string'.\n" "[test.cpp:13]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout.str()); // Ticket #6953 checkNoMemset("typedef float realnum;\n" "struct multilevel_data {\n" " realnum *GammaInv;\n" " realnum data[1];\n" "};\n" "void *new_internal_data() const {\n" " multilevel_data *d = (multilevel_data *) malloc(sizeof(multilevel_data));\n" " memset(d, 0, sizeof(multilevel_data));\n" " return (void*) d;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (portability) Using memset() on struct which contains a floating point number.\n", errout.str()); } void memsetOnStdPodType() { // Ticket #5901 Settings settings; const char xmldata[] = "\n" "\n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); checkNoMemset("class A {\n" " std::array ints;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); // std::array is POD (#5481) checkNoMemset("struct st {\n" " std::uint8_t a;\n" " std::atomic_bool b;\n" "};\n" "\n" "void f() {\n" " st s;\n" " std::memset(&s, 0, sizeof(st));\n" "}", settings); ASSERT_EQUALS("", errout.str()); } void memsetOnFloat() { checkNoMemset("struct A {\n" " float f;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (portability) Using memset() on struct which contains a floating point number.\n", errout.str()); checkNoMemset("struct A {\n" " float f[4];\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (portability) Using memset() on struct which contains a floating point number.\n", errout.str()); checkNoMemset("struct A {\n" " float f[4];\n" "};\n" "void f(const A& b) {\n" " A a;\n" " memcpy(&a, &b, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct A {\n" " float* f;\n" "};\n" "void f() {\n" " A a;\n" " memset(&a, 0, sizeof(A));\n" "}"); ASSERT_EQUALS("", errout.str()); } void memsetOnUnknown() { checkNoMemset("void clang_tokenize(CXToken **Tokens) {\n" " *Tokens = (CXToken *)malloc(sizeof(CXToken) * CXTokens.size());\n" " memmove(*Tokens, CXTokens.data(), sizeof(CXToken) * CXTokens.size());\n" "}"); ASSERT_EQUALS("", errout.str()); } void mallocOnClass() { checkNoMemset("class C { C() {} };\n" "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with malloc(), but class provides constructors.\n", errout.str()); checkNoMemset("class C { C(int z, Foo bar) { bar(); } };\n" "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with malloc(), but class provides constructors.\n", errout.str()); checkNoMemset("struct C { C() {} };\n" "void foo(C*& p) {\n" " p = realloc(p, sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with realloc(), but class provides constructors.\n", errout.str()); checkNoMemset("struct C { virtual void bar(); };\n" "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (error) Memory for class instance allocated with malloc(), but class contains a virtual function.\n", errout.str()); checkNoMemset("struct C { std::string s; };\n" "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (error) Memory for class instance allocated with malloc(), but class contains a 'std::string'.\n", errout.str()); checkNoMemset("class C { };\n" // C-Style class/struct "void foo(C*& p) {\n" " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("struct C { C() {} };\n" "void foo(C*& p) {\n" " p = new C();\n" "}"); ASSERT_EQUALS("", errout.str()); checkNoMemset("class C { C() {} };\n" "void foo(D*& p) {\n" // Unknown type " p = malloc(sizeof(C));\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkThisSubtraction(const char code[]) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check.. CheckClass checkClass(&tokenizer, &settings1, this); checkClass.thisSubtraction(); } void this_subtraction() { checkThisSubtraction("; this-x ;"); ASSERT_EQUALS("[test.cpp:1]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n", errout.str()); checkThisSubtraction("; *this = *this-x ;"); ASSERT_EQUALS("", errout.str()); checkThisSubtraction("; *this = *this-x ;\n" "this-x ;"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n", errout.str()); checkThisSubtraction("; *this = *this-x ;\n" "this-x ;\n" "this-x ;\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n" "[test.cpp:3]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n", errout.str()); } void checkConst(const char code[], Settings *s = nullptr, bool inconclusive = true) { // Clear the error log errout.str(""); // Check.. if (!s) s = &settings0; s->inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(s, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); CheckClass checkClass(&tokenizer, s, this); checkClass.checkConst(); } void const1() { checkConst("class Fred {\n" " int a;\n" " int getA() { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " const std::string foo() { return \"\"; }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Fred {\n" " std::string s;\n" " const std::string & foo() { return \"\"; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // constructors can't be const.. checkConst("class Fred {\n" " int a;\n" "public:\n" " Fred() { }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment through |=.. checkConst("class Fred {\n" " int a;\n" " int setA() { a |= true; }\n" "};"); ASSERT_EQUALS("", errout.str()); // functions with a call to a member function can only be const, if that member function is const, too.. (#1305) checkConst("class foo {\n" "public:\n" " int x;\n" " void a() { x = 1; }\n" " void b() { a(); }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" "public:\n" " int x;\n" " int a() const { return x; }\n" " void b() { a(); }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'Fred::b' can be const.\n", errout.str()); checkConst("class Fred {\n" "public:\n" " int x;\n" " void b() { a(); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::b' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // static functions can't be const.. checkConst("class foo\n" "{\n" "public:\n" " static unsigned get()\n" " { return 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " const std::string foo() const throw() { return \"\"; }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const2() { // ticket 1344 // assignment to variable can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo() { s = \"\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to function argument reference can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a) { a = s; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a) { s = a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to function argument references can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b) { a = s; b = s; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b) { s = a; s = b; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b) { s = a; b = a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b) { a = s; s = b; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const3() { // assignment to function argument pointer can be const checkConst("class Fred {\n" " int s;\n" " void foo(int * a) { *a = s; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " int s;\n" " void foo(int * a) { s = *a; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to function argument pointers can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b) { *a = s; *b = s; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b) { s = *a; s = *b; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b) { s = *a; *b = s; }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b) { *a = s; s = b; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const4() { checkConst("class Fred {\n" " int a;\n" " int getA();\n" "};\n" "int Fred::getA() { return a; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " std::string s;\n" " const std::string & foo();\n" "};\n" "const std::string & Fred::foo() { return \"\"; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // functions with a function call to a non-const member can't be const.. (#1305) checkConst("class Fred\n" "{\n" "public:\n" " int x;\n" " void a() { x = 1; }\n" " void b();\n" "};\n" "void Fred::b() { a(); }"); ASSERT_EQUALS("", errout.str()); // static functions can't be const.. checkConst("class Fred\n" "{\n" "public:\n" " static unsigned get();\n" "};\n" "static unsigned Fred::get() { return 0; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo();\n" "};\n" "void Fred::foo() { s = \"\"; }"); ASSERT_EQUALS("", errout.str()); // assignment to function argument reference can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a);\n" "};\n" "void Fred::foo(std::string & a) { a = s; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a);\n" "};\n" "void Fred::foo(std::string & a) { s = a; }"); ASSERT_EQUALS("", errout.str()); // assignment to function argument references can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b);\n" "};\n" "void Fred::foo(std::string & a, std::string & b) { a = s; b = s; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b);\n" "};\n" "void Fred::foo(std::string & a, std::string & b) { s = a; s = b; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b);\n" "};\n" "void Fred::foo(std::string & a, std::string & b) { s = a; b = a; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string & a, std::string & b);\n" "};\n" "void Fred::foo(std::string & a, std::string & b) { a = s; s = b; }"); ASSERT_EQUALS("", errout.str()); // assignment to function argument pointer can be const checkConst("class Fred {\n" " int s;\n" " void foo(int * a);\n" "};\n" "void Fred::foo(int * a) { *a = s; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " int s;\n" " void foo(int * a);\n" "};\n" "void Fred::foo(int * a) { s = *a; }"); ASSERT_EQUALS("", errout.str()); // assignment to function argument pointers can be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b);\n" "};\n" "void Fred::foo(std::string * a, std::string * b) { *a = s; *b = s; }"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b);\n" "};\n" "void Fred::foo(std::string * a, std::string * b) { s = *a; s = *b; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b);\n" "};\n" "void Fred::foo(std::string * a, std::string * b) { s = *a; *b = s; }"); ASSERT_EQUALS("", errout.str()); // assignment to variable, can't be const checkConst("class Fred {\n" " std::string s;\n" " void foo(std::string * a, std::string * b);\n" "};\n" "void Fred::foo(std::string * a, std::string * b) { *a = s; s = b; }"); ASSERT_EQUALS("", errout.str()); // check functions with same name checkConst("class Fred {\n" " std::string s;\n" " void foo();\n" " void foo(std::string & a);\n" " void foo(const std::string & a);\n" "};\n" "void Fred::foo() { }" "void Fred::foo(std::string & a) { a = s; }" "void Fred::foo(const std::string & a) { s = a; }"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:7] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout.str()); // check functions with different or missing parameter names checkConst("class Fred {\n" " std::string s;\n" " void foo1(int, int);\n" " void foo2(int a, int b);\n" " void foo3(int, int b);\n" " void foo4(int a, int);\n" " void foo5(int a, int b);\n" "};\n" "void Fred::foo1(int a, int b) { }\n" "void Fred::foo2(int c, int d) { }\n" "void Fred::foo3(int a, int b) { }\n" "void Fred::foo4(int a, int b) { }\n" "void Fred::foo5(int, int) { }"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo1' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:10] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo2' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:11] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::foo3' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:12] -> [test.cpp:6]: (performance, inconclusive) Technically the member function 'Fred::foo4' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:13] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::foo5' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); // check nested classes checkConst("class Fred {\n" " class A {\n" " int a;\n" " int getA() { return a; }\n" " };\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" " int A::getA() { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" "};\n" "int Fred::A::getA() { return a; }"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout.str()); // check deeply nested classes checkConst("class Fred {\n" " class B {\n" " int b;\n" " int getB() { return b; }\n" " class A {\n" " int a;\n" " int getA() { return a; }\n" " };\n" " };\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" "[test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n" , errout.str()); checkConst("class Fred {\n" " class B {\n" " int b;\n" " int getB();\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" " int A::getA() { return a; }\n" " };\n" " int B::getB() { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" "[test.cpp:9] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " class B {\n" " int b;\n" " int getB();\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" " };\n" " int B::A::getA() { return a; }\n" " int B::getB() { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" "[test.cpp:10] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout.str()); checkConst("class Fred {\n" " class B {\n" " int b;\n" " int getB();\n" " class A {\n" " int a;\n" " int getA();\n" " };\n" " };\n" "};\n" "int Fred::B::A::getA() { return a; }\n" "int Fred::B::getB() { return b; }"); ASSERT_EQUALS("[test.cpp:12] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" "[test.cpp:11] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout.str()); } // operator< can often be const void constoperator1() { checkConst("struct Fred {\n" " int a;\n" " bool operator<(const Fred &f) { return a < f.a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::operator<' can be const.\n", errout.str()); } // operator<< void constoperator2() { checkConst("struct Foo {\n" " void operator<<(int);\n" "};\n" "struct Fred {\n" " Foo foo;\n" " void x()\n" " {\n" " foo << 123;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Foo {\n" " void operator<<(int);\n" "};\n" "struct Fred {\n" " Foo foo;\n" " void x()\n" " {\n" " std::cout << foo << 123;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'Fred::x' can be const.\n", errout.str()); } void constoperator3() { checkConst("struct Fred {\n" " int array[10];\n" " int const & operator [] (unsigned int index) const { return array[index]; }\n" " int & operator [] (unsigned int index) { return array[index]; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Fred {\n" " int array[10];\n" " int const & operator [] (unsigned int index) { return array[index]; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::operator[]' can be const.\n", errout.str()); } void constoperator4() { // #7953 checkConst("class A {\n" " int c;\n" "public:\n" " operator int*() { return &c; };\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" " int c;\n" "public:\n" " operator const int*() { return &c; };\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorconstint*' can be const.\n", errout.str()); // #2375 checkConst("struct Fred {\n" " int array[10];\n" " typedef int* (Fred::*UnspecifiedBoolType);\n" " operator UnspecifiedBoolType() { };\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::operatorint**' can be const.\n", "", errout.str()); checkConst("struct Fred {\n" " int array[10];\n" " typedef int* (Fred::*UnspecifiedBoolType);\n" " operator UnspecifiedBoolType() { array[0] = 0; };\n" "};"); ASSERT_EQUALS("", errout.str()); } void constoperator5() { // ticket #3252 checkConst("class A {\n" " int c;\n" "public:\n" " operator int& () {return c}\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" " int c;\n" "public:\n" " operator const int& () {return c}\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorconstint&' can be const.\n", errout.str()); checkConst("class A {\n" " int c;\n" "public:\n" " operator int () {return c}\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorint' can be const.\n", errout.str()); } void constoperator6() { // ticket #8669 checkConst("class A {\n" " int c;\n" " void f() { os >> *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const5() { // ticket #1482 checkConst("class A {\n" " int a;\n" " bool foo(int i)\n" " {\n" " bool same;\n" " same = (i == a);\n" " return same;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout.str()); } void const6() { // ticket #1491 checkConst("class foo {\n" "public:\n" "};\n" "void bar() {}"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred\n" "{\n" "public:\n" " void foo() { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct fast_string\n" "{\n" " union\n" " {\n" " char buff[100];\n" " };\n" " void set_type(char t);\n" "};\n" "inline void fast_string::set_type(char t)\n" "{\n" " buff[10] = t;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const7() { checkConst("class foo {\n" " int a;\n" "public:\n" " void set(int i) { a = i; }\n" " void set(const foo & f) { *this = f; }\n" "};\n" "void bar() {}"); ASSERT_EQUALS("", errout.str()); } void const8() { // ticket #1517 checkConst("class A {\n" "public:\n" " A():m_strValue(\"\"){}\n" " std::string strGetString() { return m_strValue; }\n" "private:\n" " std::string m_strValue;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetString' can be const.\n", errout.str()); } void const9() { // ticket #1515 checkConst("class wxThreadInternal {\n" "public:\n" " void SetExitCode(wxThread::ExitCode exitcode) { m_exitcode = exitcode; }\n" "private:\n" " wxThread::ExitCode m_exitcode;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const10() { // ticket #1522 checkConst("class A {\n" "public:\n" " int foo() { return x = 0; }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " int foo() { return x ? x : x = 0; }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " int foo() { return x ? x = 0 : x; }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const11() { // ticket #1529 checkConst("class A {\n" "public:\n" " void set(struct tm time) { m_time = time; }\n" "private:\n" " struct tm m_time;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const12() { // ticket #1525 checkConst("class A {\n" "public:\n" " int foo() { x = 0; }\n" "private:\n" " mutable int x;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout.str()); } void const13() { // ticket #1519 checkConst("class A {\n" "public:\n" " A(){}\n" " std::vector GetVec() {return m_vec;}\n" " std::pair GetPair() {return m_pair;}\n" "private:\n" " std::vector m_vec;\n" " std::pair m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n" "[test.cpp:5]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::vector & GetVec() {return m_vec;}\n" " const std::pair & GetPair() {return m_pair;}\n" "private:\n" " std::vector m_vec;\n" " std::pair m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n" "[test.cpp:5]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); } void const14() { // extends ticket 1519 checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair,double> GetPair() {return m_pair;}\n" "private:\n" " std::pair,double> m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair,double>& GetPair() {return m_pair;}\n" "private:\n" " std::pair,double> m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair,double>& GetPair() {return m_pair;}\n" "private:\n" " std::pair,double> m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " pair GetPair() {return m_pair;}\n" "private:\n" " pair m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " const pair & GetPair() {return m_pair;}\n" "private:\n" " pair m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " pair & GetPair() {return m_pair;}\n" "private:\n" " pair m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< int,std::vector > GetPair() {return m_pair;}\n" "private:\n" " std::pair< int,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair< int,std::vector >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< int,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< int,std::vector >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< int,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " pair< vector, int > GetPair() {return m_pair;}\n" "private:\n" " pair< vector, int > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " const pair< vector, int >& GetPair() {return m_pair;}\n" "private:\n" " pair< vector, int > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " pair< vector, int >& GetPair() {return m_pair;}\n" "private:\n" " pair< vector, int > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< std::vector,std::vector > GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::vector,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair< std::vector,std::vector >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::vector,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< std::vector,std::vector >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::vector,std::vector > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< std::pair < int, char > , int > GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::pair < int, char > , int > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair< std::pair < int, char > , int > & GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::pair < int, char > , int > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< std::pair < int, char > , int > & GetPair() {return m_pair;}\n" "private:\n" " std::pair< std::pair < int, char > , int > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< int , std::pair < int, char > > GetPair() {return m_pair;}\n" "private:\n" " std::pair< int , std::pair < int, char > > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " const std::pair< int , std::pair < int, char > >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< int , std::pair < int, char > > m_pair;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " A(){}\n" " std::pair< int , std::pair < int, char > >& GetPair() {return m_pair;}\n" "private:\n" " std::pair< int , std::pair < int, char > > m_pair;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " vector GetVec() {return m_Vec;}\n" "private:\n" " vector m_Vec;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " const vector& GetVec() {return m_Vec;}\n" "private:\n" " vector m_Vec;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n", errout.str()); checkConst("using namespace std;" "class A {\n" "public:\n" " A(){}\n" " vector& GetVec() {return m_Vec;}\n" "private:\n" " vector m_Vec;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " int * * foo() { return &x; }\n" "private:\n" " const int * x;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " const int ** foo() { return &x; }\n" "private:\n" " const int * x;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout.str()); } void const15() { checkConst("class Fred {\n" " unsigned long long int a;\n" " unsigned long long int getA() { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::getA' can be const.\n", errout.str()); // constructors can't be const.. checkConst("class Fred {\n" " unsigned long long int a;\n" "public:\n" " Fred() { }\n" "};"); ASSERT_EQUALS("", errout.str()); // assignment through |=.. checkConst("class Fred {\n" " unsigned long long int a;\n" " unsigned long long int setA() { a |= true; }\n" "};"); ASSERT_EQUALS("", errout.str()); // static functions can't be const.. checkConst("class foo\n" "{\n" "public:\n" " static unsigned long long int get()\n" " { return 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const16() { // ticket #1551 checkConst("class Fred {\n" " int a;\n" " void set(int i) { Fred::a = i; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const17() { // ticket #1552 checkConst("class Fred {\n" "public:\n" " void set(int i, int j) { a[i].k = i; }\n" "private:\n" " struct { int k; } a[4];\n" "};"); ASSERT_EQUALS("", errout.str()); } void const18() { checkConst("class Fred {\n" "static int x;\n" "public:\n" " void set(int i) { x = i; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::set' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const19() { // ticket #1612 checkConst("using namespace std;\n" "class Fred {\n" "private:\n" " std::string s;\n" "public:\n" " void set(std::string ss) { s = ss; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const20() { // ticket #1602 checkConst("class Fred {\n" " int x : 3;\n" "public:\n" " void set(int i) { x = i; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " list x;\n" "public:\n" " list get() { return x; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " list x;\n" "public:\n" " list get() { return x; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout.str()); checkConst("class Fred {\n" " std::list x;\n" "public:\n" " std::list get() { return x; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " std::list x;\n" "public:\n" " std::list get() { return x; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout.str()); } void const21() { // ticket #1683 checkConst("class A\n" "{\n" "private:\n" " const char * l1[10];\n" "public:\n" " A()\n" " {\n" " for (int i = 0 ; i < 10; l1[i] = NULL, i++);\n" " }\n" " void f1() { l1[0] = \"Hello\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const22() { checkConst("class A\n" "{\n" "private:\n" " B::C * v1;\n" "public:\n" " void f1() { v1 = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A\n" "{\n" "private:\n" " B::C * v1[0];\n" "public:\n" " void f1() { v1[0] = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const23() { checkConst("class Class {\n" "public:\n" " typedef Template Type;\n" " typedef Template2 Type2;\n" " void set_member(Type2 m) { _m = m; }\n" "private:\n" " Type2 _m;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const24() { checkConst("class Class {\n" "public:\n" "void Settings::SetSetting(QString strSetting, QString strNewVal)\n" "{\n" " (*m_pSettings)[strSetting] = strNewVal;\n" "}\n" "private:\n" " std::map *m_pSettings;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const25() { // ticket #1724 checkConst("class A{\n" "public:\n" "A(){m_strVal=\"\";}\n" "std::string strGetString() const\n" "{return m_strVal.c_str();}\n" "const std::string strGetString1() const\n" "{return m_strVal.c_str();}\n" "private:\n" "std::string m_strVal;\n" "};\n" ); ASSERT_EQUALS("", errout.str()); checkConst("class A{\n" "public:\n" "A(){m_strVal=\"\";}\n" "std::string strGetString()\n" "{return m_strVal.c_str();}\n" "private:\n" "std::string m_strVal;\n" "};\n" ); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetString' can be const.\n", errout.str()); checkConst("class A{\n" "public:\n" "A(){m_strVal=\"\";}\n" "const std::string strGetString1()\n" "{return m_strVal.c_str();}\n" "private:\n" "std::string m_strVal;\n" "};\n" ); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetString1' can be const.\n", errout.str()); checkConst("class A{\n" "public:\n" "A(){m_strVec.push_back(\"\");}\n" "size_t strGetSize()\n" "{return m_strVec.size();}\n" "private:\n" "std::vector m_strVec;\n" "};\n" ); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetSize' can be const.\n", errout.str()); checkConst("class A{\n" "public:\n" "A(){m_strVec.push_back(\"\");}\n" "bool strGetEmpty()\n" "{return m_strVec.empty();}\n" "private:\n" "std::vector m_strVec;\n" "};\n" ); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetEmpty' can be const.\n", errout.str()); } void const26() { // ticket #1847 checkConst("class DelayBase {\n" "public:\n" "void swapSpecificDelays(int index1, int index2) {\n" " std::swap(delays_[index1], delays_[index2]);\n" "}\n" "float delays_[4];\n" "};\n" ); ASSERT_EQUALS("", errout.str()); checkConst("struct DelayBase {\n" " float swapSpecificDelays(int index1) {\n" " return delays_[index1];\n" " }\n" " float delays_[4];\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Technically the member function 'DelayBase::swapSpecificDelays' can be const.\n", errout.str()); } void const27() { // ticket #1882 checkConst("class A {\n" "public:\n" " A(){m_d=1.0; m_iRealVal=2.0;}\n" " double dGetValue();\n" "private:\n" " double m_d;\n" " double m_iRealVal;\n" "};\n" "double A::dGetValue() {\n" " double dRet = m_iRealVal;\n" " if( m_d != 0 )\n" " return m_iRealVal / m_d;\n" " return dRet;\n" "};", nullptr, true); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'A::dGetValue' can be const.\n", errout.str()); } void const28() { // ticket #1883 checkConst("class P {\n" "public:\n" " P() { x=0.0; y=0.0; }\n" " double x,y;\n" "};\n" "class A : public P {\n" "public:\n" " A():P(){}\n" " void SetPos(double xPos, double yPos) {\n" " x=xPos;\n" " y=yPos;\n" " }\n" "};\n" ); ASSERT_EQUALS("", errout.str()); checkConst("class AA : public P {\n" "public:\n" " AA():P(){}\n" " inline void vSetXPos(int x_)\n" " {\n" " UnknownScope::x = x_;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class AA {\n" "public:\n" " AA():P(){}\n" " inline void vSetXPos(int x_)\n" " {\n" " UnknownScope::x = x_;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'AA::vSetXPos' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const29() { // ticket #1922 checkConst("class test {\n" " public:\n" " test();\n" " const char* get() const;\n" " char* get();\n" " private:\n" " char* value_;\n" "};\n" "test::test()\n" "{\n" " value_ = 0;\n" "}\n" "const char* test::get() const\n" "{\n" " return value_;\n" "}\n" "char* test::get()\n" "{\n" " return value_;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const30() { // check for false negatives checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived : public Base {\n" "public:\n" " int get() {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'Derived::get' can be const.\n", errout.str()); checkConst("class Base1 {\n" "public:\n" " int a;\n" "};\n" "class Base2 {\n" "public:\n" " int b;\n" "};\n" "class Derived : public Base1, public Base2 {\n" "public:\n" " int getA() {\n" " return a;\n" " }\n" " int getB() {\n" " return b;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:11]: (style, inconclusive) Technically the member function 'Derived::getA' can be const.\n" "[test.cpp:14]: (style, inconclusive) Technically the member function 'Derived::getB' can be const.\n", errout.str()); checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived1 : public Base { };\n" "class Derived2 : public Derived1 {\n" "public:\n" " int get() {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Derived2::get' can be const.\n", errout.str()); checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived1 : public Base { };\n" "class Derived2 : public Derived1 { };\n" "class Derived3 : public Derived2 { };\n" "class Derived4 : public Derived3 {\n" "public:\n" " int get() {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (style, inconclusive) Technically the member function 'Derived4::get' can be const.\n", errout.str()); // check for false positives checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived : public Base {\n" "public:\n" " int get() const {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Base1 {\n" "public:\n" " int a;\n" "};\n" "class Base2 {\n" "public:\n" " int b;\n" "};\n" "class Derived : public Base1, public Base2 {\n" "public:\n" " int getA() const {\n" " return a;\n" " }\n" " int getB() const {\n" " return b;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived1 : public Base { };\n" "class Derived2 : public Derived1 {\n" "public:\n" " int get() const {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Base {\n" "public:\n" " int a;\n" "};\n" "class Derived1 : public Base { };\n" "class Derived2 : public Derived1 { };\n" "class Derived3 : public Derived2 { };\n" "class Derived4 : public Derived3 {\n" "public:\n" " int get() const {\n" " return a;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const31() { checkConst("namespace std { }\n" "class Fred {\n" "public:\n" " int a;\n" " int get() { return a; }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout.str()); } void const32() { checkConst("class Fred {\n" "public:\n" " std::string a[10];\n" " void seta() { a[0] = \"\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const33() { checkConst("class derived : public base {\n" "public:\n" " void f(){}\n" "};"); ASSERT_EQUALS("", errout.str()); } void const34() { // ticket #1964 checkConst("class Bar {\n" " void init(Foo * foo) {\n" " foo.bar = this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const35() { // ticket #2001 checkConst("namespace N\n" "{\n" " class Base\n" " {\n" " };\n" "}\n" "namespace N\n" "{\n" " class Derived : public Base\n" " {\n" " public:\n" " int getResourceName() { return var; }\n" " int var;\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (style, inconclusive) Technically the member function 'N::Derived::getResourceName' can be const.\n", errout.str()); checkConst("namespace N\n" "{\n" " class Base\n" " {\n" " public:\n" " int getResourceName();\n" " int var;\n" " };\n" "}\n" "int N::Base::getResourceName() { return var; }"); ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout.str()); checkConst("namespace N\n" "{\n" " class Base\n" " {\n" " public:\n" " int getResourceName();\n" " int var;\n" " };\n" "}\n" "namespace N\n" "{\n" " int Base::getResourceName() { return var; }\n" "}"); ASSERT_EQUALS("[test.cpp:12] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout.str()); checkConst("namespace N\n" "{\n" " class Base\n" " {\n" " public:\n" " int getResourceName();\n" " int var;\n" " };\n" "}\n" "using namespace N;\n" "int Base::getResourceName() { return var; }"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout.str()); } void const36() { // ticket #2003 checkConst("class Foo {\n" "public:\n" " Blue::Utility::Size m_MaxQueueSize;\n" " void SetMaxQueueSize(Blue::Utility::Size a_MaxQueueSize)\n" " {\n" " m_MaxQueueSize = a_MaxQueueSize;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const37() { // ticket #2081 and #2085 checkConst("class A\n" "{\n" "public:\n" " A(){};\n" " std::string operator+(const char *c)\n" " {\n" " return m_str+std::string(c);\n" " }\n" "private:\n" " std::string m_str;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::operator+' can be const.\n", errout.str()); checkConst("class Fred\n" "{\n" "private:\n" " long x;\n" "public:\n" " Fred() {\n" " x = 0;\n" " }\n" " bool isValid() {\n" " return (x == 0x11224488);\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (style, inconclusive) Technically the member function 'Fred::isValid' can be const.\n", errout.str()); } void const38() { // ticket #2135 checkConst("class Foo {\n" "public:\n" " ~Foo() { delete oArq; }\n" " Foo(): oArq(new std::ofstream(\"...\")) {}\n" " void MyMethod();\n" "private:\n" " std::ofstream *oArq;\n" "};\n" "void Foo::MyMethod()\n" "{\n" " (*oArq) << \"\";\n" "}"); ASSERT_EQUALS("", errout.str()); } void const39() { checkConst("class Foo\n" "{\n" " int * p;\n" "public:\n" " Foo () : p(0) { }\n" " int * f();\n" " const int * f() const;\n" "};\n" "const int * Foo::f() const\n" "{\n" " return p;\n" "}\n" "int * Foo::f()\n" "{\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const40() { // ticket #2228 checkConst("class SharedPtrHolder\n" "{\n" " private:\n" " std::tr1::shared_ptr pView;\n" " public:\n" " SharedPtrHolder()\n" " { }\n" " void SetView(const std::shared_ptr & aView)\n" " {\n" " pView = aView;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const41() { // ticket #2255 checkConst("class Fred\n" "{\n" " ::std::string m_name;\n" "public:\n" " void SetName(const ::std::string & name)\n" " {\n" " m_name = name;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class SharedPtrHolder\n" "{\n" " ::std::tr1::shared_ptr pNum;\n" " public :\n" " void SetNum(const ::std::tr1::shared_ptr & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class SharedPtrHolder2\n" "{\n" " public:\n" " typedef ::std::tr1::shared_ptr IntSharedPtr;\n" " private:\n" " IntSharedPtr pNum;\n" " public :\n" " void SetNum(const IntSharedPtr & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct IntPtrTypes\n" "{\n" " typedef ::std::tr1::shared_ptr Shared;\n" "};\n" "class SharedPtrHolder3\n" "{\n" " private:\n" " IntPtrTypes::Shared pNum;\n" " public :\n" " void SetNum(const IntPtrTypes::Shared & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("template \n" "struct PtrTypes\n" "{\n" " typedef ::std::tr1::shared_ptr Shared;\n" "};\n" "class SharedPtrHolder4\n" "{\n" " private:\n" " PtrTypes::Shared pNum;\n" " public :\n" " void SetNum(const PtrTypes::Shared & apNum)\n" " {\n" " pNum = apNum;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const42() { // ticket #2282 checkConst("class Fred\n" "{\n" "public:\n" " struct AB { };\n" " bool f(AB * ab);\n" "};\n" "bool Fred::f(Fred::AB * ab)\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Fred\n" "{\n" "public:\n" " struct AB {\n" " struct CD { };\n" " };\n" " bool f(AB::CD * cd);\n" "};\n" "bool Fred::f(Fred::AB::CD * cd)\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("namespace NS {\n" " class Fred\n" " {\n" " public:\n" " struct AB {\n" " struct CD { };\n" " };\n" " bool f(AB::CD * cd);\n" " };\n" " bool Fred::f(Fred::AB::CD * cd)\n" " {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("namespace NS {\n" " class Fred\n" " {\n" " public:\n" " struct AB {\n" " struct CD { };\n" " };\n" " bool f(AB::CD * cd);\n" " };\n" "}\n" "bool NS::Fred::f(NS::Fred::AB::CD * cd)\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Foo {\n" " class Fred\n" " {\n" " public:\n" " struct AB {\n" " struct CD { };\n" " };\n" " bool f(AB::CD * cd);\n" " };\n" "};\n" "bool Foo::Fred::f(Foo::Fred::AB::CD * cd)\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'Foo::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const43() { // ticket 2377 checkConst("class A\n" "{\n" "public:\n" " void foo( AA::BB::CC::DD b );\n" " AA::BB::CC::DD a;\n" "};\n" "void A::foo( AA::BB::CC::DD b )\n" "{\n" " a = b;\n" "}"); ASSERT_EQUALS("", errout.str()); checkConst("namespace AA\n" "{\n" " namespace BB\n" " {\n" " namespace CC\n" " {\n" " struct DD\n" " {};\n" " }\n" " }\n" "}\n" "class A\n" "{\n" " public:\n" "\n" " AA::BB::CC::DD a;\n" " void foo(AA::BB::CC::DD b)\n" " {\n" " a = b;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("namespace ZZ\n" "{\n" " namespace YY\n" " {\n" " struct XX\n" " {};\n" " }\n" "}\n" "class B\n" "{\n" " public:\n" " ZZ::YY::XX a;\n" " void foo(ZZ::YY::XX b)\n" " {\n" " a = b;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const44() { // ticket 2595 checkConst("class A\n" "{\n" "public:\n" " bool bOn;\n" " bool foo()\n" " {\n" " return 0 != (bOn = bOn);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const45() { // ticket 2664 checkConst("namespace wraps {\n" " class BaseLayout {};\n" "}\n" "namespace tools {\n" " class WorkspaceControl :\n" " public wraps::BaseLayout\n" " {\n" " int toGrid(int _value)\n" " {\n" " }\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (performance, inconclusive) Technically the member function 'tools::WorkspaceControl::toGrid' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const46() { // ticket 2663 checkConst("class Altren {\n" "public:\n" " int fun1() {\n" " int a;\n" " a++;\n" " }\n" " int fun2() {\n" " b++;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Altren::fun1' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:7]: (performance, inconclusive) Technically the member function 'Altren::fun2' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const47() { // ticket 2670 checkConst("class Altren {\n" "public:\n" " void foo() { delete this; }\n" " void foo(int i) const { }\n" " void bar() { foo(); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class Altren {\n" "public:\n" " void foo() { delete this; }\n" " void foo(int i) const { }\n" " void bar() { foo(1); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:5]: (style, inconclusive) Technically the member function 'Altren::bar' can be const.\n", errout.str()); } void const48() { // ticket 2672 checkConst("class S0 {\n" " class S1 {\n" " class S2 {\n" " class S3 {\n" " class S4 { };\n" " };\n" " };\n" " };\n" "};\n" "class TextIterator {\n" " S0::S1::S2::S3::S4 mCurrent, mSave;\n" "public:\n" " bool setTagColour();\n" "};\n" "bool TextIterator::setTagColour() {\n" " mSave = mCurrent;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const49() { // ticket 2795 checkConst("class A {\n" " private:\n" " std::map _hash;\n" " public:\n" " A() : _hash() {}\n" " unsigned int fetch(unsigned int key)\n" // cannot be 'const' " {\n" " return _hash[key];\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const50() { // ticket 2943 checkConst("class Altren\n" "{\n" " class SubClass : public std::vector\n" " {\n" " };\n" "};\n" "void _setAlign()\n" "{\n" " if (mTileSize.height > 0) return;\n" " if (mEmptyView) return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void const51() { // ticket 3040 checkConst("class PSIPTable {\n" "public:\n" " PSIPTable() : _pesdata(0) { }\n" " const unsigned char* pesdata() const { return _pesdata; }\n" " unsigned char* pesdata() { return _pesdata; }\n" " void SetSection(uint num) { pesdata()[6] = num; }\n" "private:\n" " unsigned char *_pesdata;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class PESPacket {\n" "public:\n" " PESPacket() : _pesdata(0) { }\n" " const unsigned char* pesdata() const { return _pesdata; }\n" " unsigned char* pesdata() { return _pesdata; }\n" "private:\n" " unsigned char *_pesdata;\n" "};\n" "class PSIPTable : public PESPacket\n" "{\n" "public:\n" " void SetSection(uint num) { pesdata()[6] = num; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const52() { // ticket 3048 checkConst("class foo {\n" " void DoSomething(int &a) const { a = 1; }\n" " void DoSomethingElse() { DoSomething(bar); }\n" "private:\n" " int bar;\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'foo::DoSomething' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const53() { // ticket 3049 checkConst("class A {\n" " public:\n" " A() : foo(false) {};\n" " virtual bool One(bool b = false) { foo = b; return false; }\n" " private:\n" " bool foo;\n" "};\n" "class B : public A {\n" " public:\n" " B() {};\n" " bool One(bool b = false) { return false; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const54() { // ticket 3052 checkConst("class Example {\n" " public:\n" " void Clear(void) { Example tmp; (*this) = tmp; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const55() { checkConst("class MyObject {\n" " int tmp;\n" " MyObject() : tmp(0) {}\n" "public:\n" " void set(std::stringstream &in) { in >> tmp; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const56() { // ticket #3149 checkConst("class MyObject {\n" "public:\n" " void foo(int x) {\n" " switch (x) { }\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class A\n" "{\n" " protected:\n" " unsigned short f (unsigned short X);\n" " public:\n" " A ();\n" "};\n" "\n" "unsigned short A::f (unsigned short X)\n" "{\n" " enum ERetValues {RET_NOK = 0, RET_OK = 1};\n" " enum ETypes {FLOAT_TYPE = 1, INT_TYPE = 2};\n" "\n" " try\n" " {\n" " switch (X)\n" " {\n" " case FLOAT_TYPE:\n" " {\n" " return RET_OK;\n" " }\n" " case INT_TYPE:\n" " {\n" " return RET_OK;\n" " }\n" " default:\n" " {\n" " return RET_NOK;\n" " }\n" " }\n" " }\n" " catch (...)\n" " {\n" " return RET_NOK;\n" " }\n" "\n" " return RET_NOK;\n" "}"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'A::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("class MyObject {\n" "public:\n" " void foo(int x) {\n" " for (int i = 0; i < 5; i++) { }\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const57() { // tickets #2669 and #2477 checkConst("namespace MyGUI\n" "{\n" " namespace types\n" " {\n" " struct TSize {};\n" " struct TCoord {\n" " TSize size() const { }\n" " };\n" " }\n" " typedef types::TSize IntSize;\n" " typedef types::TCoord IntCoord;\n" "}\n" "class SelectorControl\n" "{\n" " MyGUI::IntSize getSize()\n" " {\n" " return mCoordValue.size();\n" " }\n" "private:\n" " MyGUI::IntCoord mCoordValue;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:15]: (style, inconclusive) Technically the member function 'SelectorControl::getSize' can be const.\n", "[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct Foo {\n" " Bar b;\n" " void foo(Foo f) {\n" " b.run();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Bar {\n" " int i = 0;\n" " void run() { i++; }\n" "};\n" "struct Foo {\n" " Bar b;\n" " void foo(Foo f) {\n" " b.run();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Bar {\n" " void run() const { }\n" "};\n" "struct Foo {\n" " Bar b;\n" " void foo(Foo f) {\n" " b.run();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Bar::run' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:6]: (style, inconclusive) Technically the member function 'Foo::foo' can be const.\n", errout.str()); } void const58() { checkConst("struct MyObject {\n" " void foo(Foo f) {\n" " f.clear();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct MyObject {\n" " int foo(Foo f) {\n" " return f.length();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct MyObject {\n" " Foo f;\n" " int foo() {\n" " return f.length();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct MyObject {\n" " std::string f;\n" " int foo() {\n" " return f.length();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'MyObject::foo' can be const.\n", errout.str()); } void const59() { // ticket #4646 checkConst("class C {\n" "public:\n" " inline void operator += (const int &x ) { re += x; }\n" " friend inline void exp(C & c, const C & x) { }\n" "protected:\n" " int re;\n" " int im;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const60() { // ticket #3322 checkConst("class MyString {\n" "public:\n" " MyString() : m_ptr(0){}\n" " MyString& operator+=( const MyString& rhs ) {\n" " delete m_ptr;\n" " m_ptr = new char[42];\n" " }\n" " MyString append( const MyString& str )\n" " { return operator+=( str ); } \n" " char *m_ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class MyString {\n" "public:\n" " MyString() : m_ptr(0){}\n" " MyString& operator+=( const MyString& rhs );\n" " MyString append( const MyString& str )\n" " { return operator+=( str ); } \n" " char *m_ptr;\n" "};"); ASSERT_EQUALS("", errout.str()); } void const61() { // ticket #5606 - don't crash checkConst("class MixerParticipant : public MixerParticipant {\n" " int GetAudioFrame();\n" "};\n" "int MixerParticipant::GetAudioFrame() {\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkConst("class MixerParticipant : public MixerParticipant {\n" " bool InitializeFileReader() {\n" " printf(\"music\");\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // Based on an example from SVN source code causing an endless recursion within CheckClass::isConstMemberFunc() // A more complete example including a template declaration like // template class Hash{/* ... */}; // didn't . checkConst("template<>\n" "class Hash {\n" "protected:\n" " typedef Key::key_type key_type;\n" " void set(const Key& key);\n" "};\n" "template\n" "class Hash : private Hash {\n" " typedef Hash inherited;\n" " void set(const Key& key) {\n" " inherited::set(inherited::Key(key));\n" " }\n" "};\n", nullptr, false); ASSERT_EQUALS("", errout.str()); } void const62() { checkConst("class A {\n" " private:\n" " std::unordered_map _hash;\n" " public:\n" " A() : _hash() {}\n" " unsigned int fetch(unsigned int key)\n" // cannot be 'const' " {\n" " return _hash[key];\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void const63() { checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " std::string* p = &s;\n" " p->clear();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " std::string& r = s;\n" " r.clear();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " std::string& r = sth; r = s;\n" " r.clear();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout.str()); checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " const std::string* p = &s;\n" " p->somefunction();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout.str()); checkConst("struct A {\n" " std::string s;\n" " void clear() {\n" " const std::string& r = s;\n" " r.somefunction();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout.str()); } void const64() { checkConst("namespace B {\n" " namespace D {\n" " typedef int DKIPtr;\n" " }\n" " class ZClass {\n" " void set(const ::B::D::DKIPtr& p) {\n" " membervariable = p;\n" " }\n" " ::B::D::DKIPtr membervariable;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void const65() { checkConst("template \n" "class TemplateClass {\n" "public:\n" " TemplateClass() { }\n" "};\n" "template <>\n" "class TemplateClass {\n" "public:\n" " TemplateClass() { }\n" "};\n" "int main() {\n" " TemplateClass a;\n" " TemplateClass b;\n" " return 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void const66() { checkConst("struct C {\n" " C() : n(0) {}\n" " void f(int v) { g((char *) &v); }\n" " void g(char *) { n++; }\n" " int n;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void const67() { // #9193 checkConst("template >\n" "class TestList {\n" "public:\n" " LIST_T m_list;\n" "};\n" "class Test {\n" "public:\n" " const std::list>& get() { return m_test.m_list; }\n" " TestList> m_test;\n" "};\n"); ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Test::get' can be const.\n", errout.str()); } void const_handleDefaultParameters() { checkConst("struct Foo {\n" " void foo1(int i, int j = 0) {\n" " return func(this);\n" " }\n" " int bar1() {\n" " return foo1(1);\n" " }\n" " int bar2() {\n" " return foo1(1, 2);\n" " }\n" " int bar3() {\n" " return foo1(1, 2, 3);\n" " }\n" " int bar4() {\n" " return foo1();\n" " }\n" " void foo2(int i = 0) {\n" " return func(this);\n" " }\n" " int bar5() {\n" " return foo2();\n" " }\n" " void foo3() {\n" " return func(this);\n" " }\n" " int bar6() {\n" " return foo3();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:11]: (performance, inconclusive) Technically the member function 'Foo::bar3' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:14]: (performance, inconclusive) Technically the member function 'Foo::bar4' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void const_passThisToMemberOfOtherClass() { checkConst("struct Foo {\n" " void foo() {\n" " Bar b;\n" " b.takeFoo(this);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct Foo {\n" " void foo() {\n" " Foo f;\n" " f.foo();\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Foo::foo' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A;\n" // #5839 - operator() "struct B {\n" " void operator()(A *a);\n" "};\n" "struct A {\n" " void dostuff() {\n" " B()(this);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void assigningPointerToPointerIsNotAConstOperation() { checkConst("struct s\n" "{\n" " int** v;\n" " void f()\n" " {\n" " v = 0;\n" " }\n" "};\n" ); ASSERT_EQUALS("", errout.str()); } void assigningArrayElementIsNotAConstOperation() { checkConst("struct s\n" "{\n" " ::std::string v[3];\n" " void f()\n" " {\n" " v[0] = \"Happy new year!\";\n" " }\n" "};\n" ); ASSERT_EQUALS("", errout.str()); } // increment/decrement => not const void constincdec() { checkConst("class Fred {\n" " int a;\n" " void nextA() { return ++a; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return --a; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a++; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a--; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return ++a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return --a; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a++; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a--; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassign1() { checkConst("class Fred {\n" " int a;\n" " void nextA() { return a=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a-=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a+=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a*=-1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a;\n" " void nextA() { return a/=-2; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a-=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a+=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a*=-1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a;\n" "class Fred {\n" " void nextA() { return a/=-2; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassign2() { checkConst("class Fred {\n" " struct A { int a; } s;\n" " void nextA() { return s.a=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " struct A { int a; } s;\n" " void nextA() { return s.a-=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " struct A { int a; } s;\n" " void nextA() { return s.a+=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " struct A { int a; } s;\n" " void nextA() { return s.a*=-1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a-=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a+=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a*=-1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; } s;\n" "class Fred {\n" " void nextA() { return s.a/=-2; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a-=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a+=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a*=-1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("struct A { int a; };\n" "class Fred {\n" " A s;\n" " void nextA() { return s.a/=-2; }\n" "};"); ASSERT_EQUALS("", errout.str()); } // increment/decrement array element => not const void constincdecarray() { checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return ++a[0]; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return --a[0]; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]++; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]--; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return ++a[0]; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return --a[0]; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]++; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]--; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } void constassignarray() { checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]-=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]+=1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]*=-1; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class Fred {\n" " int a[2];\n" " void nextA() { return a[0]/=-2; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]-=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]+=1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]*=-1; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst("int a[2];\n" "class Fred {\n" " void nextA() { return a[0]/=-2; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); } // return pointer/reference => not const void constReturnReference() { checkConst("class Fred {\n" " int a;\n" " int &getR() { return a; }\n" " int *getP() { return &a; }" "};"); ASSERT_EQUALS("", errout.str()); } // delete member variable => not const (but technically it can, it compiles without errors) void constDelete() { checkConst("class Fred {\n" " int *a;\n" " void clean() { delete a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } // A function that returns unknown types can't be const (#1579) void constLPVOID() { checkConst("class Fred {\n" " UNKNOWN a() { return 0; };\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::a' can be static.\n", "", errout.str()); // #1579 - HDC checkConst("class Fred {\n" " foo bar;\n" " UNKNOWN a() { return b; };\n" "};"); ASSERT_EQUALS("", errout.str()); } // a function that calls const functions can be const void constFunc() { checkConst("class Fred {\n" " void f() const { };\n" " void a() { f(); };\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n" "[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::a' can be const.\n", errout.str()); // ticket #1593 checkConst("class A\n" "{\n" " std::vector m_v;\n" "public:\n" " A(){}\n" " unsigned int GetVecSize() {return m_v.size();}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'A::GetVecSize' can be const.\n", errout.str()); checkConst("class A\n" "{\n" " std::vector m_v;\n" "public:\n" " A(){}\n" " bool GetVecEmpty() {return m_v.empty();}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'A::GetVecEmpty' can be const.\n", errout.str()); } void constVirtualFunc() { // base class has no virtual function checkConst("class A { };\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout.str()); checkConst("class A { };\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout.str()); // base class has no virtual function checkConst("class A {\n" "public:\n" " int func();\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout.str()); checkConst("class A {\n" "public:\n" " int func();\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }"); ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:9]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout.str()); // base class has virtual function checkConst("class A {\n" "public:\n" " virtual int func();\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " virtual int func();\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" "public:\n" " virtual int func() = 0;\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }"); ASSERT_EQUALS("", errout.str()); // base class has no virtual function checkConst("class A {\n" " int a;\n" "public:\n" " A() : a(0) { }\n" " int func() { return a; }\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};\n" "class C : public B {\n" " int c;\n" "public:\n" " C() : c(0) { }\n" " int func() { return c; }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::func' can be const.\n" "[test.cpp:11]: (style, inconclusive) Technically the member function 'B::func' can be const.\n" "[test.cpp:17]: (style, inconclusive) Technically the member function 'C::func' can be const.\n", errout.str()); checkConst("class A {\n" " int a;\n" "public:\n" " A() : a(0) { }\n" " int func();\n" "};\n" "int A::func() { return a; }\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }\n" "class C : public B {\n" " int c;\n" "public:\n" " C() : c(0) { }\n" " int func();\n" "};\n" "int C::func() { return c; }"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (style, inconclusive) Technically the member function 'A::func' can be const.\n" "[test.cpp:14] -> [test.cpp:12]: (style, inconclusive) Technically the member function 'B::func' can be const.\n" "[test.cpp:21] -> [test.cpp:19]: (style, inconclusive) Technically the member function 'C::func' can be const.\n", errout.str()); // base class has virtual function checkConst("class A {\n" " int a;\n" "public:\n" " A() : a(0) { }\n" " virtual int func() { return a; }\n" "};\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func() { return b; }\n" "};\n" "class C : public B {\n" " int c;\n" "public:\n" " C() : c(0) { }\n" " int func() { return c; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkConst("class A {\n" " int a;\n" "public:\n" " A() : a(0) { }\n" " virtual int func();\n" "};\n" "int A::func() { return a; }\n" "class B : public A {\n" " int b;\n" "public:\n" " B() : b(0) { }\n" " int func();\n" "};\n" "int B::func() { return b; }\n" "class C : public B {\n" " int c;\n" "public:\n" " C() : c(0) { }\n" " int func();\n" "};\n" "int C::func() { return c; }"); ASSERT_EQUALS("", errout.str()); // ticket #1311 checkConst("class X {\n" " int x;\n" "public:\n" " X(int x) : x(x) { }\n" " int getX() { return x; }\n" "};\n" "class Y : public X {\n" " int y;\n" "public:\n" " Y(int x, int y) : X(x), y(y) { }\n" " int getY() { return y; }\n" "};\n" "class Z : public Y {\n" " int z;\n" "public:\n" " Z(int x, int y, int z) : Y(x, y), z(z) { }\n" " int getZ() { return z; }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'X::getX' can be const.\n" "[test.cpp:11]: (style, inconclusive) Technically the member function 'Y::getY' can be const.\n" "[test.cpp:17]: (style, inconclusive) Technically the member function 'Z::getZ' can be const.\n", errout.str()); checkConst("class X {\n" " int x;\n" "public:\n" " X(int x) : x(x) { }\n" " int getX();\n" "};\n" "int X::getX() { return x; }\n" "class Y : public X {\n" " int y;\n" "public:\n" " Y(int x, int y) : X(x), y(y) { }\n" " int getY();\n" "};\n" "int Y::getY() { return y; }\n" "class Z : public Y {\n" " int z;\n" "public:\n" " Z(int x, int y, int z) : Y(x, y), z(z) { }\n" " int getZ();\n" "};\n" "int Z::getZ() { return z; }"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (style, inconclusive) Technically the member function 'X::getX' can be const.\n" "[test.cpp:14] -> [test.cpp:12]: (style, inconclusive) Technically the member function 'Y::getY' can be const.\n" "[test.cpp:21] -> [test.cpp:19]: (style, inconclusive) Technically the member function 'Z::getZ' can be const.\n", errout.str()); } void constIfCfg() { const char code[] = "struct foo {\n" " int i;\n" " void f() {\n" //"#ifdef ABC\n" //" i = 4;\n" //"endif\n" " }\n" "};"; checkConst(code, &settings0, true); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'foo::f' can be static (but you may consider moving to unnamed namespace).\n", errout.str()); checkConst(code, &settings0, false); // TODO: Set inconclusive to true (preprocess it) ASSERT_EQUALS("", errout.str()); } void constFriend() { // ticket #1921 const char code[] = "class foo {\n" " friend void f() { }\n" "};"; checkConst(code); ASSERT_EQUALS("", errout.str()); } void constUnion() { // ticket #2111 checkConst("class foo {\n" "public:\n" " union {\n" " int i;\n" " float f;\n" " } d;\n" " void setf(float x) {\n" " d.f = x;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void constArrayOperator() { checkConst("struct foo {\n" " int x;\n" " int y[5][724];\n" " T a() {\n" " return y[x++][6];\n" " }\n" " T b() {\n" " return y[1][++x];\n" " }\n" " T c() {\n" " return y[1][6];\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (style, inconclusive) Technically the member function 'foo::c' can be const.\n", errout.str()); } void constRangeBasedFor() { // #5514 checkConst("class Fred {\n" " int array[256];\n" "public:\n" " void f1() {\n" " for (auto & e : array)\n" " foo(e);\n" " }\n" " void f2() {\n" " for (const auto & e : array)\n" " foo(e);\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Fred::f2' can be const.\n", errout.str()); } void const_shared_ptr() { // #8674 checkConst("class Fred {\n" "public:\n" " std::shared_ptr getData();\n" "private:\n" " std::shared_ptr data;\n" "};\n" "\n" "std::shared_ptr Fred::getData() { return data; }"); ASSERT_EQUALS("", errout.str()); } void constPtrToConstPtr() { checkConst("class Fred {\n" "public:\n" " const char *const *data;\n" " const char *const *getData() { return data; }\n}"); ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::getData' can be const.\n", errout.str()); } void checkInitializerListOrder(const char code[]) { // Clear the error log errout.str(""); // Check.. settings0.inconclusive = true; // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); CheckClass checkClass(&tokenizer, &settings0, this); checkClass.initializerListOrder(); } void initializerListOrder() { checkInitializerListOrder("class Fred {\n" " int a, b, c;\n" "public:\n" " Fred() : c(0), b(0), a(0) { }\n" "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::b' is in the wrong place in the initializer list.\n" "[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::a' is in the wrong place in the initializer list.\n", errout.str()); checkInitializerListOrder("class Fred {\n" " int a, b, c;\n" "public:\n" " Fred() : c{0}, b{0}, a{0} { }\n" "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::b' is in the wrong place in the initializer list.\n" "[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::a' is in the wrong place in the initializer list.\n", errout.str()); } void checkInitializationListUsage(const char code[]) { // Clear the error log errout.str(""); // Check.. Settings settings; settings.addEnabled("performance"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); CheckClass checkClass(&tokenizer, &settings, this); checkClass.initializationListUsage(); } void initializerListUsage() { checkInitializationListUsage("enum Enum { C = 0 };\n" "class Fred {\n" " int a;\n" // No message for builtin types: No performance gain " int* b;\n" // No message for pointers: No performance gain " Enum c;\n" // No message for enums: No performance gain " Fred() { a = 0; b = 0; c = C; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string s;\n" " Fred() { a = 0; s = \"foo\"; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 's' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string& s;\n" // Message is invalid for references, since their initialization in initializer list is required anyway and behaves different from assignment (#5004) " Fred(const std::string& s_) : s(s_) { s = \"foo\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::vector v;\n" " Fred() { v = unknown; }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 'v' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C { std::string s; };\n" "class Fred {\n" " C c;\n" " Fred() { c = unknown; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C c;\n" " Fred() { c = unknown; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C c;\n" " Fred(Fred const & other) { c = other.c; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C c;\n" " Fred(Fred && other) { c = other.c; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a;\n" " Fred() { initB(); a = b; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a;\n" " Fred() : a(0) { if(b) a = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a[5];\n" " Fred() { for(int i = 0; i < 5; i++) a[i] = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a; int b;\n" " Fred() : b(5) { a = b; }\n" // Don't issue a message here: You actually could move it to the initialization list, but it would cause problems if you change the order of the variable declarations. "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class C;\n" "class Fred {\n" " C a;\n" " Fred() { try { a = new int; } catch(...) {} }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string s;\n" " Fred() { s = toString((size_t)this); }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string a;\n" " std::string foo();\n" " Fred() { a = foo(); }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" " std::string a;\n" " Fred() { a = foo(); }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 'a' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout.str()); checkInitializationListUsage("class Fred {\n" // #4332 " static std::string s;\n" " Fred() { s = \"foo\"; }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" // #5640 " std::string s;\n" " Fred() {\n" " char str[2];\n" " str[0] = c;\n" " str[1] = 0;\n" " s = str;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class B {\n" // #5640 " std::shared_ptr _d;\n" " B(const B& other) : _d(std::make_shared()) {\n" " *_d = *other._d;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Bar {\n" // #8466 "public:\n" " explicit Bar(const Bar &bar) : Bar{bar.s} {}\n" " explicit Bar(const char s) : s{s} {}\n" "private:\n" " char s;\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("unsigned bar(std::string);\n" // #8291 "class Foo {\n" "public:\n" " int a_, b_;\n" " Foo(int a, int b) : a_(a), b_(b) {}\n" " Foo(int a, const std::string& b) : Foo(a, bar(b)) {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkInitializationListUsage("class Fred {\n" // #8111 " std::string a;\n" " Fred() {\n" " std::ostringstream ostr;\n" " ostr << x;\n" " a = ostr.str();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // bailout: multi line lambda in rhs => do not warn checkInitializationListUsage("class Fred {\n" " std::function f;\n" " Fred() {\n" " f = [](){\n" " return 1;\n" " };\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // don't warn if some other instance's members are assigned to checkInitializationListUsage("class C {\n" "public:\n" " C(C& c) : m_i(c.m_i) { c.m_i = (Foo)-1; }\n" "private:\n" " Foo m_i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void checkSelfInitialization(const char code []) { // Clear the error log errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings0, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); CheckClass checkClass(&tokenizer, &settings0, this); checkClass.checkSelfInitialization(); } void selfInitialization() { checkSelfInitialization("class Fred {\n" " int i;\n" " Fred() : i(i) {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 'i' is initialized by itself.\n", errout.str()); checkSelfInitialization("class Fred {\n" " int i;\n" " Fred() : i{i} {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 'i' is initialized by itself.\n", errout.str()); checkSelfInitialization("class Fred {\n" " int i;\n" " Fred();\n" "};\n" "Fred::Fred() : i(i) {\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Member variable 'i' is initialized by itself.\n", errout.str()); checkSelfInitialization("class Fred {\n" " std::string s;\n" " Fred() : s(s) {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 's' is initialized by itself.\n", errout.str()); checkSelfInitialization("class Fred {\n" " int x;\n" " Fred(int x);\n" "};\n" "Fred::Fred(int x) : x(x) { }\n" ); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("class Fred {\n" " int x;\n" " Fred(int x);\n" "};\n" "Fred::Fred(int x) : x{x} { }\n" ); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("class Fred {\n" " std::string s;\n" " Fred(const std::string& s) : s(s) {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("class Fred {\n" " std::string s;\n" " Fred(const std::string& s) : s{s} {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("struct Foo : Bar {\n" " int i;\n" " Foo(int i)\n" " : Bar(""), i(i) {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("struct Foo : std::Bar {\n" // #6073 " int i;\n" " Foo(int i)\n" " : std::Bar(""), i(i) {}\n" "};"); ASSERT_EQUALS("", errout.str()); checkSelfInitialization("struct Foo : std::Bar {\n" // #6073 " int i;\n" " Foo(int i)\n" " : std::Bar(""), i{i} {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void checkVirtualFunctionCall(const char code[], Settings *s = nullptr, bool inconclusive = true) { // Clear the error log errout.str(""); // Check.. if (!s) { static Settings settings_; s = &settings_; s->addEnabled("warning"); } s->inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(s, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); CheckClass checkClass(&tokenizer, s, this); checkClass.checkVirtualFunctionCallInConstructor(); } void virtualFunctionCallInConstructor() { checkVirtualFunctionCall("class A\n" "{\n" " virtual int f() { return 1; }\n" " A();\n" "};\n" "A::A()\n" "{f();}\n"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Virtual function 'f' is called from constructor 'A()' at line 7. Dynamic binding is not used.\n", errout.str()); checkVirtualFunctionCall("class A {\n" " virtual int f();\n" " A() {f();}\n" "};\n" "int A::f() { return 1; }\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Virtual function 'f' is called from constructor 'A()' at line 3. Dynamic binding is not used.\n", errout.str()); checkVirtualFunctionCall("class A : B {\n" " int f() override;\n" " A() {f();}\n" "};\n" "int A::f() { return 1; }\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Virtual function 'f' is called from constructor 'A()' at line 3. Dynamic binding is not used.\n", errout.str()); checkVirtualFunctionCall("class B {\n" " virtual int f() = 0;\n" "};\n" "class A : B {\n" " int f();\n" " A() {f();}\n" "};\n" "int A::f() { return 1; }\n"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (warning) Virtual function 'f' is called from constructor 'A()' at line 6. Dynamic binding is not used.\n", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " A() { A::f(); }\n" " virtual void f() {}\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCall() { checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " A();\n" "};\n" "A::A()\n" "{pure();}\n"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " virtual int pure()=0;\n" " A();\n" " int m;\n" "};\n" "A::A():m(A::pure())\n" "{}\n"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0; \n" " virtual ~A(); \n" " int m; \n" "};\n" "A::~A()\n" "{pure();}\n"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure()\n" " {pure();}\n" " A(); \n" "};\n" "A::A()\n" "{nonpure();}\n"); ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual int pure()=0;\n" " int nonpure()\n" " {return pure();}\n" " A(); \n" " int m;\n" "};\n" "A::A():m(nonpure())\n" "{}\n"); TODO_ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", "", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0; \n" " void nonpure()\n" " {pure();}\n" " virtual ~A();\n" " int m;\n" "};\n" "A::~A()\n" "{nonpure();}\n"); ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " A(bool b);\n" "};\n" "A::A(bool b)\n" "{if (b) pure();}\n"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " virtual ~A();\n" " int m;\n" "};\n" "A::~A()\n" "{if (b) pure();}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout.str()); // #5831 checkVirtualFunctionCall("class abc {\n" "public:\n" " virtual ~abc() throw() {}\n" " virtual void def(void* g) throw () = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); // #4992 checkVirtualFunctionCall("class CMyClass {\n" " std::function< void(void) > m_callback;\n" "public:\n" " CMyClass() {\n" " m_callback = [this]() { return VirtualMethod(); };\n" " }\n" " virtual void VirtualMethod() = 0;\n" "};"); ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCallOtherClass() { checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " A(const A & a);\n" "};\n" "A::A(const A & a)\n" "{a.pure();}\n"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" "{\n" " virtual void pure()=0;\n" " A();\n" "};\n" "class B\n" "{\n" " virtual void pure()=0;\n" "};\n" "A::A()\n" "{B b; b.pure();}\n"); ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCallWithBody() { checkVirtualFunctionCall("class A\n" "{\n" " virtual void pureWithBody()=0;\n" " A();\n" "};\n" "A::A()\n" "{pureWithBody();}\n" "void A::pureWithBody()\n" "{}\n"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pureWithBody()=0;\n" " void nonpure()\n" " {pureWithBody();}\n" " A(); \n" "};\n" "A::A()\n" "{nonpure();}\n" "void A::pureWithBody()\n" "{}\n"); ASSERT_EQUALS("", errout.str()); } void pureVirtualFunctionCallPrevented() { checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure(bool bCallPure)\n" " { if (bCallPure) pure();}\n" " A(); \n" "};\n" "A::A()\n" "{nonpure(false);}\n"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure(bool bCallPure)\n" " { if (!bCallPure) ; else pure();}\n" " A(); \n" "};\n" "A::A()\n" "{nonpure(false);}\n"); ASSERT_EQUALS("", errout.str()); checkVirtualFunctionCall("class A\n" " {\n" " virtual void pure()=0;\n" " void nonpure(bool bCallPure)\n" " {\n" " switch (bCallPure) {\n" " case true: pure(); break;\n" " }\n" " }\n" " A(); \n" "};\n" "A::A()\n" "{nonpure(false);}\n"); ASSERT_EQUALS("", errout.str()); } void checkOverride(const char code[]) { // Clear the error log errout.str(""); Settings settings; settings.addEnabled("style"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.checkOverride(); } void override1() { checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { virtual void f(); };"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { virtual void f() override; };"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { virtual void f() final; };"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base {\n" "public:\n" " virtual auto foo( ) const -> size_t { return 1; }\n" " virtual auto bar( ) const -> size_t { return 1; }\n" "};\n" "class Derived : public Base {\n" "public :\n" " auto foo( ) const -> size_t { return 0; }\n" " auto bar( ) const -> size_t override { return 0; }\n" "};"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:8]: (style) The function 'foo' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout.str()); checkOverride("namespace Test {\n" " class C {\n" " public:\n" " virtual ~C();\n" " };\n" "}\n" "class C : Test::C {\n" "public:\n" " ~C();\n" "};"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:9]: (style) The destructor '~C' overrides a destructor in a base class but is not marked with a 'override' specifier.\n", errout.str()); checkOverride("struct Base {\n" " virtual void foo();\n" "};\n" "\n" "struct Derived: public Base {\n" " void foo() override;\n" " void foo(int);\n" "};"); ASSERT_EQUALS("", errout.str()); } void overrideCVRefQualifiers() { checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { void f() const; }"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { void f() volatile; }"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { void f() &; }"); ASSERT_EQUALS("", errout.str()); checkOverride("class Base { virtual void f(); };\n" "class Derived : Base { void f() &&; }"); ASSERT_EQUALS("", errout.str()); } void checkUnsafeClassRefMember(const char code[]) { // Clear the error log errout.str(""); Settings settings; settings.safeChecks.classes = true; settings.addEnabled("warning"); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.checkUnsafeClassRefMember(); } void unsafeClassRefMember() { checkUnsafeClassRefMember("class C { C(const std::string &s) : s(s) {} const std::string &s; };"); ASSERT_EQUALS("[test.cpp:1]: (warning) Unsafe class: The const reference member 'C::s' is initialized by a const reference constructor argument. You need to be careful about lifetime issues.\n", errout.str()); } }; REGISTER_TEST(TestClass) cppcheck-1.90/test/testcmdlineparser.cpp000066400000000000000000001120321357737443600204600ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cmdlineparser.h" #include "platform.h" #include "redirect.h" #include "settings.h" #include "standards.h" #include "suppressions.h" #include "testsuite.h" #include "timer.h" #include #include #include #include #include class TestCmdlineParser : public TestFixture { public: TestCmdlineParser() : TestFixture("TestCmdlineParser") , defParser(&settings) { } private: Settings settings; CmdLineParser defParser; void run() OVERRIDE { TEST_CASE(nooptions); TEST_CASE(helpshort); TEST_CASE(helplong); TEST_CASE(showversion); TEST_CASE(onefile); TEST_CASE(onepath); TEST_CASE(optionwithoutfile); TEST_CASE(verboseshort); TEST_CASE(verboselong); TEST_CASE(debugSimplified); TEST_CASE(debugwarnings); TEST_CASE(forceshort); TEST_CASE(forcelong); TEST_CASE(relativePaths); TEST_CASE(quietshort); TEST_CASE(quietlong); TEST_CASE(defines_noarg); TEST_CASE(defines_noarg2); TEST_CASE(defines_noarg3); TEST_CASE(defines); TEST_CASE(defines2); TEST_CASE(defines3); TEST_CASE(defines4); TEST_CASE(enforceLanguage); TEST_CASE(includesnopath); TEST_CASE(includes); TEST_CASE(includesslash); TEST_CASE(includesbackslash); TEST_CASE(includesnospace); TEST_CASE(includes2); TEST_CASE(includesFile); TEST_CASE(configExcludesFile); TEST_CASE(enabledAll); TEST_CASE(enabledStyle); TEST_CASE(enabledPerformance); TEST_CASE(enabledPortability); TEST_CASE(enabledUnusedFunction); TEST_CASE(enabledMissingInclude); #ifdef CHECK_INTERNAL TEST_CASE(enabledInternal); #endif TEST_CASE(enabledMultiple); TEST_CASE(inconclusive); TEST_CASE(errorExitcode); TEST_CASE(errorExitcodeMissing); TEST_CASE(errorExitcodeStr); TEST_CASE(exitcodeSuppressionsOld); // TODO: Create and test real suppression file TEST_CASE(exitcodeSuppressions); TEST_CASE(exitcodeSuppressionsNoFile); TEST_CASE(fileList); // TODO: Create and test real file listing file // TEST_CASE(fileListStdin); // Disabled since hangs the test run TEST_CASE(inlineSuppr); TEST_CASE(jobs); TEST_CASE(jobsMissingCount); TEST_CASE(jobsInvalid); TEST_CASE(maxConfigs); TEST_CASE(maxConfigsMissingCount); TEST_CASE(maxConfigsInvalid); TEST_CASE(maxConfigsTooSmall); TEST_CASE(reportProgressTest); // "Test" suffix to avoid hiding the parent's reportProgress TEST_CASE(stdc99); TEST_CASE(stdcpp11); TEST_CASE(platform); TEST_CASE(suppressionsOld); // TODO: Create and test real suppression file TEST_CASE(suppressions); TEST_CASE(suppressionsNoFile); TEST_CASE(suppressionSingle); TEST_CASE(suppressionSingleFile); TEST_CASE(suppressionTwo); TEST_CASE(suppressionTwoSeparate); TEST_CASE(templates); TEST_CASE(templatesGcc); TEST_CASE(templatesVs); TEST_CASE(templatesEdit); TEST_CASE(xml); TEST_CASE(xmlver2); TEST_CASE(xmlver2both); TEST_CASE(xmlver2both2); TEST_CASE(xmlverunknown); TEST_CASE(xmlverinvalid); TEST_CASE(doc); TEST_CASE(showtime); TEST_CASE(errorlist1); TEST_CASE(errorlistverbose1); TEST_CASE(errorlistverbose2); TEST_CASE(ignorepathsnopath); // Disabling these tests since they use relative paths to the // testrunner executable. //TEST_CASE(ignorepaths1); //TEST_CASE(ignorepaths2); //TEST_CASE(ignorepaths3); //TEST_CASE(ignorepaths4); //TEST_CASE(ignorefilepaths1); //TEST_CASE(ignorefilepaths2); TEST_CASE(checkconfig); TEST_CASE(unknownParam); TEST_CASE(undefs_noarg); TEST_CASE(undefs_noarg2); TEST_CASE(undefs_noarg3); TEST_CASE(undefs); TEST_CASE(undefs2); } void nooptions() { REDIRECT; const char * const argv[] = {"cppcheck"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(1, argv)); ASSERT_EQUALS(true, parser.getShowHelp()); } void helpshort() { REDIRECT; const char * const argv[] = {"cppcheck", "-h"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.getShowHelp()); } void helplong() { REDIRECT; const char * const argv[] = {"cppcheck", "--help"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.getShowHelp()); } void showversion() { REDIRECT; const char * const argv[] = {"cppcheck", "--version"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(true, parser.getShowVersion()); } void onefile() { REDIRECT; const char * const argv[] = {"cppcheck", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(1, (int)parser.getPathNames().size()); ASSERT_EQUALS("file.cpp", parser.getPathNames().at(0)); } void onepath() { REDIRECT; const char * const argv[] = {"cppcheck", "src"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(2, argv)); ASSERT_EQUALS(1, (int)parser.getPathNames().size()); ASSERT_EQUALS("src", parser.getPathNames().at(0)); } void optionwithoutfile() { REDIRECT; const char * const argv[] = {"cppcheck", "-v"}; CmdLineParser parser(&settings); ASSERT_EQUALS(false, parser.parseFromArgs(2, argv)); ASSERT_EQUALS(0, (int)parser.getPathNames().size()); } void verboseshort() { REDIRECT; const char * const argv[] = {"cppcheck", "-v", "file.cpp"}; settings.verbose = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.verbose); } void verboselong() { REDIRECT; const char * const argv[] = {"cppcheck", "--verbose", "file.cpp"}; settings.verbose = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.verbose); } void debugSimplified() { REDIRECT; const char * const argv[] = {"cppcheck", "--debug-simplified", "file.cpp"}; settings.debugSimplified = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.debugSimplified); } void debugwarnings() { REDIRECT; const char * const argv[] = {"cppcheck", "--debug-warnings", "file.cpp"}; settings.debugwarnings = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.debugwarnings); } void forceshort() { REDIRECT; const char * const argv[] = {"cppcheck", "-f", "file.cpp"}; settings.force = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.force); } void forcelong() { REDIRECT; const char * const argv[] = {"cppcheck", "--force", "file.cpp"}; settings.force = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.force); } void relativePaths() { REDIRECT; settings.relativePaths = false; const char * const argvs[] = {"cppcheck", "-rp", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argvs)); ASSERT_EQUALS(true, settings.relativePaths); settings.relativePaths = false; const char * const argvl[] = {"cppcheck", "--relative-paths", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argvl)); ASSERT_EQUALS(true, settings.relativePaths); settings.relativePaths = false; settings.basePaths.clear(); const char * const argvsp[] = {"cppcheck", "-rp=C:/foo;C:\\bar", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argvsp)); ASSERT_EQUALS(true, settings.relativePaths); ASSERT_EQUALS(2, settings.basePaths.size()); ASSERT_EQUALS("C:/foo", settings.basePaths[0]); ASSERT_EQUALS("C:/bar", settings.basePaths[1]); settings.relativePaths = false; settings.basePaths.clear(); const char * const argvlp[] = {"cppcheck", "--relative-paths=C:/foo;C:\\bar", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argvlp)); ASSERT_EQUALS(true, settings.relativePaths); ASSERT_EQUALS(2, settings.basePaths.size()); ASSERT_EQUALS("C:/foo", settings.basePaths[0]); ASSERT_EQUALS("C:/bar", settings.basePaths[1]); } void quietshort() { REDIRECT; const char * const argv[] = {"cppcheck", "-q", "file.cpp"}; settings.quiet = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.quiet); } void quietlong() { REDIRECT; const char * const argv[] = {"cppcheck", "--quiet", "file.cpp"}; settings.quiet = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.quiet); } void defines_noarg() { REDIRECT; const char * const argv[] = {"cppcheck", "-D"}; // Fails since -D has no param ASSERT_EQUALS(false, defParser.parseFromArgs(2, argv)); } void defines_noarg2() { REDIRECT; const char * const argv[] = {"cppcheck", "-D", "-v", "file.cpp"}; // Fails since -D has no param ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void defines_noarg3() { REDIRECT; const char * const argv[] = {"cppcheck", "-D", "--quiet", "file.cpp"}; // Fails since -D has no param ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void defines() { REDIRECT; const char * const argv[] = {"cppcheck", "-D_WIN32", "file.cpp"}; settings.userDefines.clear(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS("_WIN32=1", settings.userDefines); } void defines2() { REDIRECT; const char * const argv[] = {"cppcheck", "-D_WIN32", "-DNODEBUG", "file.cpp"}; settings.userDefines.clear();; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("_WIN32=1;NODEBUG=1", settings.userDefines); } void defines3() { REDIRECT; const char * const argv[] = {"cppcheck", "-D", "DEBUG", "file.cpp"}; settings.userDefines.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("DEBUG=1", settings.userDefines); } void defines4() { REDIRECT; const char * const argv[] = {"cppcheck", "-DDEBUG=", "file.cpp"}; // #5137 - defining empty macro settings.userDefines.clear(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS("DEBUG=", settings.userDefines); } void enforceLanguage() { REDIRECT; { const char * const argv[] = {"cppcheck", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT_EQUALS(Settings::None, settings.enforcedLang); } { const char * const argv[] = {"cppcheck", "-x", "c++", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(Settings::CPP, settings.enforcedLang); } { const char * const argv[] = {"cppcheck", "-x"}; ASSERT(!defParser.parseFromArgs(2, argv)); } { const char * const argv[] = {"cppcheck", "-x", "--inconclusive", "file.cpp"}; ASSERT(!defParser.parseFromArgs(4, argv)); } { const char * const argv[] = {"cppcheck", "--language=c++", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(Settings::CPP, settings.enforcedLang); } { const char * const argv[] = {"cppcheck", "--language=c", "file.cpp"}; settings.enforcedLang = Settings::None; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(Settings::C, settings.enforcedLang); } { const char * const argv[] = {"cppcheck", "--language=unknownLanguage", "file.cpp"}; ASSERT(!defParser.parseFromArgs(3, argv)); } } void includesnopath() { REDIRECT; const char * const argv[] = {"cppcheck", "-I"}; // Fails since -I has no param ASSERT_EQUALS(false, defParser.parseFromArgs(2, argv)); } void includes() { REDIRECT; const char * const argv[] = {"cppcheck", "-I", "include", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includesslash() { REDIRECT; const char * const argv[] = {"cppcheck", "-I", "include/", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includesbackslash() { REDIRECT; const char * const argv[] = {"cppcheck", "-I", "include\\", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includesnospace() { REDIRECT; const char * const argv[] = {"cppcheck", "-Iinclude", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); } void includes2() { REDIRECT; const char * const argv[] = {"cppcheck", "-I", "include/", "-I", "framework/", "file.cpp"}; settings.includePaths.clear(); ASSERT(defParser.parseFromArgs(6, argv)); ASSERT_EQUALS("include/", settings.includePaths.front()); settings.includePaths.pop_front(); ASSERT_EQUALS("framework/", settings.includePaths.front()); } void includesFile() { REDIRECT; const char * const argv[] = {"cppcheck", "--includes-file=fileThatDoesNotExist.txt", "file.cpp"}; settings.includePaths.clear(); ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void configExcludesFile() { REDIRECT; const char * const argv[] = {"cppcheck", "--config-excludes-file=fileThatDoesNotExist.txt", "file.cpp"}; settings.includePaths.clear(); ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void enabledAll() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=all", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.isEnabled(Settings::STYLE)); ASSERT(settings.isEnabled(Settings::WARNING)); ASSERT(settings.isEnabled(Settings::UNUSED_FUNCTION)); ASSERT(settings.isEnabled(Settings::MISSING_INCLUDE)); ASSERT(!settings.isEnabled(Settings::INTERNAL)); } void enabledStyle() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=style", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.isEnabled(Settings::STYLE)); ASSERT(settings.isEnabled(Settings::WARNING)); ASSERT(settings.isEnabled(Settings::PERFORMANCE)); ASSERT(settings.isEnabled(Settings::PORTABILITY)); ASSERT(!settings.isEnabled(Settings::UNUSED_FUNCTION)); ASSERT(!settings.isEnabled(Settings::MISSING_INCLUDE)); } void enabledPerformance() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=performance", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(!settings.isEnabled(Settings::STYLE)); ASSERT(!settings.isEnabled(Settings::WARNING)); ASSERT(settings.isEnabled(Settings::PERFORMANCE)); ASSERT(!settings.isEnabled(Settings::PORTABILITY)); ASSERT(!settings.isEnabled(Settings::UNUSED_FUNCTION)); ASSERT(!settings.isEnabled(Settings::MISSING_INCLUDE)); } void enabledPortability() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=portability", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(!settings.isEnabled(Settings::STYLE)); ASSERT(!settings.isEnabled(Settings::WARNING)); ASSERT(!settings.isEnabled(Settings::PERFORMANCE)); ASSERT(settings.isEnabled(Settings::PORTABILITY)); ASSERT(!settings.isEnabled(Settings::UNUSED_FUNCTION)); ASSERT(!settings.isEnabled(Settings::MISSING_INCLUDE)); } void enabledUnusedFunction() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=unusedFunction", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.isEnabled(Settings::UNUSED_FUNCTION)); } void enabledMissingInclude() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=missingInclude", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.isEnabled(Settings::MISSING_INCLUDE)); } #ifdef CHECK_INTERNAL void enabledInternal() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=internal", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.isEnabled(Settings::INTERNAL)); } #endif void enabledMultiple() { REDIRECT; const char * const argv[] = {"cppcheck", "--enable=missingInclude,portability,warning", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(!settings.isEnabled(Settings::STYLE)); ASSERT(settings.isEnabled(Settings::WARNING)); ASSERT(!settings.isEnabled(Settings::PERFORMANCE)); ASSERT(settings.isEnabled(Settings::PORTABILITY)); ASSERT(!settings.isEnabled(Settings::UNUSED_FUNCTION)); ASSERT(settings.isEnabled(Settings::MISSING_INCLUDE)); } void inconclusive() { REDIRECT; const char * const argv[] = {"cppcheck", "--inconclusive"}; settings.inconclusive = false; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT_EQUALS(true, settings.inconclusive); } void errorExitcode() { REDIRECT; const char * const argv[] = {"cppcheck", "--error-exitcode=5", "file.cpp"}; settings.exitCode = 0; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(5, settings.exitCode); } void errorExitcodeMissing() { REDIRECT; const char * const argv[] = {"cppcheck", "--error-exitcode=", "file.cpp"}; settings.exitCode = 0; // Fails since exit code not given ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void errorExitcodeStr() { REDIRECT; const char * const argv[] = {"cppcheck", "--error-exitcode=foo", "file.cpp"}; settings.exitCode = 0; // Fails since invalid exit code ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void exitcodeSuppressionsOld() { // TODO: Fails since cannot open the file REDIRECT; const char * const argv[] = {"cppcheck", "--exitcode-suppressions", "suppr.txt", "file.cpp"}; settings.exitCode = 0; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(4, argv)); } void exitcodeSuppressions() { // TODO: Fails since cannot open the file REDIRECT; const char * const argv[] = {"cppcheck", "--exitcode-suppressions=suppr.txt", "file.cpp"}; settings.exitCode = 0; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); } void exitcodeSuppressionsNoFile() { // TODO: Fails since cannot open the file REDIRECT; const char * const argv[] = {"cppcheck", "--exitcode-suppressions", "file.cpp"}; settings.exitCode = 0; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); } void fileList() { // TODO: Fails since cannot open the file REDIRECT; const char * const argv[] = {"cppcheck", "--file-list", "files.txt", "file.cpp"}; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(4, argv)); } /* void fileListStdin() { // TODO: Give it some stdin to read from, fails because the list of // files in stdin (_pathnames) is empty REDIRECT; const char * const argv[] = {"cppcheck", "--file-list=-", "file.cpp"}; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); } */ void inlineSuppr() { REDIRECT; const char * const argv[] = {"cppcheck", "--inline-suppr", "file.cpp"}; ASSERT(defParser.parseFromArgs(3, argv)); } void jobs() { REDIRECT; const char * const argv[] = {"cppcheck", "-j", "3", "file.cpp"}; settings.jobs = 0; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(3, settings.jobs); } void jobsMissingCount() { REDIRECT; const char * const argv[] = {"cppcheck", "-j", "file.cpp"}; settings.jobs = 0; // Fails since -j is missing thread count ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void jobsInvalid() { REDIRECT; const char * const argv[] = {"cppcheck", "-j", "e", "file.cpp"}; settings.jobs = 0; // Fails since invalid count given for -j ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void maxConfigs() { REDIRECT; const char * const argv[] = {"cppcheck", "-f", "--max-configs=12", "file.cpp"}; settings.force = false; settings.maxConfigs = 12; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(12, settings.maxConfigs); ASSERT_EQUALS(false, settings.force); } void maxConfigsMissingCount() { REDIRECT; const char * const argv[] = {"cppcheck", "--max-configs=", "file.cpp"}; // Fails since --max-configs= is missing limit ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void maxConfigsInvalid() { REDIRECT; const char * const argv[] = {"cppcheck", "--max-configs=e", "file.cpp"}; // Fails since invalid count given for --max-configs= ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void maxConfigsTooSmall() { REDIRECT; const char * const argv[] = {"cppcheck", "--max-configs=0", "file.cpp"}; // Fails since limit must be greater than 0 ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); } void reportProgressTest() { REDIRECT; const char * const argv[] = {"cppcheck", "--report-progress", "file.cpp"}; settings.reportProgress = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.reportProgress); } void stdc99() { REDIRECT; const char * const argv[] = {"cppcheck", "--std=c99", "file.cpp"}; settings.standards.c = Standards::C89; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.standards.c == Standards::C99); } void stdcpp11() { REDIRECT; const char * const argv[] = {"cppcheck", "--std=c++11", "file.cpp"}; settings.standards.cpp = Standards::CPP03; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.standards.cpp == Standards::CPP11); } void platform() { REDIRECT; const char * const argv[] = {"cppcheck", "--platform=win64", "file.cpp"}; settings.platform(Settings::Unspecified); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.platformType == Settings::Win64); } void suppressionsOld() { // TODO: Fails because there is no suppr.txt file! REDIRECT; const char * const argv[] = {"cppcheck", "--suppressions", "suppr.txt", "file.cpp"}; ASSERT(!defParser.parseFromArgs(4, argv)); } void suppressions() { // TODO: Fails because there is no suppr.txt file! REDIRECT; const char * const argv[] = {"cppcheck", "--suppressions-list=suppr.txt", "file.cpp"}; TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); } void suppressionsNoFile() { REDIRECT; { CLEAR_REDIRECT_OUTPUT; const char * const argv[] = {"cppcheck", "--suppressions-list=", "file.cpp"}; ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(false, GET_REDIRECT_OUTPUT.find("If you want to pass two files") != std::string::npos); } { CLEAR_REDIRECT_OUTPUT; const char * const argv[] = {"cppcheck", "--suppressions-list=a.suppr,b.suppr", "file.cpp"}; ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, GET_REDIRECT_OUTPUT.find("If you want to pass two files") != std::string::npos); } { CLEAR_REDIRECT_OUTPUT; const char * const argv[] = {"cppcheck", "--suppressions-list=a.suppr b.suppr", "file.cpp"}; ASSERT_EQUALS(false, defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, GET_REDIRECT_OUTPUT.find("If you want to pass two files") != std::string::npos); } } static Suppressions::ErrorMessage errorMessage(const std::string &errorId, const std::string &fileName, int lineNumber) { Suppressions::ErrorMessage e; e.errorId = errorId; e.setFileName(fileName); e.lineNumber = lineNumber; return e; } void suppressionSingle() { REDIRECT; const char * const argv[] = {"cppcheck", "--suppress=uninitvar", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1))); } void suppressionSingleFile() { REDIRECT; const char * const argv[] = {"cppcheck", "--suppress=uninitvar:file.cpp", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); } void suppressionTwo() { REDIRECT; const char * const argv[] = {"cppcheck", "--suppress=uninitvar,noConstructor", "file.cpp"}; settings = Settings(); TODO_ASSERT_EQUALS(true, false, defParser.parseFromArgs(3, argv)); TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); TODO_ASSERT_EQUALS(true, false, settings.nomsg.isSuppressed(errorMessage("noConstructor", "file.cpp", 1U))); } void suppressionTwoSeparate() { REDIRECT; const char * const argv[] = {"cppcheck", "--suppress=uninitvar", "--suppress=noConstructor", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); ASSERT_EQUALS(true, settings.nomsg.isSuppressed(errorMessage("noConstructor", "file.cpp", 1U))); } void templates() { REDIRECT; const char * const argv[] = {"cppcheck", "--template", "{file}:{line},{severity},{id},{message}", "file.cpp"}; settings.templateFormat.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("{file}:{line},{severity},{id},{message}", settings.templateFormat); } void templatesGcc() { REDIRECT; const char * const argv[] = {"cppcheck", "--template", "gcc", "file.cpp"}; settings.templateFormat.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("{file}:{line}:{column}: warning: {message} [{id}]\\n{code}", settings.templateFormat); } void templatesVs() { REDIRECT; const char * const argv[] = {"cppcheck", "--template", "vs", "file.cpp"}; settings.templateFormat.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("{file}({line}): {severity}: {message}", settings.templateFormat); } void templatesEdit() { REDIRECT; const char * const argv[] = {"cppcheck", "--template", "edit", "file.cpp"}; settings.templateFormat.clear(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS("{file} +{line}: {severity}: {message}", settings.templateFormat); } void xml() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml", "file.cpp"}; settings.xml_version = 1; settings.xml = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.xml); ASSERT_EQUALS(1, settings.xml_version); } void xmlver2() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml-version=2", "file.cpp"}; settings.xml_version = 1; settings.xml = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.xml); ASSERT_EQUALS(2, settings.xml_version); } void xmlver2both() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml", "--xml-version=2", "file.cpp"}; settings.xml_version = 1; settings.xml = false; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT(settings.xml); ASSERT_EQUALS(2, settings.xml_version); } void xmlver2both2() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml-version=2", "--xml", "file.cpp"}; settings.xml_version = 1; settings.xml = false; ASSERT(defParser.parseFromArgs(4, argv)); ASSERT(settings.xml); ASSERT_EQUALS(2, settings.xml_version); } void xmlverunknown() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml", "--xml-version=3", "file.cpp"}; // FAils since unknown XML format version ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void xmlverinvalid() { REDIRECT; const char * const argv[] = {"cppcheck", "--xml", "--xml-version=a", "file.cpp"}; // FAils since unknown XML format version ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void doc() { REDIRECT; const char * const argv[] = {"cppcheck", "--doc"}; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT(defParser.exitAfterPrinting()); } void showtime() { REDIRECT; const char * const argv[] = {"cppcheck", "--showtime=summary"}; settings.showtime = SHOWTIME_MODES::SHOWTIME_NONE; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT(settings.showtime == SHOWTIME_MODES::SHOWTIME_SUMMARY); } void errorlist1() { REDIRECT; const char * const argv[] = {"cppcheck", "--errorlist"}; ASSERT(defParser.parseFromArgs(2, argv)); ASSERT(defParser.getShowErrorMessages()); } void errorlistverbose1() { REDIRECT; const char * const argv[] = {"cppcheck", "--verbose", "--errorlist"}; settings.verbose = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.verbose); } void errorlistverbose2() { REDIRECT; const char * const argv[] = {"cppcheck", "--errorlist", "--verbose"}; settings.verbose = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT(settings.verbose); } void ignorepathsnopath() { REDIRECT; const char * const argv[] = {"cppcheck", "-i"}; CmdLineParser parser(&settings); // Fails since no ignored path given ASSERT_EQUALS(false, parser.parseFromArgs(2, argv)); ASSERT_EQUALS(0, parser.getIgnoredPaths().size()); } /* void ignorepaths1() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(3, argv)); ASSERT_EQUALS(1, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/", parser.getIgnoredPaths()[0]); } void ignorepaths2() { REDIRECT; const char * const argv[] = {"cppcheck", "-i", "src", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(4, argv)); ASSERT_EQUALS(1, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/", parser.getIgnoredPaths()[0]); } void ignorepaths3() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc", "-imodule", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(4, argv)); ASSERT_EQUALS(2, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/", parser.getIgnoredPaths()[0]); ASSERT_EQUALS("module/", parser.getIgnoredPaths()[1]); } */ void ignorepaths4() { REDIRECT; const char * const argv[] = {"cppcheck", "-i", "src", "-i", "module", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(6, argv)); ASSERT_EQUALS(2, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/", parser.getIgnoredPaths()[0]); ASSERT_EQUALS("module/", parser.getIgnoredPaths()[1]); } /* void ignorefilepaths1() { REDIRECT; const char * const argv[] = {"cppcheck", "-ifoo.cpp", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(3, argv)); ASSERT_EQUALS(1, parser.getIgnoredPaths().size()); ASSERT_EQUALS("foo.cpp", parser.getIgnoredPaths()[0]); } */ void ignorefilepaths2() { REDIRECT; const char * const argv[] = {"cppcheck", "-isrc/foo.cpp", "file.cpp"}; CmdLineParser parser(&settings); ASSERT(parser.parseFromArgs(3, argv)); ASSERT_EQUALS(1, parser.getIgnoredPaths().size()); ASSERT_EQUALS("src/foo.cpp", parser.getIgnoredPaths()[0]); } void checkconfig() { REDIRECT; const char * const argv[] = {"cppcheck", "--check-config", "file.cpp"}; settings.checkConfiguration = false; ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(true, settings.checkConfiguration); } void unknownParam() { REDIRECT; const char * const argv[] = {"cppcheck", "--foo", "file.cpp"}; ASSERT(!defParser.parseFromArgs(3, argv)); } void undefs() { REDIRECT; const char * const argv[] = {"cppcheck", "-U_WIN32", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(3, argv)); ASSERT_EQUALS(1, settings.userUndefs.size()); ASSERT(settings.userUndefs.find("_WIN32") != settings.userUndefs.end()); } void undefs2() { REDIRECT; const char * const argv[] = {"cppcheck", "-U_WIN32", "-UNODEBUG", "file.cpp"}; settings = Settings(); ASSERT(defParser.parseFromArgs(4, argv)); ASSERT_EQUALS(2, settings.userUndefs.size()); ASSERT(settings.userUndefs.find("_WIN32") != settings.userUndefs.end()); ASSERT(settings.userUndefs.find("NODEBUG") != settings.userUndefs.end()); } void undefs_noarg() { REDIRECT; const char * const argv[] = {"cppcheck", "-U"}; // Fails since -U has no param ASSERT_EQUALS(false, defParser.parseFromArgs(2, argv)); } void undefs_noarg2() { REDIRECT; const char * const argv[] = {"cppcheck", "-U", "-v", "file.cpp"}; // Fails since -U has no param ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } void undefs_noarg3() { REDIRECT; const char * const argv[] = {"cppcheck", "-U", "--quiet", "file.cpp"}; // Fails since -U has no param ASSERT_EQUALS(false, defParser.parseFromArgs(4, argv)); } }; REGISTER_TEST(TestCmdlineParser) cppcheck-1.90/test/testcondition.cpp000066400000000000000000004204351357737443600176270ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkcondition.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include class TestCondition : public TestFixture { public: TestCondition() : TestFixture("TestCondition") { } private: Settings settings0; Settings settings1; void run() OVERRIDE { LOAD_LIB_2(settings0.library, "qt.cfg"); LOAD_LIB_2(settings0.library, "std.cfg"); settings0.addEnabled("style"); settings0.addEnabled("warning"); const char cfg[] = "\n" "\n" " \n" ""; tinyxml2::XMLDocument xmldoc; xmldoc.Parse(cfg, sizeof(cfg)); settings1.addEnabled("style"); settings1.addEnabled("warning"); settings1.library.load(xmldoc); TEST_CASE(assignAndCompare); // assignment and comparison don't match TEST_CASE(mismatchingBitAnd); // overlapping bitmasks TEST_CASE(comparison); // CheckCondition::comparison test cases TEST_CASE(multicompare); // mismatching comparisons TEST_CASE(overlappingElseIfCondition); // overlapping conditions in if and else-if TEST_CASE(oppositeElseIfCondition); // opposite conditions in if and else-if TEST_CASE(checkBadBitmaskCheck); TEST_CASE(incorrectLogicOperator1); TEST_CASE(incorrectLogicOperator2); TEST_CASE(incorrectLogicOperator3); TEST_CASE(incorrectLogicOperator4); TEST_CASE(incorrectLogicOperator5); // complex expressions TEST_CASE(incorrectLogicOperator6); // char literals TEST_CASE(incorrectLogicOperator7); // opposite expressions: (expr || !expr) TEST_CASE(incorrectLogicOperator8); // ! TEST_CASE(incorrectLogicOperator9); TEST_CASE(incorrectLogicOperator10); // enum TEST_CASE(incorrectLogicOperator11); TEST_CASE(incorrectLogicOperator12); TEST_CASE(incorrectLogicOperator13); TEST_CASE(incorrectLogicOperator14); TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError); TEST_CASE(incorrectLogicOp_condSwapping); TEST_CASE(testBug5895); TEST_CASE(testBug5309); TEST_CASE(modulo); TEST_CASE(oppositeInnerCondition); TEST_CASE(oppositeInnerConditionPointers); TEST_CASE(oppositeInnerConditionClass); TEST_CASE(oppositeInnerConditionUndeclaredVariable); TEST_CASE(oppositeInnerConditionAlias); TEST_CASE(oppositeInnerCondition2); TEST_CASE(oppositeInnerCondition3); TEST_CASE(oppositeInnerConditionAnd); TEST_CASE(oppositeInnerConditionEmpty); TEST_CASE(oppositeInnerConditionFollowVar); TEST_CASE(identicalInnerCondition); TEST_CASE(identicalConditionAfterEarlyExit); TEST_CASE(innerConditionModified); TEST_CASE(clarifyCondition1); // if (a = b() < 0) TEST_CASE(clarifyCondition2); // if (a & b == c) TEST_CASE(clarifyCondition3); // if (! a & b) TEST_CASE(clarifyCondition4); // ticket #3110 TEST_CASE(clarifyCondition5); // #3609 CWinTraits.. TEST_CASE(clarifyCondition6); // #3818 TEST_CASE(clarifyCondition7); TEST_CASE(clarifyCondition8); TEST_CASE(alwaysTrue); TEST_CASE(multiConditionAlwaysTrue); TEST_CASE(duplicateCondition); TEST_CASE(checkInvalidTestForOverflow); TEST_CASE(checkConditionIsAlwaysTrueOrFalseInsideIfWhile); TEST_CASE(alwaysTrueFalseInLogicalOperators); TEST_CASE(pointerAdditionResultNotNull); TEST_CASE(duplicateConditionalAssign); } void check(const char code[], const char* filename = "test.cpp", bool inconclusive = false) { // Clear the error buffer.. errout.str(""); settings0.inconclusive = inconclusive; // Raw tokens.. std::vector files(1, filename); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenizer.. Tokenizer tokenizer(&settings0, this); tokenizer.createTokens(&tokens2); tokenizer.simplifyTokens1(""); // Run checks.. CheckCondition checkCondition; checkCondition.runChecks(&tokenizer, &settings0, this); } void assignAndCompare() { // & check("void foo(int x)\n" "{\n" " int y = x & 4;\n" " if (y == 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void foo(int x)\n" "{\n" " int y = x & 4;\n" " if (y != 3);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y!=3' is always true.\n", errout.str()); // | check("void foo(int x) {\n" " int y = x | 0x14;\n" " if (y == 0x710);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==1808' is always false.\n", errout.str()); check("void foo(int x) {\n" " int y = x | 0x14;\n" " if (y == 0x71f);\n" "}"); ASSERT_EQUALS("", errout.str()); // various simple assignments check("void foo(int x) {\n" " int y = (x+1) | 1;\n" " if (y == 2);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false.\n", errout.str()); check("void foo() {\n" " int y = 1 | x();\n" " if (y == 2);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false.\n", errout.str()); // multiple conditions check("void foo(int x) {\n" " int y = x & 4;\n" " if ((y == 3) && (z == 1));\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void foo(int x) {\n" " int y = x & 4;\n" " if ((x==123) || ((y == 3) && (z == 1)));\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout.str()); check("void f(int x) {\n" " int y = x & 7;\n" " if (setvalue(&y) && y != 8);\n" "}"); ASSERT_EQUALS("", errout.str()); // recursive checking into scopes check("void f(int x) {\n" " int y = x & 7;\n" " if (z) y=0;\n" " else { if (y==8); }\n" // always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); // while check("void f(int x) {\n" " int y = x & 7;\n" " while (y==8);\n" // local variable => always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " extern int y; y = x & 7;\n" " while (y==8);\n" // non-local variable => no error "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) y--;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int x);\n" "void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) g(y);\n" " }\n" "}"); ASSERT_EQUALS( "[test.cpp:5] -> [test.cpp:6]: (style) Mismatching assignment and comparison, comparison 'y!=0' is always true.\n", errout.str()); check("void g(int &x);\n" "void f(int x) {\n" " int a = 100;\n" " while (x) {\n" " int y = 16 | a;\n" " while (y != 0) g(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // calling function check("void f(int x) {\n" " int y = x & 7;\n" " do_something();\n" " if (y==8);\n" // local variable => always false "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " int y = x & 7;\n" " do_something(&y);\n" // passing variable => no error " if (y==8);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void do_something(int);\n" "void f(int x) {\n" " int y = x & 7;\n" " do_something(y);\n" " if (y==8);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout.str()); check("void f(int x) {\n" " extern int y; y = x & 7;\n" " do_something();\n" " if (y==8);\n" // non-local variable => no error "}"); ASSERT_EQUALS("", errout.str()); // #4434 : false positive: ?: check("void f(int x) {\n" " x = x & 1;\n" " x = x & 1 ? 1 : -1;\n" " if(x != -1) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #4735 check("void f() {\n" " int x = *(char*)&0x12345678;\n" " if (x==18) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: no variable info check("void foo(int x) {\n" " y = 2 | x;\n" // y not declared => no error " if(y == 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: negative number check("void foo(int x) {\n" " int y = -2 | x;\n" // negative number => no error " if (y==1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // bailout: pass variable to function check("void foo(int x) {\n" " int y = 2 | x;\n" " bar(&y);\n" // pass variable to function => no error " if (y==1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // no crash on unary operator& (#5643) check("SdrObject* ApplyGraphicToObject() {\n" " if (&rHitObject) {}\n" " else if (rHitObject.IsClosedObj() && !&rHitObject) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #5695: increment check("void f(int a0, int n) {\n" " int c = a0 & 3;\n" " for (int a = 0; a < n; a++) {\n" " c++;\n" " if (c == 4)\n" " c = 0;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n" // #6662 " int x = a & 1;\n" " while (x <= 4) {\n" " if (x != 5) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'x!=5' is always true.\n", errout.str()); check("void f(int a) {\n" // #6662 " int x = a & 1;\n" " while ((x += 4) < 10) {\n" " if (x != 5) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int x);\n" "void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'x' is always true\n", errout.str()); check("void g(int & x);\n" "void f() {\n" " int x = 100;\n" " while (x) {\n" " g(x);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatchingBitAnd() { check("void f(int a) {\n" " int b = a & 0xf0;\n" " b &= 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0).\n", errout.str()); check("void f(int a) {\n" " int b = a & 0xf0;\n" " int c = b & 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0).\n", errout.str()); check("void f(int a) {\n" " int b = a;" " switch (x) {\n" " case 1: b &= 1; break;\n" " case 2: b &= 2; break;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void comparison() { // CheckCondition::comparison test cases // '==' check("void f(int a) {\n assert( (a & 0x07) == 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) == 0x8' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & b & 4 & c ) == 3 );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x4) == 0x3' is always false.\n", errout.str()); check("void f(int a) {\n assert( (a | 0x07) == 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) == 0x8' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) == 7U );\n}"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n assert( (a | 0x01) == -15 );\n}"); ASSERT_EQUALS("", errout.str()); // '!=' check("void f(int a) {\n assert( (a & 0x07) != 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) != 0x8' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a | 0x07) != 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) != 0x8' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) != 7U );\n}"); ASSERT_EQUALS("", errout.str()); check("void f(int a) {\n assert( (a | 0x07) != 7U );\n}"); ASSERT_EQUALS("", errout.str()); // '>=' check("void f(int a) {\n assert( (a & 0x07) >= 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) >= 0x8' is always false.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x7) >= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) >= 0x7' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) >= 7U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) >= 8U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '>' check("void f(int a) {\n assert( (a & 0x07) > 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) > 0x7' is always false.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x7) > 6U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) > 0x6' is always true.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) > 6U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) > 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '<=' check("void f(int a) {\n assert( (a & 0x07) <= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) <= 0x7' is always true.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x08) <= 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x8) <= 0x7' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) <= 6U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x08) <= 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' // '<' check("void f(int a) {\n assert( (a & 0x07) < 8U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) < 0x8' is always true.\n",errout.str()); check("void f(unsigned int a) {\n assert( (a | 0x07) < 7U );\n}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) < 0x7' is always false.\n",errout.str()); check("void f(int a) {\n assert( (a & 0x07) < 3U );\n}"); ASSERT_EQUALS("",errout.str()); check("void f(int a) {\n assert( (a | 0x07) < 7U );\n}"); ASSERT_EQUALS("",errout.str()); //correct for negative 'a' } void multicompare() { check("void foo(int x)\n" "{\n" " if (x & 7);\n" " else { if (x == 1); }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("void foo(int x)\n" "{\n" " if (x & 7);\n" " else { if (x & 1); }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout.str()); check("extern int bar() __attribute__((pure));\n" "void foo(int x)\n" "{\n" " if ( bar() >1 && b) {}\n" " else if (bar() >1 && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Expression is always false because 'else if' condition matches previous condition at line 4.\n", errout.str()); checkPureFunction("extern int bar();\n" "void foo(int x)\n" "{\n" " if ( bar() >1 && b) {}\n" " else if (bar() >1 && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Expression is always false because 'else if' condition matches previous condition at line 4.\n", errout.str()); // 7284 check("void foo() {\n" " if (a) {}\n" " else if (!!a) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); } void checkPureFunction(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings1, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); CheckCondition checkCondition; checkCondition.runChecks(&tokenizer, &settings1, this); } void overlappingElseIfCondition() { check("void f(int a, int &b) {\n" " if (a) { b = 1; }\n" " else { if (a) { b = 2; } }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition 'a' is always false\n", errout.str()); check("void f(int a, int &b) {\n" " if (a) { b = 1; }\n" " else { if (a) { b = 2; } }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition 'a' is always false\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1) { b = 1; }\n" " else { if (a == 2) { b = 2; }\n" " else { if (a == 1) { b = 3; } } }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'a==1' is always false\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1) { b = 1; }\n" " else { if (a == 2) { b = 2; }\n" " else { if (a == 2) { b = 3; } } }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'a==2' is always false\n", errout.str()); check("void f(int a, int &b) {\n" " if (a++) { b = 1; }\n" " else { if (a++) { b = 2; }\n" " else { if (a++) { b = 3; } } }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int &b) {\n" " if (!strtok(NULL, \" \")) { b = 1; }\n" " else { if (!strtok(NULL, \" \")) { b = 2; } }\n" "}"); ASSERT_EQUALS("", errout.str()); { check("void f(Class &c) {\n" " if (c.dostuff() == 3) {}\n" " else { if (c.dostuff() == 3) {} }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const Class &c) {\n" " if (c.dostuff() == 3) {}\n" " else { if (c.dostuff() == 3) {} }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); } check("void f(int a, int &b) {\n" " x = x / 2;\n" " if (x < 100) { b = 1; }\n" " else { x = x / 2; if (x < 100) { b = 2; } }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " if(i == 0x02e2000000 || i == 0xa0c6000000)\n" " foo(i);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket 3689 ( avoid false positive ) check("int fitInt(long long int nValue){\n" " if( nValue < 0x7fffffffLL )\n" " {\n" " return 32;\n" " }\n" " if( nValue < 0x7fffffffffffLL )\n" " {\n" " return 48;\n" " }\n" " else {\n" " if( nValue < 0x7fffffffffffffffLL )\n" " {\n" " return 64;\n" " } else\n" " {\n" " return -1;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(WIDGET *widget) {\n" " if (dynamic_cast(widget)){}\n" " else if (dynamic_cast(widget)){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" // #6482 " if (x & 1) {}\n" " else if (x == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 15) {}\n" " else if (x == 40) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(int x) {\n" " if (x == sizeof(double)) {}\n" " else { if (x == sizeof(long double)) {} }" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 0x08) {}\n" " else if (x & 0xF8) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x & 0xF8) {}\n" " else if (x & 0x08) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && !!a){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && a){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( b && !!a){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( b && !(!a)){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!b && !(!a)){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout.str()); check("void f(bool a, bool b) {\n" " if(a && b){}\n" " else if( !!(b) && !!(a+b)){}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #8168 check("enum MaskValues\n" "{\n" " Value1 = 0x00000001,\n" " Value2 = 0x00000002\n" "};\n" "void TestFunction(int value) {\n" " if ( value & (int)Value1 ) {}\n" " else if ( value & (int)Value2 ) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void oppositeElseIfCondition() { setMultiline(); check("void f(int x) {\n" " if (x) {}\n" " else if (!x) {}\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Condition '!x' is always true\n" "test.cpp:2:note:condition 'x'\n" "test.cpp:3:note:Condition '!x' is always true\n", errout.str()); check("void f(int x) {\n" " int y = x;\n" " if (x) {}\n" " else if (!y) {}\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Expression is always true because 'else if' condition is opposite to previous condition at line 3.\n" "test.cpp:2:note:'y' is assigned value 'x' here.\n" "test.cpp:3:note:first condition\n" "test.cpp:4:note:else if condition is opposite to first condition\n", errout.str()); } void checkBadBitmaskCheck() { check("bool f(int x) {\n" " bool b = x | 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " bool b = 0x02 | x;\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " int b = x | 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " bool b = x & 0x02;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " if(x | 0x02)\n" " return b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " int y = 0x1;\n" " if(b) y = 0;\n" " if(x | y)\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " foo(a && (x | 0x02));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return (x | 0x02) ? 0 : 5;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return x ? (x | 0x02) : 5;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("bool f(int x) {\n" " if (x) {\n" " return x | 0x02;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("const bool f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("struct F {\n" " static const bool f(int x) {\n" " return x | 0x02;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("struct F {\n" " typedef bool b_t;\n" "};\n" "F::b_t f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout.str()); check("int f(int x) {\n" " return x | 0x02;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void create_rop_masks_4( rop_mask_bits *bits) {\n" "DWORD mask_offset;\n" "BYTE *and_bits = bits->and;\n" "rop_mask *rop_mask;\n" "and_bits[mask_offset] |= (rop_mask->and & 0x0f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(unsigned a, unsigned b) {\n" " unsigned cmd1 = b & 0x0F;\n" " if (cmd1 | a) {\n" " if (b == 0x0C) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator1() { check("void f(int x) {\n" " if ((x != 1) || (x != 3))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout.str()); check("void f(int x) {\n" " if (1 != x || 3 != x)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout.str()); check("void f(int x) {\n" " if (x<0 && !x) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 0 && !x.\n", errout.str()); check("void f(int x) {\n" " if (x==0 && x) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 0 && x.\n", errout.str()); check("void f(int x) {\n" // ast.. " if (y == 1 && x == 1 && x == 7) { }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 7.\n", errout.str()); check("void f(int x, int y) {\n" " if (x != 1 || y != 1)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((y == 1) && (x != 1) || (x != 3))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 1) || (x != 3) && (y == 1))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x != 1) && (x != 3))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x == 1) || (x == 3))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 1) || (y != 3))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != hotdog) || (y != hotdog))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " if ((x != 5) || (y != 5))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x != 5) || (x != 6))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 5 || x != 6.\n", errout.str()); check("void f(unsigned int a, unsigned int b, unsigned int c) {\n" " if((a != b) || (c != b) || (c != a))\n" " {\n" " return true;\n" " }\n" " return false;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator2() { check("void f(float x) {\n" " if ((x == 1) && (x == 1.0))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x == 1) && (x == 0x00000001))\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x == 1 && x == 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 3.\n", errout.str()); check("void f(int x) {\n" " if (x == 1.0 && x == 3.0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); // float comparisons with == and != are not checked right now - such comparison is a bad idea check("void f(float x) {\n" " if (x == 1 && x == 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void bar(float f) {\n" // #5246 " if ((f > 0) && (f < 1)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 1.\n", errout.str()); check("void f(int x) {\n" " if (x < 1.0 && x > 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1.0 && x > 1.0.\n", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 1.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 1.0.\n", errout.str()); check("void f(int x) {\n" " if (x >= 1.0 && x <= 1.001)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x < 1 && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(float x) {\n" " if (x < 1.0 && x > 3.0)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1.0 && x > 3.0.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x < 3 && x > 1)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x > 3 || x < 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x < 10.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x <= 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x <= 10.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x < 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x < 10.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x <= 10)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x <= 10.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x < 3)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x <= 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x <= 3.\n", errout.str()); check("void f(int x) {\n" " if (x >= 3 || x < 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x < 3.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 || x <= 3)\n" " a++;\n" "}" ); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x <= 3.\n", errout.str()); check("void f(int x) {\n" " if((x==3) && (x!=4))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x!=4) && (x==3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x==3) || (x!=4))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x!=4) || (x==3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 3', the comparison 'x != 4' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x==3) && (x!=3))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 3 && x != 3.\n", errout.str()); check("void f(int x) {\n" " if ((x==6) || (x!=6))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x == 6 || x != 6.\n", errout.str()); check("void f(int x) {\n" " if (x > 10 || x < 3)\n" " a++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x > 5 && x == 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 5 && x == 1.\n", errout.str()); check("void f(int x) {\n" " if (x > 5 && x == 6)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 6', the comparison 'x > 5' is always true.\n", errout.str()); // #3419 check("void f() {\n" " if ( &q != &a && &q != &b ) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3676 check("void f(int m_x2, int w, int x) {\n" " if (x + w - 1 > m_x2 || m_x2 < 0 )\n" " m_x2 = x + w - 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x <= 1.0e20 && x >= -1.0e20) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x >= 1.0e20 && x <= 1.0e21) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(float x) {\n" // x+1 => x " if (x <= -1.0e20 && x >= -1.0e21) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator3() { check("void f(int x, bool& b) {\n" " b = x > 5 && x == 1;\n" " c = x < 1 && x == 3;\n" " d = x >= 5 && x == 1;\n" " e = x <= 1 && x == 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 5 && x == 1.\n" "[test.cpp:3]: (warning) Logical conjunction always evaluates to false: x < 1 && x == 3.\n" "[test.cpp:4]: (warning) Logical conjunction always evaluates to false: x >= 5 && x == 1.\n" "[test.cpp:5]: (warning) Logical conjunction always evaluates to false: x <= 1 && x == 3.\n", errout.str()); } void incorrectLogicOperator4() { check("#define ZERO 0\n" "void f(int x) {\n" " if (x && x != ZERO) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator5() { // complex expressions check("void f(int x) {\n" " if (x+3 > 2 || x+3 < 10) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: EXPR > 2 || EXPR < 10.\n", errout.str()); } void incorrectLogicOperator6() { // char literals check("void f(char x) {\n" " if (x == '1' || x == '2') {}\n" "}", "test.cpp", true); ASSERT_EQUALS("", errout.str()); check("void f(char x) {\n" " if (x == '1' && x == '2') {}\n" "}", "test.cpp", true); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == '1' && x == '2'.\n", errout.str()); check("int f(char c) {\n" " return (c >= 'a' && c <= 'z');\n" "}", "test.cpp", true); ASSERT_EQUALS("", errout.str()); check("int f(char c) {\n" " return (c <= 'a' && c >= 'z');\n" "}", "test.cpp", true); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Logical conjunction always evaluates to false: c <= 'a' && c >= 'z'.\n", errout.str()); check("int f(char c) {\n" " return (c <= 'a' && c >= 'z');\n" "}", "test.cpp", false); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator7() { // opposite expressions check("void f(int i) {\n" " if (i || !i) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: i || !(i).\n", errout.str()); check("void f(int a, int b) {\n" " if (a>b || a<=b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: a > b || a <= b.\n", errout.str()); check("void f(int a, int b) {\n" " if (a>b || a T icdf( const T uniform ) {\n" " if ((0 -1.0 - 1.0e-12))\n" " return;\n" " else\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator8() { // opposite expressions check("void f(int i) {\n" " if (!(i!=10) && !(i!=20)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: !(i != 10) && !(i != 20).\n", errout.str()); } void incorrectLogicOperator9() { // #6069 "False positive incorrectLogicOperator due to dynamic_cast" check("class MyType;\n" "class OtherType;\n" "void foo (OtherType* obj) { \n" " assert((!obj) || dynamic_cast(obj));\n" "}"); ASSERT_EQUALS("", errout.str()); } void incorrectLogicOperator10() { // #7794 - enum check("typedef enum { A, B } Type_t;\n" "void f(Type_t t) {\n" " if ((t == A) && (t == B))\n" " {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Logical conjunction always evaluates to false: t == 0 && t == 1.\n", errout.str()); } void incorrectLogicOperator11() { check("void foo(int i, const int n) { if ( i < n && i == n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i < n && i == n.\n", errout.str()); check("void foo(int i, const int n) { if ( i > n && i == n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i > n && i == n.\n", errout.str()); check("void foo(int i, const int n) { if ( i == n && i > n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i > n.\n", errout.str()); check("void foo(int i, const int n) { if ( i == n && i < n ) {} }"); ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i < n.\n", errout.str()); } void incorrectLogicOperator12() { // #8696 check("struct A {\n" " void f() const;\n" "};\n" "void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:6] -> [test.cpp:8]: (warning) Logical conjunction always evaluates to false: a > x && a < y.\n", errout.str()); check("struct A {\n" " void f();\n" "};\n" "void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(A a) {\n" " A x = a;\n" " A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(A a) {\n" " const A x = a;\n" " const A y = a;\n" " y.f();\n" " if (a > x && a < y)\n" " return;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:5]: (warning) Logical conjunction always evaluates to false: a > x && a < y.\n", errout.str()); } void incorrectLogicOperator13() { // 8780 check("void f(const int &v) {\n" " const int x=v;\n" " if ((v == 1) && (x == 2)) {;}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Logical conjunction always evaluates to false: v == 1 && x == 2.\n", errout.str()); check("void f2(const int *v) {\n" " const int *x=v;\n" " if ((*v == 1) && (*x == 2)) {;}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Logical conjunction always evaluates to false: *(v) == 1 && *(x) == 2.\n", errout.str()); } void incorrectLogicOperator14() { check("static const std ::string h;\n" "class i {\n" "public:\n" " struct j {\n" " std ::string k;\n" " std ::string l;\n" " };\n" " struct a {\n" " enum { m = 1 };\n" " };\n" "} b;\n" "namespace n {\n" "class c;\n" "}\n" "struct o {\n" " enum { p, d, q, r };\n" " enum { e, f };\n" "\n" "public:\n" " class j {\n" " public:\n" " class s {\n" " std ::string a;\n" " };\n" " };\n" "};\n" "namespace n {\n" "class b;\n" "}\n" "namespace aa {\n" "class d {\n" "public:\n" " char t;\n" " enum {} u;\n" "};\n" "} // namespace aa\n" "namespace aa {\n" "struct e {};\n" "} // namespace aa\n" "class a;\n" "class w {\n" "public:\n" " enum { x };\n" " struct {\n" " } y;\n" " std ::string z;\n" "};\n" "class ab {\n" " friend class c;\n" "\n" "public:\n" " class ac {\n" " void e(const ac &v) const;\n" " };\n" "};\n" "class f;\n" "class ad {\n" " friend class e;\n" " enum { e, ae, ag, ah, ai, aj, ak, a, b };\n" " class c {};\n" " class d {\n" " enum am { f, an, ao, ap, aq, ar, b, as, at, c, au };\n" " enum av { aw, ax, ay, az, e, ba, bb, bc, bd, a };\n" " struct b {\n" " am action;\n" " av c;\n" " };\n" " };\n" " class e {\n" " public:\n" " std ::string e;\n" " class f {\n" " } f;\n" " class be {\n" " public:\n" " };\n" " std ::vector bf;\n" " enum { bg, b } c;\n" " };\n" " struct bh {\n" " std ::map b;\n" " };\n" " std ::map bi;\n" " struct {\n" " int b;\n" " char bj;\n" " } bk;\n" " class a {\n" " public:\n" " std ::set b;\n" " };\n" "};\n" "class bl;\n" "class al;\n" "class bm;\n" "class f;\n" "class b;\n" "class bn;\n" "namespace bo {\n" "class bp {\n" "public:\n" " typedef std ::pair bq;\n" " typedef std ::list br;\n" "};\n" "const bo ::bp *dg(const f *a, const al *b);\n" "} // namespace bo\n" "const bn *dh(const f *d, bo ::bp ::br &bs);\n" "class f {\n" "public:\n" " struct bt {};\n" " std ::vector f;\n" "};\n" "class bu;\n" "class a;\n" "class c;\n" "struct bv {};\n" "class af {\n" "private:\n" "public:\n" " enum { b, d, e, f, c, bw };\n" " void a(int c);\n" " af *bx() const;\n" "};\n" "namespace by {\n" "class b;\n" "}\n" "class b {\n" "public:\n" " bool d, c;\n" "};\n" "class bz;\n" "class f;\n" "class ca {\n" " friend class b;\n" "\n" "public:\n" " const bm *cb() const { return cc; }\n" " f *d(f *e, bool f) const;\n" " int e() { return ++cd; }\n" " bl *const c;\n" " bm *cc;\n" " std ::map ce;\n" " int cd;\n" " bz *a;\n" "};\n" "namespace n {\n" "class c;\n" "class d;\n" "} // namespace n\n" "class cf {\n" "public:\n" " explicit cf(const std ::string &aname);\n" " cf(const std ::string &aname, const ca *cg, const al *ch, bl *ci)\n" " : cj(cg), ck(ch), cl(ci), cn(aname) {}\n" "\n" "protected:\n" " const ca *const cj;\n" " const al *const ck;\n" " bl *const cl;\n" " const std ::string cn;\n" "};\n" "class cm : public cf {\n" "public:\n" " void cp();\n" " std ::string d() const;\n" "};\n" "struct co {\n" " co();\n" " const bu *a;\n" " enum f {};\n" " enum {\n" " b = (1 << 0),\n" " c = (1 << 1),\n" " };\n" " void d(bool e);\n" "};\n" "class bu {\n" " friend class e;\n" "\n" "public:\n" " struct f {};\n" " enum { d, cr, cq, ct, cs, e, a, b, c, dd, cu, cv, cw, cx, cy, cz, da };\n" " const f *db;\n" " const af *dc;\n" "} f{};\n" "class bm {\n" "public:\n" " std ::list df;\n" " std ::vector de;\n" " mutable std ::set f;\n" "};\n" "void cm ::cp() {\n" " const bm *a = cj->cb();\n" " for (const bu *b : a->de)\n" " for (af *c = b->dc->bx();;) {\n" " af *d = c;\n" " af *e = c;\n" " bool f(d);\n" " bool g(e);\n" " if (f && g)\n" " ;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void secondAlwaysTrueFalseWhenFirstTrueError() { check("void f(int x) {\n" " if (x > 5 && x != 1)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n", errout.str()); check("void f(int x) {\n" " if (x > 5 && x != 6)\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if ((x > 5) && (x != 1))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n", errout.str()); check("void f(int x) {\n" " if ((x > 5) && (x != 6))\n" " a++;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 3 || x == 4;\n" " c = x < 5 || x == 4;\n" " d = x >= 3 || x == 4;\n" " e = x <= 5 || x == 4;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x == 4', the comparison 'x > 3' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x == 4', the comparison 'x < 5' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x == 4', the comparison 'x >= 3' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x == 4', the comparison 'x <= 5' is always true.\n", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 5 || x != 1;\n" " c = x < 1 || x != 3;\n" " d = x >= 5 || x != 1;\n" " e = x <= 1 || x != 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 5', the comparison 'x != 1' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x < 1', the comparison 'x != 3' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x >= 5', the comparison 'x != 1' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x <= 1', the comparison 'x != 3' is always true.\n", errout.str()); check("void f(int x, bool& b) {\n" " b = x > 6 && x > 5;\n" " c = x > 5 || x > 6;\n" " d = x < 6 && x < 5;\n" " e = x < 5 || x < 6;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: If 'x > 6', the comparison 'x > 5' is always true.\n" "[test.cpp:3]: (style) Redundant condition: If 'x > 6', the comparison 'x > 5' is always true.\n" "[test.cpp:4]: (style) Redundant condition: If 'x < 5', the comparison 'x < 6' is always true.\n" "[test.cpp:5]: (style) Redundant condition: If 'x < 5', the comparison 'x < 6' is always true.\n", errout.str()); } void incorrectLogicOp_condSwapping() { check("void f(int x) {\n" " if (x < 1 && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && x > 3)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x < 1 && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (1 > x && 3 < x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 && x < 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (3 < x && x < 1)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (x > 3 && 1 > x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); check("void f(int x) {\n" " if (3 < x && 1 > x)\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout.str()); } void modulo() { check("bool f(bool& b1, bool& b2, bool& b3) {\n" " b1 = a % 5 == 4;\n" " b2 = a % c == 100000;\n" " b3 = a % 5 == c;\n" " return a % 5 == 5-p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(bool& b1, bool& b2, bool& b3, bool& b4, bool& b5) {\n" " b1 = a % 5 < 5;\n" " b2 = a % 5 <= 5;\n" " b3 = a % 5 == 5;\n" " b4 = a % 5 != 5;\n" " b5 = a % 5 >= 5;\n" " return a % 5 > 5;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:3]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:4]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:5]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:6]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:7]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n", errout.str()); check("void f(bool& b1, bool& b2) {\n" " b1 = bar() % 5 < 889;\n" " if(x[593] % 5 <= 5)\n" " b2 = x.a % 5 == 5;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:3]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" "[test.cpp:4]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n", errout.str()); check("void f() {\n" " if (a % 2 + b % 2 == 2)\n" " foo();\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerCondition() { check("void foo(int a, int b) {\n" " if(a==b)\n" " if(a!=b)\n" " cout << a;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("bool foo(int a, int b) {\n" " if(a==b)\n" " return a!=b;\n" " return false;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'return' condition leads to a dead code block.\n", errout.str()); check("void foo(int a, int b) {\n" " if(a==b)\n" " if(b!=a)\n" " cout << a;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(int a) {\n" " if(a >= 50) {\n" " if(a < 50)\n" " cout << a;\n" " else\n" " cout << 100;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); // #4186 check("void foo(int a) {\n" " if(a >= 50) {\n" " if(a > 50)\n" " cout << a;\n" " else\n" " cout << 100;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // 4170 check("class foo {\n" " void bar() {\n" " if (tok == '(') {\n" " next();\n" " if (tok == ',') {\n" " next();\n" " if (tok != ',') {\n" " op->reg2 = asm_parse_reg();\n" " }\n" " skip(',');\n" " }\n" " }\n" " }\n" " void next();\n" " const char *tok;\n" "};"); ASSERT_EQUALS("", errout.str()); check("void foo(int i)\n" "{\n" " if(i > 5) {\n" " i = bar();\n" " if(i < 5) {\n" " cout << a;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& i) {\n" " i=6;\n" "}\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& i);\n" "void bar() {\n" " int i; i = func();\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int i);\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(const int &i);\n" "void bar(int i) {\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(int i);\n" "void bar() {\n" " int i; i = func();\n" " if(i>5) {\n" " foo(i);\n" " if(i<5) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("class C { void f(int &i) const; };\n" // #7028 - variable is changed by const method "void foo(C c, int i) {\n" " if (i==5) {\n" " c.f(i);\n" " if (i != 5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // see linux revision 1f80c0cc check("int generic_write_sync(int,int,int);\n" "\n" "void cifs_writev(int i) {\n" " int rc = __generic_file_aio_write();\n" " if (rc > 0){\n" " err = generic_write_sync(file, iocb->ki_pos - rc, rc);\n" " if(rc < 0) {\n" // <- condition is always false " err = rc;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:7]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); // #5874 - array check("void testOppositeConditions2() {\n" " int array[2] = { 0, 0 };\n" " if (array[0] < 2) {\n" " array[0] += 5;\n" " if (array[0] > 2) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6227 - FP caused by simplifications of casts and known variables check("void foo(A *a) {\n" " if(a) {\n" " B *b = dynamic_cast(a);\n" " if(!b) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a) {\n" " if(a) {\n" " int b = a;\n" " if(!b) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void foo(unsigned u) {\n" " if (u != 0) {\n" " for (int i=0; i<32; i++) {\n" " if (u == 0) {}\n" // <- don't warn " u = x;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #8186 check("void f() {\n" " for (int i=0;i<4;i++) {\n" " if (i==5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerConditionPointers() { check("void f(struct ABC *abc) {\n" " struct AB *ab = abc->ab;\n" " if (ab->a == 123){\n" " do_something(abc);\n" // might change ab->a " if (ab->a != 123) {\n" " err = rc;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void Fred::f() {\n" // daca: ace " if (this->next_ == map_man_->table_) {\n" " this->next_ = n;\n" " if (this->next_ != map_man_->table_) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(float *f) {\n" // #7405 " if(*f>10) {\n" " (*f) += 0.1f;\n" " if(*f<10) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int * f(int * x, int * y) {\n" " if(!x) return x;\n" " return y;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionClass() { // #6095 - calling member function that might change the state check("void f() {\n" " const Fred fred;\n" // <- fred is const, warn " if (fred.isValid()) {\n" " fred.dostuff();\n" " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("class Fred { public: bool isValid() const; void dostuff() const; };\n" "void f() {\n" " Fred fred;\n" " if (fred.isValid()) {\n" " fred.dostuff();\n" // <- dostuff() is const, warn " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f() {\n" " Fred fred;\n" " if (fred.isValid()) {\n" " fred.dostuff();\n" " if (!fred.isValid()) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6385 "crash in Variable::getFlag()" check("class TranslationHandler {\n" "QTranslator *mTranslator;\n" "void SetLanguage() {\n" " if (mTranslator) {\n" " qApp->removeTranslator(mTranslator);\n" " }\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // just don't crash... check("bool f(std::ofstream &CFileStream) {\n" // #8198 " if(!CFileStream.good()) { return; }\n" " CFileStream << \"abc\";\n" " if (!CFileStream.good()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionUndeclaredVariable() { // #5731 - fp when undeclared variable is used check("void f() {\n" " if (x == -1){\n" " x = do_something();\n" " if (x != -1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #5750 - another fp when undeclared variable is used check("void f() {\n" " if (r < w){\n" " r += 3;\n" " if (r > w) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6574 - another fp when undeclared variable is used check("void foo() {\n" " if(i) {\n" " i++;\n" " if(!i) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // undeclared array check("void f(int x) {\n" " if (a[x] > 0) {\n" " a[x] -= dt;\n" " if (a[x] < 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #6313 - false positive: opposite conditions in nested if blocks when condition changed check("void Foo::Bar() {\n" " if(var){\n" " --var;\n" " if(!var){}\n" " else {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // daca hyphy check("bool f() {\n" " if (rec.lLength==0) {\n" " rec.Delete(i);\n" " if (rec.lLength!=0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionAlias() { check("void f() {\n" " struct S s;\n" " bool hasFailed = false;\n" " s.status = &hasFailed;\n" "\n" " if (! hasFailed) {\n" " doStuff(&s);\n" " if (hasFailed) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void oppositeInnerCondition2() { // first comparison: < check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x==5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x!=5) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x!=5' is always true\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x>5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x>=5) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x<5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<4) {\n" " if (x<=5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x==4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x!=4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x>4) {}\n" // <- TODO " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x>=4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x<4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x<5) {\n" " if (x<=4) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // first comparison: > check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x==5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x>5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x>=5) {}\n" // <- TODO " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x<5) {}\n" // <- TODO " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>4) {\n" " if (x<=5) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x==4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x>4) {}\n" // <- TODO " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x>=4) {}\n" // <- TODO " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x<4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" "\n" " if (x>5) {\n" " if (x<=4) {}\n" // <- Warning " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f(int x) {\n" " if (x < 4) {\n" " if (10 < x) {}\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerCondition3() { check("void f3(char c) { if(c=='x') if(c=='y') {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f4(char *p) { if(*p=='x') if(*p=='y') {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f5(const char * const p) { if(*p=='x') if(*p=='y') {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f5(const char * const p) { if('x'==*p) if('y'==*p) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f6(char * const p) { if(*p=='x') if(*p=='y') {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f7(const char * p) { if(*p=='x') if(*p=='y') {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f8(int i) { if(i==4) if(i==2) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f9(int *p) { if (*p==4) if(*p==2) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f10(int * const p) { if (*p==4) if(*p==2) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f11(const int *p) { if (*p==4) if(*p==2) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f12(const int * const p) { if (*p==4) if(*p==2) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("struct foo {\n" " int a;\n" " int b;\n" "};\n" "void f(foo x) { if(x.a==4) if(x.b==2) {}} "); ASSERT_EQUALS("", errout.str()); check("struct foo {\n" " int a;\n" " int b;\n" "};\n" "void f(foo x) { if(x.a==4) if(x.b==4) {}} "); ASSERT_EQUALS("", errout.str()); check("void f3(char a, char b) { if(a==b) if(a==0) {}} "); ASSERT_EQUALS("", errout.str()); check("void f(int x) { if (x == 1) if (x != 1) {} }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerConditionAnd() { check("void f(int x) {\n" " if (a>3 && x > 100) {\n" " if (x < 10) {}\n" " }" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); } void oppositeInnerConditionEmpty() { check("void f1(const std::string &s) { if(s.size() > 42) if(s.empty()) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s) { if(s.size() > 0) if(s.empty()) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s) { if(s.size() < 0) if(s.empty()) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s) { if(s.empty()) if(s.size() > 42) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("template void f1(const T &s) { if(s.size() > 42) if(s.empty()) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f2(const std::wstring &s) { if(s.empty()) if(s.size() > 42) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(QString s) { if(s.isEmpty()) if(s.length() > 42) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout.str()); check("void f1(const std::string &s, bool b) { if(s.empty() || ((s.size() == 1) && b)) {}} "); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &x, const std::string &y) { if(x.size() > 42) if(y.empty()) {}} "); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &x, const std::string &y) { if(y.empty()) if(x.size() > 42) {}} "); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string v[10]) { if(v[0].size() > 42) if(v[1].empty()) {}} "); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() <= 1) if(s.empty()) {}} "); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() <= 2) if(s.empty()) {}} "); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() < 2) if(s.empty()) {}} "); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.size() >= 0) if(s.empty()) {}} "); ASSERT_EQUALS("", errout.str()); // TODO: These are identical condition since size cannot be negative check("void f1(const std::string &s) { if(s.size() <= 0) if(s.empty()) {}} "); ASSERT_EQUALS("", errout.str()); // TODO: These are identical condition since size cannot be negative check("void f1(const std::string &s) { if(s.size() < 1) if(s.empty()) {}} "); ASSERT_EQUALS("", errout.str()); } void oppositeInnerConditionFollowVar() { check("struct X {\n" " void f() {\n" " const int flag = get();\n" " if (flag) {\n" " bar();\n" " if (!get()) {}\n" " }\n" " }\n" " void bar();\n" " int get() const;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class C {\n" "public:\n" " bool f() const { return x > 0; }\n" " void g();\n" " int x = 0;\n" "};\n" "\n" "void C::g() {\n" " bool b = f();\n" " x += 1;\n" " if (!b && f()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void identicalInnerCondition() { check("void f1(int a, int b) { if(a==b) if(a==b) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); check("void f2(int a, int b) { if(a!=b) if(a!=b) {}}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); // #6645 false negative: condition is always false check("void f(bool a, bool b) {\n" " if(a && b) {\n" " if(a) {}\n" " else {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical inner 'if' condition is always true.\n", errout.str()); check("bool f(int a, int b) {\n" " if(a == b) { return a == b; }\n" " return false;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (warning) Identical inner 'return' condition is always true.\n", errout.str()); check("bool f(bool a) {\n" " if(a) { return a; }\n" " return false;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int* f(int* a, int * b) {\n" " if(a) { return a; }\n" " return b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int* f(std::shared_ptr a, std::shared_ptr b) {\n" " if(a.get()) { return a.get(); }\n" " return b.get();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int * x; };\n" "int* f(A a, int * b) {\n" " if(a.x) { return a.x; }\n" " return b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " uint32_t value;\n" " get_value(&value);\n" " int opt_function_capable = (value >> 28) & 1;\n" " if (opt_function_capable) {\n" " value = 0;\n" " get_value (&value);\n" " if ((value >> 28) & 1) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void identicalConditionAfterEarlyExit() { check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("bool f(int x) {\n" " if (x > 100) { return false; }\n" " return x > 100;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition and return expression 'x>100', return value is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (x > 100 || y > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (x > 100 && y > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " if (abc) {}\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 100) { return; }\n" " while (abc) { y = x; }\n" " if (x > 100) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition 'x>100', second condition is always false\n", errout.str()); check("void f(int x) {\n" // #8217 - crash for incomplete code " if (x > 100) { return; }\n" " X(do);\n" " if (x > 100) {}\n" "}"); // TODO: we should probably throw unknownMacro InternalError. Complain that the macro X must be defined. We can't check the code well without the definition. ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x>100' is always false\n", errout.str()); check("void f(const int *i) {\n" " if (!i) return;\n" " if (!num1tok) { *num1 = *num2; }\n" " if (!i) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition '!i', second condition is always false\n", errout.str()); check("void C::f(Tree &coreTree) {\n" // daca " if(!coreTree.build())\n" " return;\n" " coreTree.dostuff();\n" " if(!coreTree.build()) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct C { void f(const Tree &coreTree); };\n" "void C::f(const Tree &coreTree) {\n" " if(!coreTree.build())\n" " return;\n" " coreTree.dostuff();\n" " if(!coreTree.build()) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) Identical condition '!coreTree.build()', second condition is always false\n", errout.str()); check("void f(int x) {\n" // daca: labplot " switch(type) {\n" " case 1:\n" " if (x == 0) return 1;\n" " else return 2;\n" " case 2:\n" " if (x == 0) return 3;\n" " else return 4;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static int failed = 0;\n" "void f() {\n" " if (failed) return;\n" " checkBuffer();\n" " if (failed) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // daca icu check("void f(const uint32_t *section, int32_t start) {\n" " if(10<=section[start]) { return; }\n" " if(++start<100 && 10<=section[start]) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // daca iqtree check("void readNCBITree(std::istream &in) {\n" " char ch;\n" " in >> ch;\n" " if (ch != '|') return;\n" " in >> ch;\n" " if (ch != '|') {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #8924 check("struct A {\n" " void f() {\n" " if (this->FileIndex >= 0) return;\n" " this->FileIndex = 1 ;\n" " if (this->FileIndex < 0) return;\n" " }\n" " int FileIndex; \n" "};\n"); ASSERT_EQUALS("", errout.str()); } void innerConditionModified() { check("void f(int x, int y) {\n" " if (x == 0) {\n" " x += y;\n" " if (x == 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x == 0) {\n" " x += y;\n" " if (x == 1) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int * x, int * y) {\n" " if (x[*y] == 0) {\n" " (*y)++;\n" " if (x[*y] == 0) {}\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } // clarify conditions with = and comparison void clarifyCondition1() { check("void f() {\n" " if (x = b() < 0) {}\n" // don't simplify and verify this code "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (assignment + comparison); Clarify expression with parentheses.\n", errout.str()); check("void f(int i) {\n" " for (i = 0; i < 10; i++) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " x = a(); if (x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (x = b < 0 ? 1 : 2) {}\n" // don't simplify and verify this code "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y || (!y && z));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'y || (!y && z)' is equivalent to 'y || z'\n", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y || !y && z);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'y || (!y && z)' is equivalent to 'y || z'\n", errout.str()); check("void f() {\n" " if (!a || a && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. '!a || (a && b)' is equivalent to '!a || b'\n", errout.str()); check("void f(const Token *tok) {\n" " if (!tok->next()->function() || \n" " (tok->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok->next()->function(). '!A || (A && B)' is equivalent to '!A || B'\n", errout.str()); check("void f() {\n" " if (!tok->next()->function() || \n" " (!tok->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (!tok->next()->function() || \n" " (!tok2->next()->function() && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const Token *tok) {\n" " if (!tok->next(1)->function(1) || \n" " (tok->next(1)->function(1) && tok->next(1)->function(1)->isConstructor()));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok->next(1)->function(1). '!A || (A && B)' is equivalent to '!A || B'\n", errout.str()); check("void f() {\n" " if (!tok->next()->function(1) || \n" " (tok->next()->function(2) && tok->next()->function()->isConstructor()));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int y = rand(), z = rand();\n" " if (y==0 || y!=0 && z);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: y!=0. 'y==0 || (y!=0 && z)' is equivalent to 'y==0 || z'\n", errout.str()); check("void f() {\n" " if (x>0 || (x<0 && y)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Test Token::expressionString, TODO move this test check("void f() {\n" " if (!dead || (dead && (*it).ticks > 0)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: dead. '!dead || (dead && (*it).ticks>0)' is equivalent to '!dead || (*it).ticks>0'\n", errout.str()); check("void f() {\n" " if (!x || (x && (2>(y-1)))) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: x. '!x || (x && 2>(y-1))' is equivalent to '!x || 2>(y-1)'\n", errout.str()); } // clarify conditions with bitwise operator and comparison void clarifyCondition2() { check("void f() {\n" " if (x & 3 == 2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Condition 'x&3==2' is always false\n", errout.str()); check("void f() {\n" " if (a & fred1.x == fred2.y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" "[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" , errout.str()); } // clarify condition that uses ! operator and then bitwise operator void clarifyCondition3() { check("void f(int w) {\n" " if(!w & 0x8000) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f(int w) {\n" " if((!w) & 0x8000) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (x == foo() & 2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f() {\n" " if (2 & x == foo()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("void f() {\n" " if (2 & (x == foo())) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::list &ints) { }"); ASSERT_EQUALS("", errout.str()); check("void f() { A a; }"); ASSERT_EQUALS("", errout.str()); check("void f() { a(x there are never templates ASSERT_EQUALS("[test.c:1]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout.str()); check("class A;", "test.cpp"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (result != (char *)&inline_result) { }\n" // don't simplify and verify cast "}"); ASSERT_EQUALS("", errout.str()); // #8495 check("void f(bool a, bool b) {\n" " C & a & b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition4() { // ticket #3110 check("typedef double SomeType;\n" "typedef std::pair PairType;\n" "struct S\n" "{\n" " bool operator()\n" " ( PairType const & left\n" " , PairType const & right) const\n" " {\n" " return left.first < right.first;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition5() { // ticket #3609 (using | in template instantiation) check("template struct CWinTraits;\n" "CWinTraits::GetWndStyle(0);"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition6() { check("template\n" "SharedPtr& operator=( SharedPtr const & r ) {\n" " px = r.px;\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition7() { // Ensure that binary and unary &, and & in declarations are distinguished properly check("void f(bool error) {\n" " bool & withoutSideEffects=found.first->second;\n" // Declaring a reference to a boolean; & is no operator at all " execute(secondExpression, &programMemory, &result, &error);\n" // Unary & "}"); ASSERT_EQUALS("", errout.str()); } void clarifyCondition8() { // don't warn when boolean result comes from function call, array index, etc // the operator precedence is not unknown then check("bool a();\n" "bool f(bool b) {\n" " return (a() & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("bool f(bool *a, bool b) {\n" " return (a[10] & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { bool a; };\n" "bool f(struct A a, bool b) {\n" " return (a.a & b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A { bool a; };\n" "bool f(struct A a, bool b) {\n" " return (A::a & b);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testBug5895() { check("void png_parse(uint64_t init, int buf_size) {\n" " if (init == 0x89504e470d0a1a0a || init == 0x8a4d4e470d0a1a0a)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testBug5309() { check("extern uint64_t value;\n" "void foo() {\n" " if( ( value >= 0x7ff0000000000001ULL )\n" " && ( value <= 0x7fffffffffffffffULL ) );\n" "}"); ASSERT_EQUALS("", errout.str()); } void alwaysTrue() { check("void f() {\n" // #4842 " int x = 0;\n" " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels " if (!x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!x' is always true\n", errout.str()); check("bool f(int x) {\n" " if(x == 0) { x++; return x == 0; } \n" " return false;\n" "}"); TODO_ASSERT_EQUALS("return value is always true?", "", errout.str()); check("void f() {\n" // #6898 (Token::expressionString) " int x = 0;\n" " A(x++ == 1);\n" " A(x++ == 2);\n" "}"); TODO_ASSERT_EQUALS("function argument is always true? however is code really weird/suspicious?", "", errout.str()); check("bool foo(int bar) {\n" " bool ret = false;\n" " if (bar == 1)\n" " return ret;\n" // <- #9326 - FP condition is always false " if (bar == 2)\n" " ret = true;\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f1(const std::string &s) { if(s.empty()) if(s.size() == 0) {}} "); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition 's.size()==0' is always true\n", errout.str()); check("void f() {\n" " int buf[42];\n" " if( buf != 0) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf!=0' is always true\n", errout.str()); // #8924 check("void f() {\n" " int buf[42];\n" " if( !buf ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!buf' is always false\n", errout.str()); check("void f() {\n" " int buf[42];\n" " bool b = buf;\n" " if( b ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'b' is always true\n", errout.str()); check("void f() {\n" " int buf[42];\n" " bool b = buf;\n" " if( !b ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always false\n", errout.str()); check("void f() {\n" " int buf[42];\n" " int * p = nullptr;\n" " if( buf == p ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'buf==p' is always false\n", errout.str()); check("void f(bool x) {\n" " int buf[42];\n" " if( buf || x ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf' is always true\n", errout.str()); check("void f(int * p) {\n" " int buf[42];\n" " if( buf == p ) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int buf[42];\n" " int p[42];\n" " if( buf == p ) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int buf[42];\n" " if( buf == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Avoid FP when condition comes from macro check("#define NOT !\n" "void f() {\n" " int x = 0;\n" " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels " if (NOT x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("#define M x != 0\n" "void f() {\n" " int x = 0;\n" " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels " if (M) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("#define IF(X) if (X && x())\n" "void f() {\n" " IF(1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Avoid FP for sizeof condition check("void f() {\n" " if (sizeof(char) != 123) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 123;\n" " if (sizeof(char) != x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'sizeof(char)!=x' is always true\n", errout.str()); // Don't warn in assertions. Condition is often 'always true' by intention. // If platform,defines,etc cause an 'always false' assertion then that is not very dangerous neither check("void f() {\n" " int x = 0;\n" " assert(x == 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // #9363 - do not warn about value passed to function check("void f(bool b) {\n" " if (b) {\n" " if (bar(!b)) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #7783 FP knownConditionTrueFalse on assert(0 && "message") check("void foo(int x) {\n" " if (x<0)\n" " {\n" " assert(0 && \"bla\");\n" " ASSERT(0 && \"bla\");\n" " assert_foo(0 && \"bla\");\n" " ASSERT_FOO(0 && \"bla\");\n" " assert((int)(0==0));\n" " assert((int)(0==0) && \"bla\");\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #7750 char literals in boolean expressions check("void f() {\n" " if('a'){}\n" " if(L'b'){}\n" " if(1 && 'c'){}\n" " int x = 'd' ? 1 : 2;\n" "}"); ASSERT_EQUALS("", errout.str()); // Skip literals check("void f() { if(true) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(false) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(!true) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(!false) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(0) {} }"); ASSERT_EQUALS("", errout.str()); check("void f() { if(1) {} }"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " bool b = false;\n" " if (i == 0) b = true;\n" " else if (!b && i == 1) {}\n" " if (b)\n" " {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always true\n", errout.str()); check("bool f() { return nullptr; }"); ASSERT_EQUALS("", errout.str()); check("enum E { A };\n" "bool f() { return A; }\n"); ASSERT_EQUALS("", errout.str()); check("bool f() { \n" " const int x = 0;\n" " return x; \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(void){return 1/abs(10);}"); ASSERT_EQUALS("", errout.str()); check("bool f() { \n" " int x = 0;\n" " return x; \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool f() {\n" " const int a = 50;\n" " const int b = 52;\n" " return a+b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int a = 50;\n" " int b = 52;\n" " a++;\n" " b++;\n" " return a+b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool& g();\n" "bool f() {\n" " bool & b = g();\n" " b = false;\n" " return b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " bool b;\n" " bool f() {\n" " b = false;\n" " return b;\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("bool f(long maxtime) {\n" " if (std::time(0) > maxtime)\n" " return std::time(0) > maxtime;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(double param) {\n" " while(bar()) {\n" " if (param<0.)\n" " return;\n" " }\n" " if (param<0.)\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(int i) {\n" " if (i==42)\n" " {\n" " bar();\n" " }\n" " if (cond && (42==i))\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // 8842 crash check("class a {\n" " int b;\n" " c(b);\n" " void f() {\n" " if (b) return;\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("void f(const char* x, const char* t) {\n" " if (!(strcmp(x, y) == 0)) { return; }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(const int a[]){ if (a == 0){} }"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " bool operator<(const S&);\n" "};\n" "int main() {\n" " S s;\n" " bool c = s [test.cpp:3]: (style) Condition 'handle' is always true\n", errout.str()); check("int f(void *handle) {\n" " if (handle == 0) return 0;\n" " if (handle) return 1;\n" " else return 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition 'handle' is always true\n", errout.str()); check("int f(void *handle) {\n" " if (handle != 0) return 0;\n" " if (handle) return 1;\n" " else return 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition 'handle' is always false\n", errout.str()); check("void f(void* x, void* y) {\n" " if (x == nullptr && y == nullptr)\n" " return;\n" " if (x == nullptr || y == nullptr)\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void* g();\n" "void f(void* a, void* b) {\n" " while (a) {\n" " a = g();\n" " if (a == b)\n" " break;\n" " }\n" " if (a) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void* g();\n" "void f(void* a, void* b) {\n" " while (a) {\n" " a = g();\n" " }\n" " if (a) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'a' is always false\n", errout.str()); check("void f(int * x, bool b) {\n" " if (!x && b) {}\n" " else if (x) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const std::string x=\"xyz\";\n" " if(!x.empty()){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!x.empty()' is always true\n", errout.str()); check("std::string g();\n" "void f() {\n" " const std::string msg = g();\n" " if(!msg.empty()){}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int *array, int size ) {\n" " for(int i = 0; i < size; ++i) {\n" " if(array == 0)\n" " continue;\n" " if(array){}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Condition 'array' is always true\n", errout.str()); check("void f(int *array, int size ) {\n" " for(int i = 0; i < size; ++i) {\n" " if(array == 0)\n" " continue;\n" " else if(array){}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Condition 'array' is always true\n", errout.str()); // #9277 check("int f() {\n" " constexpr bool x = true;\n" " if constexpr (x)\n" " return 0;\n" " else\n" " return 1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9319 check("struct S {\n" " int a;\n" " int b;\n" "};\n" "void g(S s, bool& x);\n" "void f() {\n" " bool x = false;\n" " g({0, 1}, x);\n" " if (x) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9318 check("class A {};\n" "class B : public A {};\n" "void f(A* x) {\n" " if (!x)\n" " return;\n" " auto b = dynamic_cast(x);\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // handleKnownValuesInLoop check("bool g();\n" "void f(bool x) {\n" " if (x) while(x) x = g();\n" "}\n"); ASSERT_EQUALS("", errout.str()); // isLikelyStream check("void f(std::istringstream& iss) {\n" " std::string x;\n" " while (iss) {\n" " iss >> x;\n" " if (!iss) break;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if (x > 5) {\n" " x++;\n" " if (x == 1) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==1' is always false\n", errout.str()); check("void f(int x) {\n" " if (x > 5) {\n" " x++;\n" " if (x != 1) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x!=1' is always true\n", errout.str()); // #9332 check("struct A { void* g(); };\n" "void f() {\n" " A a;\n" " void* b = a.g();\n" " if (!b) return;\n" " void* c = a.g();\n" " if (!c) return;\n" " bool compare = c == b;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9361 check("void f(char c) {\n" " if (c == '.') {}\n" " else if (isdigit(c) != 0) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9351 check("int f(int x) {\n" " const bool b = x < 42;\n" " if(b) return b?0:-1;\n" " return 42;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (style) Condition 'b' is always true\n", errout.str()); // #9362 check("uint8_t g();\n" "void f() {\n" " const uint8_t v = g();\n" " if((v != 0x00)) {\n" " if( (v & 0x01) == 0x00) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9367 check("void f(long x) {\n" " if (x <= 0L)\n" " return;\n" " if (x % 360L == 0)\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void alwaysTrueContainer() { // #9329 check("void c1(std::vector&);\n" "void c2(std::vector&);\n" "void foo(int flag) {\n" " std::vector g;\n" " if (flag)\n" " c1(g );\n" " else\n" " c2(g );\n" " if ( !g.empty() )\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo(int flag) {\n" " std::vector g;\n" " if (flag)\n" " c1(g );\n" " else\n" " c2(g );\n" " if ( !g.empty() )\n" " return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void multiConditionAlwaysTrue() { check("void f() {\n" " int val = 0;\n" " if (val < 0) continue;\n" " if (val > 0) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if (val > 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if (val < 0) {}\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int activate = 0;\n" " int foo = 0;\n" " if (activate) {}\n" " else if (foo) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'activate' is always false\n" "[test.cpp:5]: (style) Condition 'foo' is always false\n", errout.str()); // #6904 check("void f() {\n" " const int b[2] = { 1,0 };\n" " if(b[1] == 2) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'b[1]==2' is always false\n", errout.str()); } void duplicateCondition() { check("void f(bool x) {\n" " if(x) {}\n" " if(x) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x == 1) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x == 2) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if(x == 1) {}\n" " if(x != 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool x) {\n" " if(x) {}\n" " g();\n" " if(x) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " if(x == 1) { x++; }\n" " if(x == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #8996 check("void g(int** v);\n" "void f() {\n" " int a = 0;\n" " int b = 0;\n" " int* d[] = {&a, &b};\n" " g(d);\n" " if (a) {}\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9311 check("struct c {\n" " int* p;\n" "};\n" "void g(struct c* v);\n" "void f() {\n" " int a = 0;\n" " int b = 0;\n" " struct c d[] = {{&a}, {&b}};\n" " g(d);\n" " if (a) {}\n" " if (b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #8993 check("void f(const std::string& x) {\n" " auto y = x;\n" " if (x.empty()) y = \"1\";\n" " if (y.empty()) return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9106 check("struct A {int b;};\n" "void f(A a, int c) {\n" " if (a.b) a.b = c;\n" " if (a.b) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void checkInvalidTestForOverflow() { check("void f(char *p, unsigned int x) {\n" " assert((p + x) < p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow '(p+x)= p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow '(p+x)>=p'. Condition is always true unless there is overflow, and overflow is undefined behaviour.\n", errout.str()); check("void f(char *p, unsigned int x) {\n" " assert(p > (p + x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'p>(p+x)'. Condition is always false unless there is overflow, and overflow is undefined behaviour.\n", errout.str()); check("void f(char *p, unsigned int x) {\n" " assert(p <= (p + x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'p<=(p+x)'. Condition is always true unless there is overflow, and overflow is undefined behaviour.\n", errout.str()); check("void f(signed int x) {\n" " assert(x + 100 < x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'x+100 don't warn " assert(x + 100U < x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkConditionIsAlwaysTrueOrFalseInsideIfWhile() { check("void f() {\n" " enum states {A,B,C};\n" " const unsigned g_flags = B|C;\n" " if(g_flags & A) {}\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'g_flags&A' is always false\n", errout.str()); check("void f() {\n" " int a = 5;" " if(a) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'a' is always true\n", errout.str()); check("void f() {\n" " int a = 5;" " while(a + 1) { a--; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 5;" " while(a + 1) { return; }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'a+1' is always true\n", errout.str()); } void alwaysTrueFalseInLogicalOperators() { check("bool f();\n" "void foo() { bool x = true; if(x||f()) {}}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout.str()); check("void foo(bool b) { bool x = true; if(x||b) {}}\n"); ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always true\n", errout.str()); check("void foo(bool b) { if(true||b) {}}\n"); ASSERT_EQUALS("", errout.str()); check("bool f();\n" "void foo() { bool x = false; if(x||f()) {}}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always false\n", errout.str()); check("bool f();\n" "void foo() { bool x = false; if(x&&f()) {}}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always false\n", errout.str()); check("void foo(bool b) { bool x = false; if(x&&b) {}}\n"); ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always false\n", errout.str()); check("void foo(bool b) { if(false&&b) {}}\n"); ASSERT_EQUALS("", errout.str()); check("bool f();\n" "void foo() { bool x = true; if(x&&f()) {}}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout.str()); } void pointerAdditionResultNotNull() { check("void f(char *ptr) {\n" " if (ptr + 1 != 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison is wrong. Result of 'ptr+1' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour.\n", errout.str()); } void duplicateConditionalAssign() { setMultiline(); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y;\n" "}\n"); ASSERT_EQUALS("test.cpp:3:style:Assignment 'x=y' is redundant with condition 'x==y'.\n" "test.cpp:2:note:Condition 'x==y'\n" "test.cpp:3:note:Assignment 'x=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x != y)\n" " x = y;\n" "}\n"); ASSERT_EQUALS("test.cpp:2:style:The statement 'if (x!=y) x=y' is logically equivalent to 'x=y'.\n" "test.cpp:3:note:Assignment 'x=y'\n" "test.cpp:2:note:Condition 'x!=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y;\n" " else\n" " x = 1;\n" "}\n"); ASSERT_EQUALS("test.cpp:3:style:Assignment 'x=y' is redundant with condition 'x==y'.\n" "test.cpp:2:note:Condition 'x==y'\n" "test.cpp:3:note:Assignment 'x=y' is redundant\n", errout.str()); check("void f(int& x, int y) {\n" " if (x != y)\n" " x = y;\n" " else\n" " x = 1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int& x, int y) {\n" " if (x == y)\n" " x = y + 1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g();\n" "void f(int& x, int y) {\n" " if (x == y) {\n" " x = y;\n" " g();\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestCondition) cppcheck-1.90/test/testconstructors.cpp000066400000000000000000003716601357737443600204160ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkclass.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestConstructors : public TestFixture { public: TestConstructors() : TestFixture("TestConstructors") { } private: Settings settings; void check(const char code[], bool showAll = false) { // Clear the error buffer.. errout.str(""); settings.inconclusive = showAll; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check class constructors.. CheckClass checkClass(&tokenizer, &settings, this); checkClass.constructors(); } void run() OVERRIDE { settings.addEnabled("style"); settings.addEnabled("warning"); TEST_CASE(simple1); TEST_CASE(simple2); TEST_CASE(simple3); TEST_CASE(simple4); TEST_CASE(simple5); // ticket #2560 TEST_CASE(simple6); // ticket #4085 - uninstantiated template class TEST_CASE(simple7); // ticket #4531 TEST_CASE(simple8); TEST_CASE(simple9); // ticket #4574 TEST_CASE(simple10); // ticket #4388 TEST_CASE(simple11); // ticket #4536, #6214 TEST_CASE(simple12); // ticket #4620 TEST_CASE(simple13); // #5498 - no constructor, c++11 assignments TEST_CASE(simple14); // #6253 template base TEST_CASE(noConstructor1); TEST_CASE(noConstructor2); TEST_CASE(noConstructor3); TEST_CASE(noConstructor4); TEST_CASE(noConstructor5); TEST_CASE(noConstructor6); // ticket #4386 TEST_CASE(noConstructor7); // ticket #4391 TEST_CASE(noConstructor8); // ticket #4404 TEST_CASE(noConstructor9); // ticket #4419 TEST_CASE(noConstructor11); // ticket #3552 TEST_CASE(noConstructor12); // #8951 - member initialization TEST_CASE(forwardDeclaration); // ticket #4290/#3190 TEST_CASE(initvar_with_this); // BUG 2190300 TEST_CASE(initvar_if); // BUG 2190290 TEST_CASE(initvar_operator_eq1); // BUG 2190376 TEST_CASE(initvar_operator_eq2); // BUG 2190376 TEST_CASE(initvar_operator_eq3); TEST_CASE(initvar_operator_eq4); // ticket #2204 TEST_CASE(initvar_operator_eq5); // ticket #4119 TEST_CASE(initvar_operator_eq6); TEST_CASE(initvar_same_classname); // BUG 2208157 TEST_CASE(initvar_chained_assign); // BUG 2270433 TEST_CASE(initvar_2constructors); // BUG 2270353 TEST_CASE(initvar_constvar); TEST_CASE(initvar_staticvar); TEST_CASE(initvar_union); TEST_CASE(initvar_delegate); // ticket #4302 TEST_CASE(initvar_delegate2); TEST_CASE(initvar_private_constructor); // BUG 2354171 - private constructor TEST_CASE(initvar_copy_constructor); // ticket #1611 TEST_CASE(initvar_nested_constructor); // ticket #1375 TEST_CASE(initvar_nocopy1); // ticket #2474 TEST_CASE(initvar_nocopy2); // ticket #2484 TEST_CASE(initvar_nocopy3); // ticket #3611 TEST_CASE(initvar_with_member_function_this); // ticket #4824 TEST_CASE(initvar_destructor); // No variables need to be initialized in a destructor TEST_CASE(initvar_func_ret_func_ptr); // ticket #4449 TEST_CASE(initvar_alias); // #6921 TEST_CASE(initvar_templateMember); // #7205 TEST_CASE(operatorEqSTL); TEST_CASE(uninitVar1); TEST_CASE(uninitVar2); TEST_CASE(uninitVar3); TEST_CASE(uninitVar4); TEST_CASE(uninitVar5); TEST_CASE(uninitVar6); TEST_CASE(uninitVar7); TEST_CASE(uninitVar8); TEST_CASE(uninitVar9); // ticket #1730 TEST_CASE(uninitVar10); // ticket #1993 TEST_CASE(uninitVar11); TEST_CASE(uninitVar12); // ticket #2078 TEST_CASE(uninitVar13); // ticket #1195 TEST_CASE(uninitVar14); // ticket #2149 TEST_CASE(uninitVar15); TEST_CASE(uninitVar16); TEST_CASE(uninitVar17); TEST_CASE(uninitVar18); // ticket #2465 TEST_CASE(uninitVar19); // ticket #2792 TEST_CASE(uninitVar20); // ticket #2867 TEST_CASE(uninitVar21); // ticket #2947 TEST_CASE(uninitVar22); // ticket #3043 TEST_CASE(uninitVar23); // ticket #3702 TEST_CASE(uninitVar24); // ticket #3190 TEST_CASE(uninitVar25); // ticket #4789 TEST_CASE(uninitVar26); TEST_CASE(uninitVar27); // ticket #5170 - rtl::math::setNan(&d) TEST_CASE(uninitVar28); // ticket #6258 TEST_CASE(uninitVar29); TEST_CASE(uninitVar30); // ticket #6417 TEST_CASE(uninitVar31); // ticket #8271 TEST_CASE(uninitVar32); // ticket #8835 TEST_CASE(uninitVarEnum1); TEST_CASE(uninitVarEnum2); // ticket #8146 TEST_CASE(uninitVarStream); TEST_CASE(uninitVarTypedef); TEST_CASE(uninitVarMemset); TEST_CASE(uninitVarArray1); TEST_CASE(uninitVarArray2); TEST_CASE(uninitVarArray3); TEST_CASE(uninitVarArray4); TEST_CASE(uninitVarArray5); TEST_CASE(uninitVarArray6); TEST_CASE(uninitVarArray7); TEST_CASE(uninitVarArray8); TEST_CASE(uninitVarArray9); // ticket #6957, #6959 TEST_CASE(uninitVarArray2D); TEST_CASE(uninitVarArray3D); TEST_CASE(uninitVarCpp11Init1); TEST_CASE(uninitVarCpp11Init2); TEST_CASE(uninitVarStruct1); // ticket #2172 TEST_CASE(uninitVarStruct2); // ticket #838 TEST_CASE(uninitVarUnion1); // ticket #3196 TEST_CASE(uninitVarUnion2); TEST_CASE(uninitMissingFuncDef); // can't expand function in constructor TEST_CASE(privateCtor1); // If constructor is private.. TEST_CASE(privateCtor2); // If constructor is private.. TEST_CASE(function); // Function is not variable TEST_CASE(uninitVarPublished); // Borland C++: Variables in the published section are auto-initialized TEST_CASE(uninitOperator); // No FP about uninitialized 'operator[]' TEST_CASE(uninitFunction1); // No FP when initialized in function TEST_CASE(uninitFunction2); // No FP when initialized in function TEST_CASE(uninitFunction3); // No FP when initialized in function TEST_CASE(uninitFunction4); TEST_CASE(uninitFunction5); TEST_CASE(uninitSameClassName); // No FP when two classes have the same name TEST_CASE(uninitFunctionOverload); // No FP when there are overloaded functions TEST_CASE(uninitVarOperatorEqual); // ticket #2415 TEST_CASE(uninitVarPointer); // ticket #3801 TEST_CASE(uninitConstVar); TEST_CASE(constructors_crash1); // ticket #5641 TEST_CASE(classWithOperatorInName);// ticket #2827 TEST_CASE(templateConstructor); // ticket #7942 TEST_CASE(typedefArray); // ticket #5766 TEST_CASE(uninitAssignmentWithOperator); // ticket #7429 TEST_CASE(uninitCompoundAssignment); // ticket #7429 TEST_CASE(uninitComparisonAssignment); // ticket #7429 TEST_CASE(uninitTemplate1); // ticket #7372 } void simple1() { check("class Fred\n" "{\n" "public:\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) The class 'Fred' does not have a constructor although it has private member variables.\n", errout.str()); check("struct Fred\n" "{\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) The struct 'Fred' does not have a constructor although it has private member variables.\n", errout.str()); } void simple2() { check("class Fred\n" "{\n" "public:\n" " Fred() : i(0) { }\n" " Fred(Fred const & other) : i(other.i) {}\n" " Fred(Fred && other) : i(other.i) {}\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { i = 0; }\n" " Fred(Fred const & other) {i=other.i}\n" " Fred(Fred && other) {i=other.i}\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { }\n" " Fred(Fred const & other) {}\n" " Fred(Fred && other) {}\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n" "[test.cpp:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); check("struct Fred\n" "{\n" " Fred() { }\n" " Fred(Fred const & other) {}\n" " Fred(Fred && other) {}\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n" "[test.cpp:4]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void simple3() { check("struct Fred\n" "{\n" " Fred();\n" " int i;\n" "};\n" "Fred::Fred() :i(0)\n" "{ }"); ASSERT_EQUALS("", errout.str()); check("struct Fred\n" "{\n" " Fred();\n" " int i;\n" "};\n" "Fred::Fred()\n" "{ i = 0; }"); ASSERT_EQUALS("", errout.str()); check("struct Fred\n" "{\n" " Fred();\n" " int i;\n" "};\n" "Fred::Fred()\n" "{ }"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void simple4() { check("struct Fred\n" "{\n" " Fred();\n" " explicit Fred(int _i);\n" " Fred(Fred const & other);\n" " int i;\n" "};\n" "Fred::Fred()\n" "{ }\n" "Fred::Fred(int _i)\n" "{\n" " i = _i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:8]: (warning, inconclusive) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void simple5() { // ticket #2560 check("namespace Nsp\n" "{\n" " class B { };\n" "}\n" "class Altren : public Nsp::B\n" "{\n" "public:\n" " Altren () : Nsp::B(), mValue(0)\n" " {\n" " }\n" "private:\n" " int mValue;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple6() { // ticket #4085 - uninstantiated template class check("template struct A {\n" " A() { x = 0; }\n" " A(const T & t) { x = t.x; }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); check("template struct A {\n" " A() : x(0) { }\n" " A(const T & t) : x(t.x) { }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); check("template struct A {\n" " A() : x(0) { }\n" " A(const T & t) : x(t.x) { }\n" "private:\n" " int x;\n" " int y;\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (warning) Member variable 'A::y' is not initialized in the constructor.\n" "[test.cpp:3]: (warning) Member variable 'A::y' is not initialized in the constructor.\n", errout.str()); } void simple7() { // ticket #4531 check("class Fred;\n" "struct Fred {\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple8() { check("struct Fred { int x; };\n" "class Barney { Fred fred; };\n" "class Wilma { struct Betty { int x; } betty; };"); ASSERT_EQUALS("[test.cpp:2]: (style) The class 'Barney' does not have a constructor although it has private member variables.\n" "[test.cpp:3]: (style) The class 'Wilma' does not have a constructor although it has private member variables.\n", errout.str()); } void simple9() { // ticket #4574 check("class Unknown::Fred {\n" "public:\n" " Fred() : x(0) { }\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple10() { // ticket #4388 check("class Fred {\n" "public:\n" " Fred() = default;\n" "private:\n" " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void simple11() { // ticket #4536, #6214 check("class Fred {\n" "public:\n" " Fred() {}\n" "private:\n" " int x = 0;\n" " int y = f();\n" " int z{0};\n" " int (*pf[2])(){nullptr, nullptr};\n" " int a[2][3] = {{1,2,3},{4,5,6}};\n" " int b{1}, c{2};\n" " int d, e{3};\n" " int f{4}, g;\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n" "[test.cpp:3]: (warning) Member variable 'Fred::g' is not initialized in the constructor.\n", errout.str()); } void simple12() { // ticket #4620 check("class Fred {\n" " int x;\n" "public:\n" " Fred() { Init(); }\n" " void Init(int i = 0);\n" "};\n" "void Fred::Init(int i) { x = i; }"); ASSERT_EQUALS("", errout.str()); check("class Fred {\n" " int x;\n" " int y;\n" "public:\n" " Fred() { Init(0); }\n" " void Init(int i, int j = 0);\n" "};\n" "void Fred::Init(int i, int j) { x = i; y = j; }"); ASSERT_EQUALS("", errout.str()); } void simple13() { // #5498 check("class Fred {\n" " int x=1;\n" " int *y=0;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void simple14() { // #6253 template base check("class Fred : public Base {" "public:" " Fred()\n" " :Base(1),\n" " x(1)\n" " {}\n" "private:\n" " int x;\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("class Fred : public Base {" "public:" " Fred()\n" " :Base{1},\n" " x{1}\n" " {}\n" "private:\n" " int x;\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void noConstructor1() { // There are nonstatic member variables - constructor is needed check("class Fred\n" "{\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:1]: (style) The class 'Fred' does not have a constructor although it has private member variables.\n", errout.str()); } void noConstructor2() { check("class Fred\n" "{\n" "public:\n" " static void foobar();\n" "};\n" "\n" "void Fred::foobar()\n" "{ }"); ASSERT_EQUALS("", errout.str()); } void noConstructor3() { check("class Fred\n" "{\n" "private:\n" " static int foobar;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor4() { check("class Fred\n" "{\n" "public:\n" " int foobar;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor5() { check("namespace Foo\n" "{\n" " int i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void noConstructor6() { // ticket #4386 check("class Ccpucycles {\n" " friend class foo::bar;\n" " Ccpucycles() :\n" " m_v(0), m_b(true)\n" " {}\n" "private:\n" " cpucyclesT m_v;\n" " bool m_b;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor7() { // ticket #4391 check("short bar;\n" "class foo;\n"); ASSERT_EQUALS("", errout.str()); } void noConstructor8() { // ticket #4404 check("class LineSegment;\n" "class PointArray { };\n" "void* tech_ = NULL;\n"); ASSERT_EQUALS("", errout.str()); } void noConstructor9() { // ticket #4419 check("class CGreeting : public CGreetingBase {\n" "public:\n" " CGreeting() : CGreetingBase(), MessageSet(false) {}\n" "private:\n" " bool MessageSet;\n" "};"); ASSERT_EQUALS("", errout.str()); } void noConstructor11() { // #3552 check("class Fred { int x; };\n" "union U { int y; Fred fred; };"); ASSERT_EQUALS("", errout.str()); } void noConstructor12() { // #8951 check("class Fred { int x{0}; };"); ASSERT_EQUALS("", errout.str()); check("class Fred { int x=0; };"); ASSERT_EQUALS("", errout.str()); check("class Fred { int x[1]={0}; };"); // #8850 ASSERT_EQUALS("", errout.str()); check("class Fred { int x[1]{0}; };"); ASSERT_EQUALS("", errout.str()); } // ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor." // ticket #3190 "SymbolDatabase: Parse of sub class constructor fails" void forwardDeclaration() { check("class foo;\n" "int bar;\n"); ASSERT_EQUALS("", errout.str()); check("class foo;\n" "class foo;\n"); ASSERT_EQUALS("", errout.str()); check("class foo{};\n" "class foo;\n"); ASSERT_EQUALS("", errout.str()); } void initvar_with_this() { check("struct Fred\n" "{\n" " Fred()\n" " { this->i = 0; }\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_if() { check("struct Fred\n" "{\n" " Fred()\n" " {\n" " if (true)\n" " i = 0;\n" " else\n" " i = 1;\n" " }\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq1() { // Bug 2190376 and #3820 - False positive, Uninitialized member variable with operator= check("struct Fred\n" "{\n" " int i;\n" "\n" " Fred()\n" " { i = 0; }\n" "\n" " Fred(const Fred &fred)\n" " { *this = fred; }\n" "\n" " const Fred & operator=(const Fred &fred)\n" " { i = fred.i; return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct Fred {\n" " int i;\n" "\n" " Fred(const Fred &fred)\n" " { (*this) = fred; }\n" "\n" " const Fred & operator=(const Fred &fred)\n" " { i = fred.i; return *this; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A\n" "{\n" " A() : i(0), j(0) {}\n" "\n" " A &operator=(const int &value)\n" " {\n" " i = value;\n" " return (*this);\n" " }\n" "\n" " int i;\n" " int j;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq2() { check("struct Fred\n" "{\n" " Fred() { i = 0; }\n" " void operator=(const Fred &fred) { }\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout.str()); } void initvar_operator_eq3() { check("struct Fred\n" "{\n" " Fred() { Init(); }\n" " void operator=(const Fred &fred) { Init(); }\n" "private:\n" " void Init() { i = 0; }\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq4() { check("class Fred\n" "{\n" " int i;\n" "public:\n" " Fred() : i(5) { }\n" " Fred & operator=(const Fred &fred)\n" " {\n" " if (&fred != this)\n" " {\n" " }\n" " return *this\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("class Fred\n" "{\n" " int * i;\n" "public:\n" " Fred() : i(NULL) { }\n" " Fred & operator=(const Fred &fred)\n" " {\n" " if (&fred != this)\n" " {\n" " }\n" " return *this\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("class Fred\n" "{\n" " const int * i;\n" "public:\n" " Fred() : i(NULL) { }\n" " Fred & operator=(const Fred &fred)\n" " {\n" " if (&fred != this)\n" " {\n" " }\n" " return *this\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("class Fred\n" "{\n" " const int i;\n" "public:\n" " Fred() : i(5) { }\n" " Fred & operator=(const Fred &fred)\n" " {\n" " if (&fred != this)\n" " {\n" " }\n" " return *this\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq5() { // #4119 - false positive when using swap to assign variables check("class Fred {\n" " int i;\n" "public:\n" " Fred() : i(5) { }\n" " ~Fred() { }\n" " Fred(const Fred &fred) : i(fred.i) { }\n" " Fred & operator=(const Fred &rhs) {\n" " Fred(rhs).swap(*this);\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_operator_eq6() { // std::vector check("struct Fred {\n" " uint8_t data;\n" " Fred & operator=(const Fred &rhs) {\n" " return *this;\n" " }\n" "};",true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::data' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("struct Fred {\n" " std::vector ints;\n" " Fred & operator=(const Fred &rhs) {\n" " return *this;\n" " }\n" "};",true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::ints' is not assigned a value in 'Fred::operator='.\n", errout.str()); check("struct Fred {\n" " Data data;\n" " Fred & operator=(const Fred &rhs) {\n" " return *this;\n" " }\n" "};",true); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::data' is not assigned a value in 'Fred::operator='.\n", errout.str()); } void initvar_same_classname() { // Bug 2208157 - False positive: Uninitialized variable, same class name check("void func1()\n" "{\n" " class Fred\n" " {\n" " int a;\n" " Fred() { a = 0; }\n" " };\n" "}\n" "\n" "void func2()\n" "{\n" " class Fred\n" " {\n" " int b;\n" " Fred() { b = 0; }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("void func1()\n" "{\n" " struct Fred\n" " {\n" " int a;\n" " Fred() { a = 0; }\n" " };\n" "}\n" "\n" "void func2()\n" "{\n" " class Fred\n" " {\n" " int b;\n" " Fred() { b = 0; }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("void func1()\n" "{\n" " struct Fred\n" " {\n" " int a;\n" " Fred() { a = 0; }\n" " };\n" "}\n" "\n" "void func2()\n" "{\n" " struct Fred\n" " {\n" " int b;\n" " Fred() { b = 0; }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Foo {\n" " void func1()\n" " {\n" " struct Fred\n" " {\n" " int a;\n" " Fred() { a = 0; }\n" " };\n" " }\n" "\n" " void func2()\n" " {\n" " struct Fred\n" " {\n" " int b;\n" " Fred() { b = 0; }\n" " };\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Foo {\n" " void func1()\n" " {\n" " struct Fred\n" " {\n" " int a;\n" " Fred() { }\n" " };\n" " }\n" "\n" " void func2()\n" " {\n" " struct Fred\n" " {\n" " int b;\n" " Fred() { }\n" " };\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::a' is not initialized in the constructor.\n" "[test.cpp:16]: (warning) Member variable 'Fred::b' is not initialized in the constructor.\n", errout.str()); } void initvar_chained_assign() { // Bug 2270433 - Uninitialized variable false positive on chained assigns check("struct c\n" "{\n" " c();\n" "\n" " int m_iMyInt1;\n" " int m_iMyInt2;\n" "}\n" "\n" "c::c()\n" "{\n" " m_iMyInt1 = m_iMyInt2 = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void initvar_2constructors() { check("struct c\n" "{\n" " c();\n" " explicit c(bool b);" "\n" " void InitInt();\n" "\n" " int m_iMyInt;\n" " int m_bMyBool;\n" "}\n" "\n" "c::c()\n" "{\n" " m_bMyBool = false;\n" " InitInt();" "}\n" "\n" "c::c(bool b)\n" "{\n" " m_bMyBool = b;\n" " InitInt();\n" "}\n" "\n" "void c::InitInt()\n" "{\n" " m_iMyInt = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void initvar_constvar() { check("struct Fred\n" "{\n" " const char *s;\n" " Fred();\n" "};\n" "Fred::Fred() : s(NULL)\n" "{ }"); ASSERT_EQUALS("", errout.str()); check("struct Fred\n" "{\n" " const char *s;\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{ s = NULL; }"); ASSERT_EQUALS("", errout.str()); check("struct Fred\n" "{\n" " const char *s;\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{ }"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::s' is not initialized in the constructor.\n", errout.str()); } void initvar_staticvar() { check("class Fred\n" "{\n" "public:\n" " Fred() { }\n" " static void *p;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_union() { check("class Fred\n" "{\n" " union\n" " {\n" " int a;\n" " char b[4];\n" " } U;\n" "public:\n" " Fred()\n" " {\n" " U.a = 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" " union\n" " {\n" " int a;\n" " char b[4];\n" " } U;\n" "public:\n" " Fred()\n" " {\n" " }\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Fred::U' is not initialized in the constructor.\n", "", errout.str()); } void initvar_delegate() { check("class A {\n" " int number;\n" "public:\n" " A(int n) { }\n" " A() : A(42) {}\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) { number = n; }\n" " A() : A(42) {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) : A() { }\n" " A() {}\n" "};", true); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" "[test.cpp:5]: (warning, inconclusive) Member variable 'A::number' is not initialized in the constructor.\n", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) : A() { }\n" " A() { number = 42; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) { }\n" " A() : A{42} {}\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) { number = n; }\n" " A() : A{42} {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) : A{} { }\n" " A() {}\n" "};", true); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" "[test.cpp:5]: (warning, inconclusive) Member variable 'A::number' is not initialized in the constructor.\n", errout.str()); check("class A {\n" " int number;\n" "public:\n" " A(int n) : A{} { }\n" " A() { number = 42; }\n" "};"); ASSERT_EQUALS("", errout.str()); // Ticket #6675 check("struct Foo {\n" " Foo();\n" " Foo(int foo);\n" " int foo_;\n" "};\n" "Foo::Foo() : Foo(0) {}\n" "Foo::Foo(int foo) : foo_(foo) {}\n"); ASSERT_EQUALS("", errout.str()); // Noexcept ctors check("class A {\n" "private:\n" " int _a;\n" "public:\n" " A(const int a) noexcept : _a{a} {}\n" " A() noexcept;\n" "};\n" "\n" "A::A() noexcept: A(0) {}"); ASSERT_EQUALS("", errout.str()); // Ticket #8581 check("class A {\n" "private:\n" " int _a;\n" "public:\n" " A(int a) : _a(a) {}\n" " A(float a) : A(int(a)) {}\n" "};"); ASSERT_EQUALS("", errout.str()); // Ticket #8258 check("struct F{};\n" "struct Foo {\n" " Foo(int a, F&& f, int b = 21) : _a(a), _b(b), _f(f) {}\n" " Foo(int x, const char* value) : Foo(x, F(), 42) {}\n" " Foo(int x, int* value) : Foo(x, F()) {}\n" " int _a;\n" " int _b;\n" " F _f;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_delegate2() { check("class Foo {\n" "public:\n" " explicit Foo(const Bar bar);\n" " Foo(const std::string& id);\n" " virtual ~RtpSession() { }\n" "protected:\n" " bool a;\n" " uint16_t b;\n" "};\n" "\n" "Foo::Foo(const Bar var)\n" " : Foo(bar->getId())\n" "{\n" "}\n" "\n" "Foo::Foo(const std::string& id)\n" " : a(true)\n" " , b(0)\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); } void initvar_private_constructor() { settings.standards.cpp = Standards::CPP11; check("class Fred\n" "{\n" "private:\n" " int var;\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{ }"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::var' is not initialized in the constructor.\n", errout.str()); settings.standards.cpp = Standards::CPP03; check("class Fred\n" "{\n" "private:\n" " int var;\n" " Fred();\n" "};\n" "Fred::Fred()\n" "{ }"); ASSERT_EQUALS("", errout.str()); } void initvar_copy_constructor() { // ticket #1611 check("class Fred\n" "{\n" "private:\n" " std::string var;\n" "public:\n" " Fred() { };\n" " Fred(const Fred &) { };\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "private:\n" " std::string var;\n" "public:\n" " Fred() { };\n" " Fred(const Fred &) { };\n" "};", true); ASSERT_EQUALS("[test.cpp:7]: (warning, inconclusive) Member variable 'Fred::var' is not initialized in the constructor.\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " std::string var;\n" "public:\n" " Fred();\n" " Fred(const Fred &);\n" "};\n" "Fred::Fred() { };\n" "Fred::Fred(const Fred &) { };\n", true); ASSERT_EQUALS("[test.cpp:10]: (warning, inconclusive) Member variable 'Fred::var' is not initialized in the constructor.\n", errout.str()); } void initvar_nested_constructor() { // ticket #1375 check("class A {\n" "public:\n" " A();\n" " struct B {\n" " explicit B(int x);\n" " struct C {\n" " explicit C(int y);\n" " struct D {\n" " int d;\n" " explicit D(int z);\n" " };\n" " int c;\n" " };\n" " int b;\n" " };\n" "private:\n" " int a;\n" " B b;\n" "};\n" "A::A(){}\n" "A::B::B(int x){}\n" "A::B::C::C(int y){}\n" "A::B::C::D::D(int z){}"); // Note that the example code is not compilable. The A constructor must // explicitly initialize A::b. A warning for A::b is not necessary. ASSERT_EQUALS("[test.cpp:20]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" "[test.cpp:22]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" "[test.cpp:23]: (warning) Member variable 'D::d' is not initialized in the constructor.\n", errout.str()); check("class A {\n" "public:\n" " A();\n" " struct B {\n" " explicit B(int x);\n" " struct C {\n" " explicit C(int y);\n" " struct D {\n" " D(const D &);\n" " int d;\n" " };\n" " int c;\n" " };\n" " int b;\n" " };\n" "private:\n" " int a;\n" " B b;\n" "};\n" "A::A(){}\n" "A::B::B(int x){}\n" "A::B::C::C(int y){}\n" "A::B::C::D::D(const A::B::C::D & d){}"); // Note that the example code is not compilable. The A constructor must // explicitly initialize A::b. A warning for A::b is not necessary. ASSERT_EQUALS("[test.cpp:20]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" "[test.cpp:22]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" "[test.cpp:23]: (warning) Member variable 'D::d' is not initialized in the constructor.\n", errout.str()); check("class A {\n" "public:\n" " A();\n" " struct B {\n" " explicit B(int x);\n" " struct C {\n" " explicit C(int y);\n" " struct D {\n" " struct E { int e; };\n" " struct E d;\n" " explicit D(const E &);\n" " };\n" " int c;\n" " };\n" " int b;\n" " };\n" "private:\n" " int a;\n" " B b;\n" "};\n" "A::A(){}\n" "A::B::B(int x){}\n" "A::B::C::C(int y){}\n" "A::B::C::D::D(const A::B::C::D::E & e){}"); // Note that the example code is not compilable. The A constructor must // explicitly initialize A::b. A warning for A::b is not necessary. ASSERT_EQUALS("[test.cpp:21]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:22]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" "[test.cpp:23]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" "[test.cpp:24]: (warning) Member variable 'D::d' is not initialized in the constructor.\n", errout.str()); } void initvar_nocopy1() { // ticket #2474 check("class B\n" "{\n" " B (const B & Var);\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" " B (B && Var);\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" " B & operator= (const B & Var);\n" "public:\n" " B ();\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" "public:\n" " B (const B & Var);\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A : public std::vector\n" "{\n" "public:\n" " A(const A &a);\n" "};\n" "class B\n" "{\n" " A a;\n" "public:\n" " B(){}\n" " B(const B&){}\n" " B(B &&){}\n" " const B& operator=(const B&){return *this;}\n" "};", true); ASSERT_EQUALS("[test.cpp:11]: (warning, inconclusive) Member variable 'B::a' is not initialized in the constructor.\n" "[test.cpp:12]: (warning, inconclusive) Member variable 'B::a' is not initialized in the constructor.\n" "[test.cpp:13]: (warning, inconclusive) Member variable 'B::a' is not assigned a value in 'B::operator='.\n", errout.str()); check("class B\n" "{\n" "public:\n" " B (B && Var);\n" " int data;\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'A::m_SemVar' is not initialized in the constructor.\n", errout.str()); check("class B\n" "{\n" "public:\n" " B ();\n" " B & operator= (const B & Var);\n" " int data;\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " A(A &&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " const A& operator=(const A&){return *this;}\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_nocopy2() { // ticket #2484 check("class B\n" "{\n" " B (B & Var);\n" " B & operator= (const B & Var);\n" " int data;\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " const A& operator=(const A&){return *this;}\n" "};", true); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" "public:\n" " B (B & Var);\n" " B & operator= (const B & Var);\n" " int data;\n" "};\n" "class A\n" "{\n" " B m_SemVar;\n" "public:\n" " A(){}\n" " A(const A&){}\n" " const A& operator=(const A&){return *this;}\n" "};", true); ASSERT_EQUALS("[test.cpp:12]: (warning) Member variable 'A::m_SemVar' is not initialized in the constructor.\n" "[test.cpp:13]: (warning) Member variable 'A::m_SemVar' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'A::m_SemVar' is not assigned a value in 'A::operator='.\n", errout.str()); } void initvar_nocopy3() { // #3611 - unknown type is non-copyable check("struct A {\n" " B b;\n" " A() {}\n" " A(const A& rhs) {}\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " B b;\n" " A() {}\n" " A(const A& rhs) {}\n" "};", true); ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Member variable 'A::b' is not initialized in the constructor.\n", errout.str()); } void initvar_with_member_function_this() { check("struct Foo {\n" " Foo(int m) { this->setMember(m); }\n" " void setMember(int m) { member = m; }\n" " int member;\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_destructor() { check("class Fred\n" "{\n" "private:\n" " int var;\n" "public:\n" " Fred() : var(0) {}\n" " ~Fred() {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_func_ret_func_ptr() { // ticket #4449 (segmentation fault) check("class something {\n" " int * ( something :: * process()) () { return 0; }\n" " something() { process(); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void initvar_alias() { // #6921 check("struct S {\n" " int a;\n" " S() {\n" " int& pa = a;\n" " pa = 4;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int a;\n" " S() {\n" " int* pa = &a;\n" " *pa = 4;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int a[2];\n" " S() {\n" " int* pa = a;\n" " for (int i = 0; i < 2; i++)\n" " *pa++ = i;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* a[2];\n" " S() {\n" " int* pa = a[1];\n" " *pa = 0;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'S::a' is not initialized in the constructor.\n", errout.str()); check("struct S {\n" " int a;\n" " S() {\n" " int pa = a;\n" " pa = 4;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'S::a' is not initialized in the constructor.\n", errout.str()); } void initvar_templateMember() { check("template\n" "struct Wrapper {\n" " static void foo(int * x) {\n" " for (int i(0); i <= n_; ++i)\n" " x[i] = 5;\n" " }\n" "};\n" "class A {\n" "public:\n" " static constexpr int dim = 5;\n" " int x[dim + 1];\n" " A() {\n" " Wrapper::foo(x);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void operatorEqSTL() { check("class Fred\n" "{\n" "private:\n" " std::vector ints;\n" "public:\n" " Fred();\n" " void operator=(const Fred &f);\n" "};\n" "\n" "Fred::Fred()\n" "{ }\n" "\n" "void Fred::operator=(const Fred &f)\n" "{ }", true); ASSERT_EQUALS("[test.cpp:13]: (warning, inconclusive) Member variable 'Fred::ints' is not assigned a value in 'Fred::operator='.\n", errout.str()); } void uninitVar1() { check("enum ECODES\n" "{\n" " CODE_1 = 0,\n" " CODE_2 = 1\n" "};\n" "\n" "class Fred\n" "{\n" "public:\n" " Fred() {}\n" "\n" "private:\n" " ECODES _code;\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Fred::_code' is not initialized in the constructor.\n", errout.str()); check("class A{};\n" "\n" "class B : public A\n" "{\n" "public:\n" " B() {}\n" "private:\n" " float f;\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'B::f' is not initialized in the constructor.\n", errout.str()); check("class C\n" "{\n" " FILE *fp;\n" "\n" "public:\n" " explicit C(FILE *fp);\n" "};\n" "\n" "C::C(FILE *fp) {\n" " C::fp = fp;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar2() { check("class John\n" "{\n" "public:\n" " John() { (*this).i = 0; }\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar3() { // No FP when struct has constructor check("class Foo\n" "{\n" "public:\n" " Foo() { }\n" "private:\n" " struct Bar {\n" " Bar();\n" " };\n" " Bar bars[2];\n" "};"); ASSERT_EQUALS("", errout.str()); // Using struct that doesn't have constructor check("class Foo\n" "{\n" "public:\n" " Foo() { }\n" "private:\n" " struct Bar {\n" " int x;\n" " };\n" " Bar bars[2];\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::bars' is not initialized in the constructor.\n", errout.str()); } void uninitVar4() { check("class Foo\n" "{\n" "public:\n" " Foo() { bar.x = 0; }\n" "private:\n" " struct Bar {\n" " int x;\n" " };\n" " struct Bar bar;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar5() { check("class Foo\n" "{\n" "public:\n" " Foo() { }\n" " Foo &operator=(const Foo &)\n" " { return *this; }\n" " static int i;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar6() { check("class Foo : public Bar\n" "{\n" "public:\n" " explicit Foo(int i) : Bar(mi=i) { }\n" "private:\n" " int mi;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Foo : public Bar\n" "{\n" "public:\n" " explicit Foo(int i) : Bar{mi=i} { }\n" "private:\n" " int mi;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar7() { check("class Foo {\n" " int a;\n" "public:\n" " Foo() : a(0) {}\n" " Foo& operator=(const Foo&);\n" " void Swap(Foo& rhs);\n" "};\n" "\n" "void Foo::Swap(Foo& rhs) {\n" " std::swap(a,rhs.a);\n" "}\n" "\n" "Foo& Foo::operator=(const Foo& rhs) {\n" " Foo copy(rhs);\n" " copy.Swap(*this);\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar8() { check("class Foo {\n" " int a;\n" "public:\n" " Foo() : a(0) {}\n" " Foo& operator=(const Foo&);\n" "};\n" "\n" "Foo& Foo::operator=(const Foo& rhs) {\n" " if (&rhs != this)\n" " {\n" " }\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'Foo::a' is not assigned a value in 'Foo::operator='.\n", errout.str()); } void uninitVar9() { // ticket #1730 check("class Prefs {\n" "private:\n" " int xasd;\n" "public:\n" " explicit Prefs(wxSize size);\n" "};\n" "Prefs::Prefs(wxSize size)\n" "{\n" " SetMinSize( wxSize( 48,48 ) );\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Prefs::xasd' is not initialized in the constructor.\n", errout.str()); } void uninitVar10() { // ticket #1993 check("class A {\n" "public:\n" " A();\n" "private:\n" " int var1;\n" " int var2;\n" "};\n" "A::A() : var1(0) { }"); ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'A::var2' is not initialized in the constructor.\n", errout.str()); } void uninitVar11() { check("class A {\n" "public:\n" " explicit A(int a = 0);\n" "private:\n" " int var;\n" "};\n" "A::A(int a) { }"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::var' is not initialized in the constructor.\n", errout.str()); } void uninitVar12() { // ticket #2078 check("class Point\n" "{\n" "public:\n" " Point()\n" " {\n" " Point(0, 0);\n" " }\n" " Point(int x, int y)\n" " : x(x), y(y)\n" " {}\n" " int x, y;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Point::x' is not initialized in the constructor.\n" "[test.cpp:4]: (warning) Member variable 'Point::y' is not initialized in the constructor.\n", errout.str()); } void uninitVar13() { // ticket #1195 check("class A {\n" "private:\n" " std::vector *ints;\n" "public:\n" " A()\n" " {}\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'A::ints' is not initialized in the constructor.\n", errout.str()); } void uninitVar14() { // ticket #2149 // no namespace check("class Foo\n" "{\n" "public:\n" " Foo();\n" "private:\n" " bool mMember;\n" "};\n" "Foo::Foo()\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // single namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" " Foo::Foo()\n" " {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor outside namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "Foo::Foo()\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); // constructor outside namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "Output::Foo::Foo()\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor in separate namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "namespace Output\n" "{\n" " Foo::Foo()\n" " {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor in different separate namespace check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "namespace Input\n" "{\n" " Foo::Foo()\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // constructor in different separate namespace (won't compile) check("namespace Output\n" "{\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" "}\n" "namespace Input\n" "{\n" " Output::Foo::Foo()\n" " {\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // constructor in nested separate namespace check("namespace A\n" "{\n" " namespace Output\n" " {\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" " }\n" " namespace Output\n" " {\n" " Foo::Foo()\n" " {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:15]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout.str()); // constructor in nested different separate namespace check("namespace A\n" "{\n" " namespace Output\n" " {\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" " }\n" " namespace Input\n" " {\n" " Foo::Foo()\n" " {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // constructor in nested different separate namespace check("namespace A\n" "{\n" " namespace Output\n" " {\n" " class Foo\n" " {\n" " public:\n" " Foo();\n" " private:\n" " bool mMember;\n" " };\n" " }\n" " namespace Input\n" " {\n" " Output::Foo::Foo()\n" " {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar15() { check("class Fred\n" "{\n" " int a;\n" "public:\n" " Fred();\n" " ~Fred();\n" "};\n" "Fred::~Fred()\n" "{\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar16() { check("struct Foo\n" "{\n" " int a;\n" " void set(int x) { a = x; }\n" "};\n" "class Bar\n" "{\n" " Foo foo;\n" "public:\n" " Bar()\n" " {\n" " foo.set(0);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct Foo\n" "{\n" " int a;\n" " void set(int x) { a = x; }\n" "};\n" "class Bar\n" "{\n" " Foo foo;\n" "public:\n" " Bar()\n" " {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Bar::foo' is not initialized in the constructor.\n", errout.str()); } void uninitVar17() { check("struct Foo\n" "{\n" " int a;\n" "};\n" "class Bar\n" "{\n" " Foo foo[10];\n" "public:\n" " Bar()\n" " {\n" " foo[0].a = 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct Foo\n" "{\n" " int a;\n" "};\n" "class Bar\n" "{\n" " Foo foo[10];\n" "public:\n" " Bar()\n" " {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Bar::foo' is not initialized in the constructor.\n", errout.str()); } void uninitVar18() { // ticket #2465 check("struct Altren\n" "{\n" " explicit Altren(int _a = 0) : value(0) { }\n" " int value;\n" "};\n" "class A\n" "{\n" "public:\n" " A() { }\n" "private:\n" " Altren value;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar19() { // ticket #2792 check("class mystring\n" "{\n" " char* m_str;\n" " int m_len;\n" "public:\n" " explicit mystring(const char* str)\n" " {\n" " m_len = strlen(str);\n" " m_str = (char*) malloc(m_len+1);\n" " memcpy(m_str, str, m_len+1);\n" " }\n" " mystring& operator=(const mystring& copy)\n" " {\n" " return (*this = copy.m_str);\n" " }\n" " ~mystring()\n" " {\n" " free(m_str);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar20() { // ticket #2867 check("Object::MemFunc() {\n" " class LocalClass {\n" " public:\n" " LocalClass() : dataLength_(0) {}\n" " std::streamsize dataLength_;\n" " double bitsInData_;\n" " } obj;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", errout.str()); check("struct copy_protected;\n" "Object::MemFunc() {\n" " class LocalClass : public copy_protected {\n" " public:\n" " LocalClass() : copy_protected(1), dataLength_(0) {}\n" " std::streamsize dataLength_;\n" " double bitsInData_;\n" " } obj;\n" "};"); ASSERT_EQUALS( "[test.cpp:5]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", errout.str()); check("struct copy_protected;\n" "Object::MemFunc() {\n" " class LocalClass : ::copy_protected {\n" " public:\n" " LocalClass() : copy_protected(1), dataLength_(0) {}\n" " std::streamsize dataLength_;\n" " double bitsInData_;\n" " } obj;\n" "};"); ASSERT_EQUALS( "[test.cpp:5]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", errout.str()); } void uninitVar21() { // ticket #2947 check("class Fred {\n" "private:\n" " int a[23];\n" "public:\n" " Fred();\n" "};\n" "Fred::Fred() {\n" " a[x::y] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVar22() { // ticket #3043 check("class Fred {\n" " public:\n" " Fred & operator=(const Fred &);\n" " virtual Fred & clone(const Fred & other);\n" " int x;\n" "};\n" "Fred & Fred::operator=(const Fred & other) {\n" " return clone(other);\n" "}\n" "Fred & Fred::clone(const Fred & other) {\n" " x = 0;\n" " return *this;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred {\n" " public:\n" " Fred & operator=(const Fred &);\n" " virtual Fred & clone(const Fred & other);\n" " int x;\n" "};\n" "Fred & Fred::operator=(const Fred & other) {\n" " return clone(other);\n" "}\n" "Fred & Fred::clone(const Fred & other) {\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::x' is not assigned a value in 'Fred::operator='.\n", errout.str()); } void uninitVar23() { // ticket #3702 check("class Fred {\n" " int x;\n" "public:\n" " Fred(struct A a, struct B b);\n" " Fred(C c, struct D d);\n" " Fred(struct E e, F f);\n" " Fred(struct G, struct H);\n" " Fred(I, J);\n" "};\n" "Fred::Fred(A a, B b) { }\n" "Fred::Fred(struct C c, D d) { }\n" "Fred::Fred(E e, struct F f) { }\n" "Fred::Fred(G g, H h) { }\n" "Fred::Fred(struct I i, struct J j) { }\n" ); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" "[test.cpp:11]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" "[test.cpp:12]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" "[test.cpp:13]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n", errout.str()); } void uninitVar24() { // ticket #3190 check("class Foo;\n" "class Bar;\n" "class Sub;\n" "class Foo { class Sub; };\n" "class Bar { class Sub; };\n" "class Bar::Sub {\n" " int b;\n" "public:\n" " Sub() { }\n" " Sub(int);\n" "};\n" "Bar::Sub::Sub(int) { };\n" "class ::Foo::Sub {\n" " int f;\n" "public:\n" " ~Sub();\n" " Sub();\n" "};\n" "::Foo::Sub::~Sub() { }\n" "::Foo::Sub::Sub() { }\n" "class Foo;\n" "class Bar;\n" "class Sub;\n", true); ASSERT_EQUALS("[test.cpp:9]: (warning, inconclusive) Member variable 'Sub::b' is not initialized in the constructor.\n" "[test.cpp:12]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n" "[test.cpp:20]: (warning) Member variable 'Sub::f' is not initialized in the constructor.\n", errout.str()); } void uninitVar25() { // ticket #4789 check("struct A {\n" " int a;\n" " int b;\n" " int c;\n" " A(int x = 0, int y = 0, int z = 0);\n" "};\n" "A::A(int x = 0, int y = 0, int z = 0) { } \n" "struct B {\n" " int a;\n" " int b;\n" " int c;\n" " B(int x = 0, int y = 0, int z = 0);\n" "};\n" "B::B(int x, int y, int z) { } \n" "struct C {\n" " int a;\n" " int b;\n" " int c;\n" " C(int, int, int);\n" "};\n" "C::C(int x = 0, int y = 0, int z = 0) { } \n" "struct D {\n" " int a;\n" " int b;\n" " int c;\n" " D(int, int, int);\n" "};\n" "D::D(int x, int y, int z) { } \n" "struct E {\n" " int a;\n" " int b;\n" " int c;\n" " E(int x, int y, int z);\n" "};\n" "E::E(int, int, int) { } \n" "struct F {\n" " int a;\n" " int b;\n" " int c;\n" " F(int x = 0, int y = 0, int z = 0);\n" "};\n" "F::F(int, int, int) { }\n", true); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:7]: (warning) Member variable 'A::b' is not initialized in the constructor.\n" "[test.cpp:7]: (warning) Member variable 'A::c' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'B::a' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" "[test.cpp:14]: (warning) Member variable 'B::c' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'C::a' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'C::b' is not initialized in the constructor.\n" "[test.cpp:21]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" "[test.cpp:28]: (warning) Member variable 'D::a' is not initialized in the constructor.\n" "[test.cpp:28]: (warning) Member variable 'D::b' is not initialized in the constructor.\n" "[test.cpp:28]: (warning) Member variable 'D::c' is not initialized in the constructor.\n" "[test.cpp:35]: (warning) Member variable 'E::a' is not initialized in the constructor.\n" "[test.cpp:35]: (warning) Member variable 'E::b' is not initialized in the constructor.\n" "[test.cpp:35]: (warning) Member variable 'E::c' is not initialized in the constructor.\n" "[test.cpp:42]: (warning) Member variable 'F::a' is not initialized in the constructor.\n" "[test.cpp:42]: (warning) Member variable 'F::b' is not initialized in the constructor.\n" "[test.cpp:42]: (warning) Member variable 'F::c' is not initialized in the constructor.\n", errout.str()); } void uninitVar26() { check("class A {\n" " int * v;\n" " int sz;\n" "public:\n" " A(int s) {\n" " v = new int [sz = s];\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar27() { check("class A {\n" " double d;\n" "public:\n" " A() {\n" " rtl::math::setNan(&d);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A {\n" " double d;\n" "public:\n" " A() {\n" " ::rtl::math::setNan(&d);\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar28() { check("class Fred {\n" " int i;\n" " float f;\n" "public:\n" " Fred() {\n" " foo(1);\n" " foo(1.0f);\n" " }\n" " void foo(int a) { i = a; }\n" " void foo(float a) { f = a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVar29() { check("class A {\n" " int i;\n" "public:\n" " A() { foo(); }\n" " void foo() const { };\n" " void foo() { i = 0; }\n" "};\n" "class B {\n" " int i;\n" "public:\n" " B() { foo(); }\n" " void foo() { i = 0; }\n" " void foo() const { }\n" "};\n" "class C {\n" " int i;\n" "public:\n" " C() { foo(); }\n" " void foo() const { i = 0; }\n" " void foo() { }\n" "};\n" "class D {\n" " int i;\n" "public:\n" " D() { foo(); }\n" " void foo() { }\n" " void foo() const { i = 0; }\n" "};"); ASSERT_EQUALS("[test.cpp:18]: (warning) Member variable 'C::i' is not initialized in the constructor.\n" "[test.cpp:25]: (warning) Member variable 'D::i' is not initialized in the constructor.\n", errout.str()); } void uninitVar30() { // ticket #6417 check("namespace NS {\n" " class MyClass {\n" " public:\n" " MyClass();\n" " ~MyClass();\n" " private:\n" " bool SomeVar;\n" " };\n" "}\n" "using namespace NS;\n" "MyClass::~MyClass() { }\n" "MyClass::MyClass() : SomeVar(false) { }\n"); ASSERT_EQUALS("", errout.str()); } void uninitVar31() { // ticket #8271 check("void bar();\n" "class MyClass {\n" "public:\n" " MyClass();\n" " void Restart();\n" "protected:\n" " int m_retCode;\n" "};\n" "MyClass::MyClass() {\n" " bar(),Restart();\n" "}\n" "void MyClass::Restart() {\n" " m_retCode = 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void uninitVar32() { // ticket #8835 check("class Foo {\n" " friend class Bar;\n" " int member;\n" "public:\n" " Foo()\n" " {\n" " if (1) {}\n" " }\n" "};\n"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); check("class Foo {\n" " friend class Bar;\n" " int member;\n" "public:\n" " Foo()\n" " {\n" " while (1) {}\n" " }\n" "};\n"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); check("class Foo {\n" " friend class Bar;\n" " int member;\n" "public:\n" " Foo()\n" " {\n" " for (;;) {}\n" " }\n" "};\n"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout.str()); } void uninitVarArray1() { check("class John\n" "{\n" "public:\n" " John() {}\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'John::name' is not initialized in the constructor.\n", errout.str()); check("class John\n" "{\n" "public:\n" " John() {John::name[0] = '\\0';}\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("", errout.str()); check("class John\n" "{\n" "public:\n" " John() { strcpy(name, \"\"); }\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("", errout.str()); check("class John\n" "{\n" "public:\n" " John() { }\n" "\n" " double operator[](const unsigned long i);\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A;\n" "class John\n" "{\n" "public:\n" " John() { }\n" " A a[5];\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A;\n" "class John\n" "{\n" "public:\n" " John() { }\n" " A (*a)[5];\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'John::a' is not initialized in the constructor.\n", errout.str()); } void uninitVarArray2() { check("class John\n" "{\n" "public:\n" " John() { *name = 0; }\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("", errout.str()); // #5754 check("class John\n" "{\n" "public:\n" " John() {*this->name = '\\0';}\n" "\n" "private:\n" " char name[255];\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray3() { check("class John\n" "{\n" "private:\n" " int a[100];\n" " int b[100];\n" "\n" "public:\n" " John()\n" " {\n" " memset(a,0,sizeof(a));\n" " memset(b,0,sizeof(b));\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray4() { check("class John\n" "{\n" "private:\n" " int a[100];\n" " int b[100];\n" "\n" "public:\n" " John()\n" " {\n" " if (snprintf(a,10,\"a\")) { }\n" " if (snprintf(b,10,\"b\")) { }\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray5() { check("class Foo\n" "{\n" "private:\n" " Bar bars[10];\n" "public:\n" " Foo()\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray6() { check("class Foo\n" "{\n" "public:\n" " Foo();\n" " static const char STR[];\n" "};\n" "const char Foo::STR[] = \"abc\";\n" "Foo::Foo() { }"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray7() { check("class Foo\n" "{\n" " int array[10];\n" "public:\n" " Foo() { }\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::array' is not initialized in the constructor.\n", errout.str()); check("class Foo\n" "{\n" " int array[10];\n" "public:\n" " Foo() { memset(array, 0, sizeof(array)); }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Foo\n" "{\n" " int array[10];\n" "public:\n" " Foo() { ::memset(array, 0, sizeof(array)); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray8() { check("class Foo {\n" " char a[10];\n" "public:\n" " Foo() { ::ZeroMemory(a); }\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray9() { // #6957 check("class BaseGDL;\n" "struct IxExprListT {\n" "private:\n" " BaseGDL* eArr[3];\n" "public:\n" " IxExprListT() {}\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'IxExprListT::eArr' is not initialized in the constructor.\n", errout.str()); check("struct sRAIUnitDefBL {\n" " sRAIUnitDefBL();\n" " ~sRAIUnitDefBL();\n" "};\n" "struct sRAIUnitDef {\n" " sRAIUnitDef() {}\n" " sRAIUnitDefBL *List[35];\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'sRAIUnitDef::List' is not initialized in the constructor.\n", errout.str()); } void uninitVarArray2D() { check("class John\n" "{\n" "public:\n" " John() { a[0][0] = 0; }\n" "\n" "private:\n" " char a[2][2];\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarArray3D() { check("class John\n" "{\n" "private:\n" " char a[2][2][2];\n" "public:\n" " John() { a[0][0][0] = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarCpp11Init1() { check("class Foo {\n" " std::vector bar;\n" "public:\n" " Foo()\n" " : bar({\"a\", \"b\"})\n" " {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarCpp11Init2() { check("class Fred {\n" " struct Foo {\n" " int a;\n" " bool b;\n" " };\n" " Foo f;\n" " float g;\n" "public:\n" " Fred() : f{0, true} { }\n" " float get() const;\n" "};\n" "float Fred::get() const { return g; }"); ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Fred::g' is not initialized in the constructor.\n", errout.str()); } void uninitVarStruct1() { // ticket #2172 check("class A\n" "{\n" "private:\n" " struct B {\n" " std::string str1;\n" " std::string str2;\n" " }\n" " struct B b;\n" "public:\n" " A() {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" "private:\n" " struct B {\n" " char *str1;\n" " char *str2;\n" " }\n" " struct B b;\n" "public:\n" " A() {\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'A::b' is not initialized in the constructor.\n", errout.str()); check("class A\n" "{\n" "private:\n" " struct B {\n" " char *str1;\n" " char *str2;\n" " B() : str1(NULL), str2(NULL) { }\n" " }\n" " struct B b;\n" "public:\n" " A() {\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarStruct2() { // ticket #838 check("struct POINT\n" "{\n" " int x;\n" " int y;\n" "};\n" "class Fred\n" "{\n" "private:\n" " POINT p;\n" "public:\n" " Fred()\n" " { }\n" "};"); ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Fred::p' is not initialized in the constructor.\n", errout.str()); check("struct POINT\n" "{\n" " int x;\n" " int y;\n" " POINT();\n" "};\n" "class Fred\n" "{\n" "private:\n" " POINT p;\n" "public:\n" " Fred()\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct POINT\n" "{\n" " int x;\n" " int y;\n" " POINT() :x(0), y(0) { }\n" "};\n" "class Fred\n" "{\n" "private:\n" " POINT p;\n" "public:\n" " Fred()\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); // non static data-member initialization check("struct POINT\n" "{\n" " int x=0;\n" " int y=0;\n" "};\n" "class Fred\n" "{\n" "private:\n" " POINT p;\n" "public:\n" " Fred()\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarUnion1() { // ticket #3196 check("class Fred\n" "{\n" "private:\n" " union { int a; int b; };\n" "public:\n" " Fred()\n" " { a = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarUnion2() { // If the "data_type" is 0 it means union member "data" is invalid. // So it's ok to not initialize "data". // related forum: http://sourceforge.net/apps/phpbb/cppcheck/viewtopic.php?f=3&p=1806 check("union Data { int id; int *ptr; };\n" "\n" "class Fred {\n" "private:\n" " int data_type;\n" " Data data;\n" "public:\n" " Fred() : data_type(0)\n" " { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitMissingFuncDef() { // Unknown member function check("class Fred\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " void Init();" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); // Unknown non-member function (friend class) check("class Fred\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " friend ABC;\n" " int i;\n" "};"); ASSERT_EQUALS("", errout.str()); // Unknown non-member function (is Init a virtual function?) check("class Fred : private ABC\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); // Unknown non-member function check("class Fred\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); // Unknown non-member function check("class ABC { };\n" "class Fred : private ABC\n" "{\n" "public:\n" " Fred() { Init(); }\n" "private:\n" " int i;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void uninitVarEnum1() { check("class Fred\n" "{\n" "public:\n" " enum abc {a,b,c};\n" " Fred() {}\n" "private:\n" " unsigned int i;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout.str()); } void uninitVarEnum2() { // ticket #8146 check("enum E { E1 };\n" "struct X { E e = E1; };\n" "struct Y {\n" " Y() {}\n" " X x;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarStream() { check("class Foo\n" "{\n" "private:\n" " int foo;\n" "public:\n" " explicit Foo(std::istream &in)\n" " {\n" " if(!(in >> foo))\n" " throw 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarTypedef() { check("class Foo\n" "{\n" "public:\n" " typedef int * pointer;\n" " Foo() : a(0) {}\n" " pointer a;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitVarMemset() { check("class Foo\n" "{\n" "public:\n" " int * pointer;\n" " Foo() { memset(this, 0, sizeof(*this)); }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Foo\n" "{\n" "public:\n" " int * pointer;\n" " Foo() { ::memset(this, 0, sizeof(*this)); }\n" "};"); ASSERT_EQUALS("", errout.str()); // Ticket #7068 check("struct Foo {\n" " int * p;\n" " char c;\n" " Foo() { memset(p, 0, sizeof(int)); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::c' is not initialized in the constructor.\n", errout.str()); check("struct Foo {\n" " int i;\n" " char c;\n" " Foo() { memset(&i, 0, sizeof(int)); }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::c' is not initialized in the constructor.\n", errout.str()); check("struct Foo { int f; };\n" "struct Bar { int b; };\n" "struct FooBar {\n" " FooBar() {\n" " memset(&foo, 0, sizeof(foo));\n" " }\n" " Foo foo;\n" " Bar bar;\n" "};\n" "int main() {\n" " FooBar foobar;\n" " return foobar.foo.f + foobar.bar.b;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'FooBar::bar' is not initialized in the constructor.\n", errout.str()); check("struct Foo { int f; };\n" "struct Bar { int b; };\n" "struct FooBar {\n" " FooBar() {\n" " memset(&this->foo, 0, sizeof(this->foo));\n" " }\n" " Foo foo;\n" " Bar bar;\n" "};\n" "int main() {\n" " FooBar foobar;\n" " return foobar.foo.f + foobar.bar.b;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'FooBar::bar' is not initialized in the constructor.\n", errout.str()); // #7755 check("struct A {\n" " A() {\n" " memset(this->data, 0, 42);\n" " }\n" " char data[42];\n" "};"); ASSERT_EQUALS("", errout.str()); } void privateCtor1() { settings.standards.cpp = Standards::CPP03; check("class Foo {\n" " int foo;\n" " Foo() { }\n" "};"); ASSERT_EQUALS("", errout.str()); settings.standards.cpp = Standards::CPP11; check("class Foo {\n" " int foo;\n" " Foo() { }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Foo::foo' is not initialized in the constructor.\n", errout.str()); } void privateCtor2() { check("class Foo\n" "{\n" "private:\n" " int foo;\n" " Foo() { }\n" "public:\n" " explicit Foo(int _i) { }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Foo::foo' is not initialized in the constructor.\n", errout.str()); } void function() { check("class A\n" "{\n" "public:\n" " A();\n" " int* f(int*);\n" "};\n" "\n" "A::A()\n" "{\n" "}\n" "\n" "int* A::f(int* p)\n" "{\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Borland C++: No FP for published pointers - they are automatically initialized void uninitVarPublished() { check("class Fred\n" "{\n" "__published:\n" " int *i;\n" "public:\n" " Fred() { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitOperator() { check("class Fred\n" "{\n" "public:\n" " Fred() { }\n" " int *operator [] (int index) { return 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitFunction1() { check("class Fred\n" "{\n" "public:\n" " Fred() { init(*this); }\n" "\n" " static void init(Fred &f)\n" " { f.d = 0; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { init(*this); }\n" "\n" " static void init(Fred &f)\n" " { }\n" "\n" " double d;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n", "", errout.str()); } void uninitFunction2() { check("class Fred\n" "{\n" "public:\n" " Fred() { if (!init(*this)); }\n" "\n" " static bool init(Fred &f)\n" " { f.d = 0; return true; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { if (!init(*this)); }\n" "\n" " static bool init(Fred &f)\n" " { return true; }\n" "\n" " double d;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n", "", errout.str()); } void uninitFunction3() { check("class Fred\n" "{\n" "public:\n" " Fred() { if (!init()); }\n" "\n" " bool init()\n" " { d = 0; return true; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { if (!init()); }\n" "\n" " bool init()\n" " { return true; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n", errout.str()); } void uninitFunction4() { check("class Fred\n" "{\n" "public:\n" " Fred() { init(this); }\n" "\n" " init(Fred *f)\n" " { f.d = 0; }\n" "\n" " double d;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { init(this); }\n" "\n" " init(Fred *f)\n" " { }\n" "\n" " double d;\n" "};"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n", "", errout.str()); } void uninitFunction5() { // #4072 - FP about struct that is initialized in function check("struct Structure {\n" " int C;\n" "};\n" "\n" "class A {\n" " Structure B;\n" "public:\n" " A() { Init( B ); };\n" " void Init( Structure& S ) { S.C = 0; };\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitSameClassName() { check("class B\n" "{\n" "public:\n" " B();\n" " int j;\n" "};\n" "\n" "class A\n" "{\n" " class B\n" " {\n" " public:\n" " B();\n" " int i;\n" " };\n" "};\n" "\n" "A::B::B()\n" "{\n" " i = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class B\n" "{\n" "public:\n" " B();\n" " int j;\n" "};\n" "\n" "class A\n" "{\n" " class B\n" " {\n" " public:\n" " B();\n" " int i;\n" " };\n" "};\n" "\n" "B::B()\n" "{\n" "}\n" "\n" "A::B::B()\n" "{\n" "}"); ASSERT_EQUALS("[test.cpp:18]: (warning) Member variable 'B::j' is not initialized in the constructor.\n" "[test.cpp:22]: (warning) Member variable 'B::i' is not initialized in the constructor.\n", errout.str()); // Ticket #1700 check("namespace n1\n" "{\n" "class Foo {" "public:\n" " Foo() : i(0) { }\n" "private:\n" " int i;\n" "};\n" "}\n" "\n" "namespace n2\n" "{\n" "class Foo {" "public:\n" " Foo() { }\n" "};\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace n1\n" "{\n" "class Foo {\n" "public:\n" " Foo();\n" "private:\n" " int i;\n" "};\n" "}\n" "\n" "n1::Foo::Foo()\n" "{ }\n" "\n" "namespace n2\n" "{\n" "class Foo {\n" "public:\n" " Foo() { }\n" "};\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Foo::i' is not initialized in the constructor.\n", errout.str()); check("namespace n1\n" "{\n" "class Foo {" "public:\n" " Foo();\n" "private:\n" " int i;\n" "};\n" "}\n" "\n" "n1::Foo::Foo() : i(0)\n" "{ }\n" "\n" "namespace n2\n" "{\n" "class Foo {" "public:\n" " Foo() { }\n" "};\n" "}"); ASSERT_EQUALS("", errout.str()); } void uninitFunctionOverload() { // Ticket #1783 - overloaded "init" functions check("class A\n" "{\n" "private:\n" " int i;\n" "\n" "public:\n" " A()\n" " {\n" " init();\n" " }\n" "\n" " void init() { init(0); }\n" "\n" " void init(int value)\n" " { i = value; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" "private:\n" " int i;\n" "\n" "public:\n" " A()\n" " {\n" " init();\n" " }\n" "\n" " void init() { init(0); }\n" "\n" " void init(int value)\n" " { }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::i' is not initialized in the constructor.\n", errout.str()); } void uninitVarOperatorEqual() { // ticket #2415 check("struct A {\n" " int a;\n" " A() { a=0; }\n" " A(A const &a) { operator=(a); }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " A() { a=0; }\n" " A(A const &a) { operator=(a); }\n" " A & operator = (const A & rhs) {\n" " a = rhs.a;\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int a;\n" " A() { a=0; }\n" " A(A const &a) { operator=(a); }\n" " A & operator = (const A & rhs) {\n" " return *this;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'A::a' is not assigned a value in 'A::operator='.\n", errout.str()); } void uninitVarPointer() { // #3801 check("struct A {\n" " int a;\n" "};\n" "struct B {\n" " A* a;\n" " B() { }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct A;\n" "struct B {\n" " A* a;\n" " B() { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct A;\n" "struct B {\n" " const A* a;\n" " B() { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct A;\n" "struct B {\n" " A* const a;\n" " B() { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); } void uninitConstVar() { check("struct A;\n" "struct B {\n" " A* const a;\n" " B() { }\n" " B(B& b) { }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n" "[test.cpp:5]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct A;\n" "struct B {\n" " A* const a;\n" " B& operator=(const B& r) { }\n" "};"); ASSERT_EQUALS("", errout.str()); // #3804 check("struct B {\n" " const int a;\n" " B() { }\n" " B(B& b) { }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'B::a' is not initialized in the constructor.\n" "[test.cpp:4]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout.str()); check("struct B {\n" " const int a;\n" " B& operator=(const B& r) { }\n" "};"); ASSERT_EQUALS("", errout.str()); } // Ticket #5641 "Regression. Crash for 'C() _STLP_NOTHROW {}'" void constructors_crash1() { check("class C {\n" "public:\n" " C() _STLP_NOTHROW {}\n" " C(const C&) _STLP_NOTHROW {}\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void classWithOperatorInName() { // ticket #2827 check("class operatorX {\n" " int mValue;\n" "public:\n" " operatorX() : mValue(0) {}\n" "};"); ASSERT_EQUALS("", errout.str()); } void templateConstructor() { // ticket #7942 check("template struct Container {\n" " Container();\n" " T* mElements;\n" "};\n" "template Container::Container() : mElements(nullptr) {}\n" "Container intContainer;"); ASSERT_EQUALS("", errout.str()); } void typedefArray() { // ticket #5766 check("typedef float rvec[3];\n" "class SelectionPosition {\n" "public:\n" " SelectionPosition() {}\n" " const rvec &x() const;\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitAssignmentWithOperator() { check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b = b && SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};", true); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'C::x' is not initialized in the constructor.\n", "[test.cpp:3]: (warning) Member variable 'C::x' is not initialized in the constructor.\n", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b = true || SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};", true); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'C::x' is not initialized in the constructor.\n", "[test.cpp:3]: (warning) Member variable 'C::x' is not initialized in the constructor.\n", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = true;\n" " b = b & SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b = true | SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i * SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i / SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i % SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i + SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i - SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i << SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i >> SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i = i ^ SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitCompoundAssignment() { check("struct C {\n" " int x;\n" " C() {\n" " bool b = true;\n" " b &= SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b |= SetValue();\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i *= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i /= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i %= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i += SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i -= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i <<= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i >>= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " int i = 0;\n" " i ^= SetValue();\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitComparisonAssignment() { check("struct C {\n" " int x;\n" " C() {\n" " bool b = true;\n" " b = (true == SetValue());\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = false;\n" " b |= (true != SetValue());\n" " }\n" " bool SetValue() {\n" " x = 1;\n" " return true;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = (0 < SetValue());\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = (0 <= SetValue());\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = (0 > SetValue());\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("struct C {\n" " int x;\n" " C() {\n" " bool b = (0 >= SetValue());\n" " }\n" " int SetValue() { return x = 1; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void uninitTemplate1() { check("template class C;\n" "template \n" "class C {\n" " public:\n" " C() : b(0) { }\n" " C(A* a) : b(a) { }\n" " private:\n" " A* b;\n" "};\n" "template \n" "class C {\n" " private:\n" " A* b;\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("template class A{};\n" "template class B{};\n" "template\n" "class A> {\n" " public:\n" " A();\n" " bool m_value;\n" "};\n" "template\n" "A>::A() : m_value(false) {}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestConstructors) cppcheck-1.90/test/testcppcheck.cpp000066400000000000000000000066431357737443600174220ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check.h" #include "cppcheck.h" #include "errorlogger.h" #include "testsuite.h" #include #include #include class TestCppcheck : public TestFixture { public: TestCppcheck() : TestFixture("TestCppcheck") { } private: class ErrorLogger2 : public ErrorLogger { public: std::list id; void reportOut(const std::string & /*outmsg*/) OVERRIDE { } void reportErr(const ErrorLogger::ErrorMessage &msg) OVERRIDE { id.push_back(msg.id); } }; void run() OVERRIDE { TEST_CASE(instancesSorted); TEST_CASE(classInfoFormat); TEST_CASE(getErrorMessages); } void instancesSorted() const { for (std::list::const_iterator i = Check::instances().begin(); i != Check::instances().end(); ++i) { std::list::const_iterator j = i; ++j; if (j != Check::instances().end()) { ASSERT_EQUALS(true, (*i)->name() < (*j)->name()); } } } void classInfoFormat() const { for (std::list::const_iterator i = Check::instances().begin(); i != Check::instances().end(); ++i) { const std::string info = (*i)->classInfo(); if (!info.empty()) { ASSERT('\n' != info[0]); // No \n in the beginning ASSERT('\n' == info.back()); // \n at end if (info.size() > 1) ASSERT('\n' != info[info.length()-2]); // Only one \n at end } } } void getErrorMessages() const { ErrorLogger2 errorLogger; CppCheck cppCheck(errorLogger, true); cppCheck.getErrorMessages(); ASSERT(!errorLogger.id.empty()); // Check if there are duplicate error ids in errorLogger.id std::string duplicate; for (std::list::iterator it = errorLogger.id.begin(); it != errorLogger.id.end(); ++it) { if (std::find(errorLogger.id.begin(), it, *it) != it) { duplicate = "Duplicate ID: " + *it; break; } } ASSERT_EQUALS("", duplicate); // Check for error ids from this class. bool foundPurgedConfiguration = false; bool foundTooManyConfigs = false; for (const std::string & it : errorLogger.id) { if (it == "purgedConfiguration") foundPurgedConfiguration = true; else if (it == "toomanyconfigs") foundTooManyConfigs = true; } ASSERT(foundPurgedConfiguration); ASSERT(foundTooManyConfigs); } }; REGISTER_TEST(TestCppcheck) cppcheck-1.90/test/testerrorlogger.cpp000066400000000000000000000424021357737443600201640ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "cppcheck.h" #include "errorlogger.h" #include "suppressions.h" #include "testsuite.h" #include #include class TestErrorLogger : public TestFixture { public: TestErrorLogger() : TestFixture("TestErrorLogger"), fooCpp5("foo.cpp", 5, 1), barCpp8("bar.cpp", 8, 1) { } private: const ErrorLogger::ErrorMessage::FileLocation fooCpp5; const ErrorLogger::ErrorMessage::FileLocation barCpp8; void run() OVERRIDE { TEST_CASE(PatternSearchReplace); TEST_CASE(FileLocationDefaults); TEST_CASE(FileLocationSetFile); TEST_CASE(ErrorMessageConstruct); TEST_CASE(ErrorMessageConstructLocations); TEST_CASE(ErrorMessageVerbose); TEST_CASE(ErrorMessageVerboseLocations); TEST_CASE(CustomFormat); TEST_CASE(CustomFormat2); TEST_CASE(CustomFormatLocations); TEST_CASE(ToXmlV2); TEST_CASE(ToXmlV2Locations); TEST_CASE(ToXmlV2Encoding); // Inconclusive results in xml reports.. TEST_CASE(InconclusiveXml); // Serialize / Deserialize inconclusive message TEST_CASE(SerializeInconclusiveMessage); TEST_CASE(DeserializeInvalidInput); TEST_CASE(SerializeSanitize); TEST_CASE(SerializeFileLocation); TEST_CASE(suppressUnmatchedSuppressions); } void TestPatternSearchReplace(const std::string& idPlaceholder, const std::string& id) const { const std::string plainText = "text"; ErrorLogger::ErrorMessage message; message.id = id; std::string serialized = message.toString(true, idPlaceholder + plainText + idPlaceholder); ASSERT_EQUALS(id + plainText + id, serialized); serialized = message.toString(true, idPlaceholder + idPlaceholder); ASSERT_EQUALS(id + id, serialized); serialized = message.toString(true, plainText + idPlaceholder + plainText); ASSERT_EQUALS(plainText + id + plainText, serialized); } void PatternSearchReplace() const { const std::string idPlaceholder = "{id}"; const std::string empty; TestPatternSearchReplace(idPlaceholder, empty); const std::string shortIdValue = "ID"; ASSERT_EQUALS(true, shortIdValue.length() < idPlaceholder.length()); TestPatternSearchReplace(idPlaceholder, shortIdValue); const std::string mediumIdValue = "_ID_"; ASSERT_EQUALS(mediumIdValue.length(), idPlaceholder.length()); TestPatternSearchReplace(idPlaceholder, mediumIdValue); const std::string longIdValue = "longId"; ASSERT_EQUALS(true, longIdValue.length() > idPlaceholder.length()); TestPatternSearchReplace(idPlaceholder, longIdValue); } void FileLocationDefaults() const { ErrorLogger::ErrorMessage::FileLocation loc; ASSERT_EQUALS("", loc.getfile()); ASSERT_EQUALS(0, loc.line); } void FileLocationSetFile() const { ErrorLogger::ErrorMessage::FileLocation loc; loc.setfile("foo.cpp"); ASSERT_EQUALS("foo.cpp", loc.getfile()); ASSERT_EQUALS(0, loc.line); } void ErrorMessageConstruct() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.", "errorId", false); ASSERT_EQUALS(1, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Programming error.", msg.verboseMessage()); ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false)); ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(true)); } void ErrorMessageConstructLocations() const { std::list locs = { fooCpp5, barCpp8 }; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.", "errorId", false); ASSERT_EQUALS(2, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Programming error.", msg.verboseMessage()); ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false)); ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(true)); } void ErrorMessageVerbose() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS(1, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false)); ASSERT_EQUALS("[foo.cpp:5]: (error) Verbose error", msg.toString(true)); } void ErrorMessageVerboseLocations() const { std::list locs = { fooCpp5, barCpp8 }; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS(2, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false)); ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Verbose error", msg.toString(true)); } void CustomFormat() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS(1, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("foo.cpp:5,error,errorId,Programming error.", msg.toString(false, "{file}:{line},{severity},{id},{message}")); ASSERT_EQUALS("foo.cpp:5,error,errorId,Verbose error", msg.toString(true, "{file}:{line},{severity},{id},{message}")); } void CustomFormat2() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS(1, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("Programming error. - foo.cpp(5):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})")); ASSERT_EQUALS("Verbose error - foo.cpp(5):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})")); } void CustomFormatLocations() const { // Check that first location from location stack is used in template std::list locs = { fooCpp5, barCpp8 }; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); ASSERT_EQUALS(2, msg.callStack.size()); ASSERT_EQUALS("Programming error.", msg.shortMessage()); ASSERT_EQUALS("Verbose error", msg.verboseMessage()); ASSERT_EQUALS("Programming error. - bar.cpp(8):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})")); ASSERT_EQUALS("Verbose error - bar.cpp(8):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})")); } void ToXmlV2() const { std::list locs(1, fooCpp5); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); std::string header("\n\n"); header += " \n "; ASSERT_EQUALS(header, ErrorLogger::ErrorMessage::getXMLHeader()); ASSERT_EQUALS(" \n", ErrorLogger::ErrorMessage::getXMLFooter()); std::string message(" \n"; message += " \n "; ASSERT_EQUALS(message, msg.toXML()); } void ToXmlV2Locations() const { std::list locs = { fooCpp5, barCpp8 }; locs.back().setinfo("ä"); ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", false); std::string header("\n\n"); header += " \n "; ASSERT_EQUALS(header, ErrorLogger::ErrorMessage::getXMLHeader()); ASSERT_EQUALS(" \n", ErrorLogger::ErrorMessage::getXMLFooter()); std::string message(" \n"; message += " \n"; message += " \n "; ASSERT_EQUALS(message, msg.toXML()); } void ToXmlV2Encoding() const { { std::list locs; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error.\nComparing \"\203\" with \"\003\"", "errorId", false); const std::string expected(" "); ASSERT_EQUALS(expected, msg.toXML()); } { const char code1[]="äöü"; const char code2[]="\x12\x00\x00\x01"; std::list locs; ErrorMessage msg1(locs, emptyString, Severity::error, std::string("Programming error.\nReading \"")+code1+"\"", "errorId", false); ASSERT_EQUALS(" ", msg1.toXML()); ErrorMessage msg2(locs, emptyString, Severity::error, std::string("Programming error.\nReading \"")+code2+"\"", "errorId", false); ASSERT_EQUALS(" ", msg2.toXML()); } } void InconclusiveXml() const { // Location std::list locs(1, fooCpp5); // Inconclusive error message ErrorMessage msg(locs, emptyString, Severity::error, "Programming error", "errorId", true); // xml version 2 error message ASSERT_EQUALS(" \n" " \n" " ", msg.toXML()); } void SerializeInconclusiveMessage() const { // Inconclusive error message std::list locs; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error", "errorId", true); ASSERT_EQUALS("7 errorId" "5 error" "1 0" "12 inconclusive" "17 Programming error" "17 Programming error" "0 ", msg.serialize()); ErrorMessage msg2; msg2.deserialize(msg.serialize()); ASSERT_EQUALS("errorId", msg2.id); ASSERT_EQUALS(Severity::error, msg2.severity); ASSERT_EQUALS(true, msg2.inconclusive); ASSERT_EQUALS("Programming error", msg2.shortMessage()); ASSERT_EQUALS("Programming error", msg2.verboseMessage()); } void DeserializeInvalidInput() const { ErrorMessage msg; ASSERT_THROW(msg.deserialize("500foobar"), InternalError); } void SerializeSanitize() const { std::list locs; ErrorMessage msg(locs, emptyString, Severity::error, std::string("Illegal character in \"foo\001bar\""), "errorId", false); ASSERT_EQUALS("7 errorId" "5 error" "1 0" "33 Illegal character in \"foo\\001bar\"" "33 Illegal character in \"foo\\001bar\"" "0 ", msg.serialize()); ErrorMessage msg2; msg2.deserialize(msg.serialize()); ASSERT_EQUALS("errorId", msg2.id); ASSERT_EQUALS(Severity::error, msg2.severity); ASSERT_EQUALS("Illegal character in \"foo\\001bar\"", msg2.shortMessage()); ASSERT_EQUALS("Illegal character in \"foo\\001bar\"", msg2.verboseMessage()); } void SerializeFileLocation() const { ErrorLogger::ErrorMessage::FileLocation loc1(":/,;", 654, 33); loc1.setfile("[]:;,()"); loc1.setinfo("abcd:/,"); std::list locs{loc1}; ErrorMessage msg(locs, emptyString, Severity::error, "Programming error", "errorId", true); ErrorMessage msg2; msg2.deserialize(msg.serialize()); ASSERT_EQUALS("[]:;,()", msg2.callStack.front().getfile(false)); ASSERT_EQUALS(":/,;", msg2.callStack.front().getOrigFile(false)); ASSERT_EQUALS(654, msg2.callStack.front().line); ASSERT_EQUALS(33, msg2.callStack.front().column); ASSERT_EQUALS("abcd:/,", msg2.callStack.front().getinfo()); } void suppressUnmatchedSuppressions() { std::list suppressions; // No unmatched suppression errout.str(""); suppressions.clear(); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "*", Suppressions::Suppression::NO_LINE); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression (corresponds to "--suppress=unmatchedSuppression") errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "", Suppressions::Suppression::NO_LINE); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress all unmatchedSuppression in a.c errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", Suppressions::Suppression::NO_LINE); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // suppress unmatchedSuppression in a.c at line 10 errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", 10U); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("", errout.str()); // don't suppress unmatchedSuppression when file is mismatching errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "b.c", Suppressions::Suppression::NO_LINE); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str()); // don't suppress unmatchedSuppression when line is mismatching errout.str(""); suppressions.clear(); suppressions.emplace_back("abc", "a.c", 10U); suppressions.emplace_back("unmatchedSuppression", "a.c", 1U); reportUnmatchedSuppressions(suppressions); ASSERT_EQUALS("[a.c:10]: (information) Unmatched suppression: abc\n", errout.str()); } }; REGISTER_TEST(TestErrorLogger) cppcheck-1.90/test/testexceptionsafety.cpp000066400000000000000000000337471357737443600210610ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkexceptionsafety.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestExceptionSafety : public TestFixture { public: TestExceptionSafety() : TestFixture("TestExceptionSafety") { } private: Settings settings; void run() OVERRIDE { settings.addEnabled("all"); TEST_CASE(destructors); TEST_CASE(deallocThrow1); TEST_CASE(deallocThrow2); TEST_CASE(deallocThrow3); TEST_CASE(rethrowCopy1); TEST_CASE(rethrowCopy2); TEST_CASE(rethrowCopy3); TEST_CASE(rethrowCopy4); TEST_CASE(rethrowCopy5); TEST_CASE(catchExceptionByValue); TEST_CASE(noexceptThrow); TEST_CASE(nothrowThrow); TEST_CASE(unhandledExceptionSpecification1); // #4800 TEST_CASE(unhandledExceptionSpecification2); TEST_CASE(nothrowAttributeThrow); TEST_CASE(nothrowAttributeThrow2); // #5703 TEST_CASE(nothrowDeclspecThrow); } void check(const char code[], bool inconclusive = false) { // Clear the error buffer.. errout.str(""); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check char variable usage.. CheckExceptionSafety checkExceptionSafety(&tokenizer, &settings, this); checkExceptionSafety.runChecks(&tokenizer, &settings, this); } void destructors() { check("class x {\n" " ~x() {\n" " throw e;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (warning) Class x is not safe, destructor throws exception\n", errout.str()); check("class x {\n" " ~x();\n" "};\n" "x::~x() {\n" " throw e;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Class x is not safe, destructor throws exception\n", errout.str()); // #3858 - throwing exception in try block in destructor. check("class x {\n" " ~x() {\n" " try {\n" " throw e;\n" " } catch (...) {\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("class x {\n" " ~x() {\n" " if(!std::uncaught_exception()) {\n" " throw e;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void deallocThrow1() { check("int * p;\n" "void f(int x) {\n" " delete p;\n" " if (x)\n" " throw 123;\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout.str()); check("void f() {\n" " static int* p = foo;\n" " delete p;\n" " if (foo)\n" " throw 1;\n" " p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout.str()); } void deallocThrow2() { check("void f() {\n" " int* p = 0;\n" " delete p;\n" " if (foo)\n" " throw 1;\n" " p = new int;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static int* p = 0;\n" " delete p;\n" " reset(p);\n" " throw 1;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void deallocThrow3() { check("void f() {\n" " static int* p = 0;\n" " delete p;\n" " throw 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static int* p = 0;\n" " delete p;\n" " throw 1;\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout.str()); } void rethrowCopy1() { check("void f() {\n" " try\n" " {\n" " foo();\n" " }\n" " catch(const exception& err)\n" " {\n" " throw err;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout.str()); } void rethrowCopy2() { check("void f() {\n" " try\n" " {\n" " foo();\n" " }\n" " catch(exception& err)\n" " {\n" " throw err;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout.str()); } void rethrowCopy3() { check("void f() {\n" " try {\n" " foo();\n" " }\n" " catch(std::runtime_error& err) {\n" " throw err;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout.str()); } void rethrowCopy4() { check("void f() {\n" " try\n" " {\n" " foo();\n" " }\n" " catch(const exception& err)\n" " {\n" " exception err2;\n" " throw err2;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void rethrowCopy5() { check("void f() {\n" " try {\n" " foo();\n" " }\n" " catch(const exception& outer) {\n" " try {\n" " foo(outer);\n" " }\n" " catch(const exception& inner) {\n" " throw inner;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout.str()); check("void f() {\n" " try {\n" " foo();\n" " }\n" " catch(const exception& outer) {\n" " try {\n" " foo(outer);\n" " }\n" " catch(const exception& inner) {\n" " throw outer;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void catchExceptionByValue() { check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch( ::std::exception err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Exception should be caught by reference.\n", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(const exception err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Exception should be caught by reference.\n", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch( ::std::exception& err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(exception* err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(const exception& err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(int err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " try {\n" " bar();\n" " }\n" " catch(exception* const err) {\n" " foo(err);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void noexceptThrow() { check("void func1() noexcept(false) { throw 1; }\n" "void func2() noexcept { throw 1; }\n" "void func3() noexcept(true) { throw 1; }\n" "void func4() noexcept(false) { throw 1; }\n" "void func5() noexcept(true) { func1(); }\n" "void func6() noexcept(false) { func1(); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:5]: (error) Exception thrown in function declared not to throw exceptions.\n", errout.str()); // avoid false positives check("const char *func() noexcept { return 0; }\n"); ASSERT_EQUALS("", errout.str()); } void nothrowThrow() { check("void func1() throw(int) { throw 1; }\n" "void func2() throw() { throw 1; }\n" "void func3() throw(int) { throw 1; }\n" "void func4() throw() { func1(); }\n" "void func5() throw(int) { func1(); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:4]: (error) Exception thrown in function declared not to throw exceptions.\n", errout.str()); // avoid false positives check("const char *func() throw() { return 0; }\n"); ASSERT_EQUALS("", errout.str()); } void unhandledExceptionSpecification1() { // #4800 check("void myThrowingFoo() throw(MyException) {\n" " throw MyException();\n" "}\n" "void myNonCatchingFoo() {\n" " myThrowingFoo();\n" "}\n" "void myCatchingFoo() {\n" " try {\n" " myThrowingFoo();\n" " } catch(MyException &) {}\n" "}\n", true); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1]: (style, inconclusive) Unhandled exception specification when calling function myThrowingFoo().\n", errout.str()); } void unhandledExceptionSpecification2() { check("void f() const throw (std::runtime_error);\n" "int main()\n" "{\n" " f();\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nothrowAttributeThrow() { check("void func1() throw(int) { throw 1; }\n" "void func2() __attribute((nothrow)); void func2() { throw 1; }\n" "void func3() __attribute((nothrow)); void func3() { func1(); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n", errout.str()); // avoid false positives check("const char *func() __attribute((nothrow)); void func1() { return 0; }\n"); ASSERT_EQUALS("", errout.str()); } void nothrowAttributeThrow2() { check("class foo {\n" " void copyMemberValues() throw () {\n" " copyMemberValues();\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); } void nothrowDeclspecThrow() { check("void func1() throw(int) { throw 1; }\n" "void __declspec(nothrow) func2() { throw 1; }\n" "void __declspec(nothrow) func3() { func1(); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n", errout.str()); // avoid false positives check("const char *func() __attribute((nothrow)); void func1() { return 0; }\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestExceptionSafety) cppcheck-1.90/test/testexprengine.cpp000066400000000000000000000361271357737443600200060ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "exprengine.h" #include "settings.h" #include "symboldatabase.h" #include "tokenize.h" #include "testsuite.h" #include #include class TestExprEngine : public TestFixture { public: TestExprEngine() : TestFixture("TestExprEngine") { } private: void run() OVERRIDE { #ifdef USE_Z3 TEST_CASE(expr1); TEST_CASE(expr2); TEST_CASE(expr3); TEST_CASE(expr4); TEST_CASE(expr5); TEST_CASE(expr6); TEST_CASE(expr7); TEST_CASE(exprAssign1); TEST_CASE(exprAssign2); // Truncation TEST_CASE(if1); TEST_CASE(ifelse1); TEST_CASE(switch1); TEST_CASE(while1); TEST_CASE(while2); TEST_CASE(while3); TEST_CASE(array1); TEST_CASE(array2); TEST_CASE(array3); TEST_CASE(array4); TEST_CASE(arrayInit1); TEST_CASE(arrayInit2); TEST_CASE(arrayUninit); TEST_CASE(floatValue1); TEST_CASE(floatValue2); TEST_CASE(functionCall1); TEST_CASE(functionCall2); TEST_CASE(functionCall3); TEST_CASE(int1); TEST_CASE(pointer1); TEST_CASE(pointerAlias1); TEST_CASE(pointerAlias2); TEST_CASE(pointerAlias3); TEST_CASE(pointerAlias4); TEST_CASE(pointerNull1); TEST_CASE(structMember); #endif } std::string expr(const char code[], const std::string &binop) { Settings settings; settings.platform(cppcheck::Platform::Unix64); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); std::string ret; std::function f = [&](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { if (tok->str() != binop) return; auto b = dynamic_cast(&value); if (!b) return; if (!ret.empty()) ret += "\n"; ret += b->getExpr(dataBase); }; std::vector callbacks; callbacks.push_back(f); std::ostringstream dummy; ExprEngine::executeAllFunctions(&tokenizer, &settings, callbacks, dummy); return ret; } std::string getRange(const char code[], const std::string &str, int linenr = 0) { Settings settings; settings.platform(cppcheck::Platform::Unix64); settings.library.smartPointers.insert("std::shared_ptr"); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); std::string ret; std::function f = [&](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) { (void)dataBase; if ((linenr == 0 || linenr == tok->linenr()) && tok->expressionString() == str) { if (!ret.empty()) ret += ","; ret += value.getRange(); } }; std::vector callbacks; callbacks.push_back(f); std::ostringstream dummy; ExprEngine::executeAllFunctions(&tokenizer, &settings, callbacks, dummy); return ret; } std::string trackExecution(const char code[]) { Settings settings; settings.debugVerification = true; settings.platform(cppcheck::Platform::Unix64); settings.library.smartPointers.insert("std::shared_ptr"); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); std::vector dummy; std::ostringstream ret; ExprEngine::executeAllFunctions(&tokenizer, &settings, dummy, ret); return ret.str(); } void expr1() { ASSERT_EQUALS("-32768:32767", getRange("void f(short x) { a = x; }", "x")); } void expr2() { ASSERT_EQUALS("($1)+($1)", getRange("void f(short x) { a = x + x; }", "x+x")); } void expr3() { ASSERT_EQUALS("($1)+($1)", getRange("int f(short x) { int a = x + x; return a; }", "return a")); } void expr4() { ASSERT_EQUALS("($1)-($1)", getRange("int f(short x) { int a = x - x; return a; }", "return a")); } void expr5() { ASSERT_EQUALS("($1)+($2)", getRange("void f(short a, short b, short c, short d) { if (a+b 1000;" "}"; ASSERT_EQUALS("(8)-($1)", getRange(code, "8-x")); ASSERT_EQUALS("(declare-fun $1 () Int)\n" "(assert (and (>= $1 0) (<= $1 255)))\n" "(assert (> (- 8 $1) 1000))\n" "z3::unsat", expr(code, ">")); } void expr7() { const char code[] = "void f(bool a, bool b, int c) {\n" " if (a||b) {}\n" " c > 1000;" "}"; ASSERT_EQUALS("(declare-fun $3 () Int)\n" "(declare-fun $2 () Int)\n" "(declare-fun $1 () Int)\n" "(assert (and (>= $3 (- 2147483648)) (<= $3 2147483647)))\n" "(assert (or (distinct $1 0) (distinct $2 0)))\n" "(assert (> $3 1000))\n" "z3::sat\n" "(declare-fun $3 () Int)\n" "(declare-fun $2 () Int)\n" "(declare-fun $1 () Int)\n" "(assert (and (>= $3 (- 2147483648)) (<= $3 2147483647)))\n" "(assert (= (ite (or (distinct $1 0) (distinct $2 0)) 1 0) 0))\n" "(assert (> $3 1000))\n" "z3::sat", expr(code, ">")); } void exprAssign1() { ASSERT_EQUALS("($1)+(1)", getRange("void f(unsigned char a) { a += 1; }", "a+=1")); } void exprAssign2() { ASSERT_EQUALS("2", getRange("void f(unsigned char x) { x = 258; int a = x }", "a=x")); } void if1() { ASSERT_EQUALS("(declare-fun $1 () Int)\n" "(declare-fun $2 () Int)\n" "(assert (and (>= $1 (- 2147483648)) (<= $1 2147483647)))\n" "(assert (and (>= $2 (- 2147483648)) (<= $2 2147483647)))\n" "(assert (< $1 $2))\n" "(assert (= $1 $2))\n" "z3::unsat", expr("void f(int x, int y) { if (x < y) return x == y; }", "==")); } void ifelse1() { ASSERT_EQUALS("(declare-fun $1 () Int)\n" "(assert (and (>= $1 (- 32768)) (<= $1 32767)))\n" "(assert (<= $1 5))\n" "(assert (= (+ $1 2) 40))\n" "z3::unsat", expr("void f(short x) { if (x > 5) ; else if (x+2==40); }", "==")); } void switch1() { const char code[] = "void f(int x) {\n" " switch (x) {\n" " case 1: x==3; break;\n" " case 2: x>0; break;\n" " };\n" " x<=4;\n" "}"; ASSERT_EQUALS("(declare-fun $1 () Int)\n" "(assert (and (>= $1 (- 2147483648)) (<= $1 2147483647)))\n" "(assert (= $1 1))\n" "(assert (= $1 3))\n" "z3::unsat", expr(code, "==")); } void while1() { const char code[] = "void f(int y) {\n" " int x = 0;\n" " while (x < y)\n" " x = x + 34;\n" " x == 340;\n" "}"; ASSERT_EQUALS("(declare-fun $2 () Int)\n" "(assert (and (>= $2 (- 2147483648)) (<= $2 2147483647)))\n" "(assert (= (+ $2 34) 340))\n" "z3::sat", expr(code, "==")); } void while2() { const char code[] = "void f(int y) {\n" " int x = 0;\n" " while (x < y)\n" " x++;\n" " x == 1;\n" "}"; ASSERT_EQUALS("(declare-fun $2 () Int)\n" "(assert (and (>= $2 (- 2147483648)) (<= $2 2147483647)))\n" "(assert (= $2 1))\n" "z3::sat", expr(code, "==")); } void while3() { const char code[] = "struct AB {int a; int b;};\n" "void f() {\n" " struct AB ab;\n" " while (1)\n" " ab.a = 3;\n" " ab.a == 0;\n" "}"; ASSERT_EQUALS("(assert (= 3 0))\n" "z3::unsat", expr(code, "==")); } void array1() { ASSERT_EQUALS("(assert (= 5 0))\nz3::unsat", expr("int f() { int arr[10]; arr[4] = 5; return arr[4]==0; }", "==")); } void array2() { ASSERT_EQUALS("(declare-fun |$3:4| () Int)\n" "(assert (and (>= |$3:4| 0) (<= |$3:4| 255)))\n" "(assert (= |$3:4| 365))\n" "z3::unsat", expr("void dostuff(unsigned char *); int f() { unsigned char arr[10] = \"\"; dostuff(arr); return arr[4] == 365; }", "==")); } void array3() { const char code[] = "void f(unsigned char x) { int arr[10]; arr[4] = 43; return arr[x] == 12; }"; ASSERT_EQUALS("?,43", getRange(code, "arr[x]")); ASSERT_EQUALS("(declare-fun $1 () Int)\n" "(assert (and (>= $1 0) (<= $1 255)))\n" "(assert (= (ite (= $1 4) 43 0) 12))\n" "z3::unsat", expr(code, "==")); } void array4() { const char code[] = "int buf[10];\n" "void f() { int x = buf[0]; }"; ASSERT_EQUALS("2:16: $2:0=-2147483648:2147483647\n" "2:20: $2=-2147483648:2147483647\n" "2:26: { buf=($1,size=10,[:]=$2) x=$2:0}\n", trackExecution(code)); } void arrayInit1() { ASSERT_EQUALS("0", getRange("inf f() { char arr[10] = \"\"; return arr[4]; }", "arr[4]")); } void arrayInit2() { ASSERT_EQUALS("66", getRange("void f() { char str[] = \"hello\"; str[0] = \'B\'; }", "str[0]=\'B\'")); } void arrayUninit() { ASSERT_EQUALS("?", getRange("int f() { int arr[10]; return arr[4]; }", "arr[4]")); } void floatValue1() { ASSERT_EQUALS(std::to_string(std::numeric_limits::min()) + ":" + std::to_string(std::numeric_limits::max()), getRange("float f; void func() { f=f; }", "f=f")); } void floatValue2() { ASSERT_EQUALS("(29.0)/(2.0)", getRange("void func() { float f = 29.0; f = f / 2.0; }", "f/2.0")); } void functionCall1() { ASSERT_EQUALS("-2147483648:2147483647", getRange("int atoi(const char *p); void f() { int x = atoi(a); x = x; }", "x=x")); } void functionCall2() { const char code[] = "namespace NS {\n" " short getValue();\n" "}" "void f() {\n" " short value = NS::getValue();\n" " value = value;\n" "}"; ASSERT_EQUALS("-32768:32767", getRange(code, "value=value")); } void functionCall3() { ASSERT_EQUALS("-2147483648:2147483647", getRange("int fgets(int, const char *, void *); void f() { int x = -1; fgets(stdin, \"%d\", &x); x=x; }", "x=x")); } void int1() { ASSERT_EQUALS("(declare-fun $1 () Int)\n" "(assert (and (>= $1 (- 2147483648)) (<= $1 2147483647)))\n" "(assert (= (+ 2 $1) 3))\n" "z3::sat", expr("void f(int x) { return 2+x==3; }", "==")); } void pointer1() { const char code[] = "void f(unsigned char *p) { return *p == 7; }"; ASSERT_EQUALS("->$1,null,->?", getRange(code, "p")); ASSERT_EQUALS("(declare-fun $1 () Int)\n" "(assert (and (>= $1 0) (<= $1 255)))\n" "(assert (= $1 7))\n" "z3::sat", expr(code, "==")); } void pointerAlias1() { ASSERT_EQUALS("3", getRange("int f() { int x; int *p = &x; x = 3; return *p; }", "return*p")); } void pointerAlias2() { ASSERT_EQUALS("1", getRange("int f() { int x; int *p = &x; *p = 1; return *p; }", "return*p")); } void pointerAlias3() { ASSERT_EQUALS("7", getRange("int f() {\n" " int x = 18;\n" " int *p = &x;\n" " *p = 7;\n" " return x;\n" "}", "x", 5)); } void pointerAlias4() { ASSERT_EQUALS("71", getRange("int f() { int x[10]; int *p = x+3; *p = 71; return x[3]; }", "x[3]")); } void pointerNull1() { ASSERT_EQUALS("1", getRange("void f(void *p) { p = NULL; p += 1; }", "p+=1")); } void structMember() { ASSERT_EQUALS("(declare-fun $2 () Int)\n" "(declare-fun $3 () Int)\n" "(assert (and (>= $2 0) (<= $2 255)))\n" "(assert (and (>= $3 0) (<= $3 255)))\n" "(assert (= (+ $2 $3) 0))\n" "z3::sat", expr("struct S {\n" " unsigned char a;\n" " unsigned char b;\n" "};\n" "void f(struct S s) { return s.a + s.b == 0; }", "==")); } }; REGISTER_TEST(TestExprEngine) cppcheck-1.90/test/testfilelister.cpp000066400000000000000000000050471357737443600200010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "filelister.h" #include "pathmatch.h" #include "testsuite.h" #include #include #include #include #include #ifndef _WIN32 #include #endif class TestFileLister: public TestFixture { public: TestFileLister() :TestFixture("TestFileLister") { } private: void run() OVERRIDE { // bail out if the tests are not executed from the base folder { std::ifstream fin("test/testfilelister.cpp"); if (!fin.is_open()) return; } TEST_CASE(isDirectory); TEST_CASE(recursiveAddFiles); } void isDirectory() const { ASSERT_EQUALS(false, FileLister::isDirectory("readme.txt")); ASSERT_EQUALS(true, FileLister::isDirectory("lib")); } void recursiveAddFiles() const { // Recursively add add files.. std::map files; std::vector masks; PathMatch matcher(masks); FileLister::recursiveAddFiles(files, ".", matcher); // In case there are leading "./".. for (std::map::iterator i = files.begin(); i != files.end();) { if (i->first.compare(0,2,"./") == 0) { files[i->first.substr(2)] = i->second; files.erase(i++); } else ++i; } // Make sure source files are added.. ASSERT(files.find("cli/main.cpp") != files.end()); ASSERT(files.find("lib/token.cpp") != files.end()); ASSERT(files.find("lib/tokenize.cpp") != files.end()); ASSERT(files.find("test/testfilelister.cpp") != files.end()); // Make sure headers are not added.. ASSERT(files.find("lib/tokenize.h") == files.end()); } }; REGISTER_TEST(TestFileLister) cppcheck-1.90/test/testfunctions.cpp000066400000000000000000001701461357737443600176520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "checkfunctions.h" #include "library.h" #include "settings.h" #include "standards.h" #include "testsuite.h" #include "tokenize.h" class TestFunctions : public TestFixture { public: TestFunctions() : TestFixture("TestFunctions") { } private: Settings settings; void run() OVERRIDE { settings.addEnabled("style"); settings.addEnabled("warning"); settings.addEnabled("portability"); settings.libraries.emplace_back("posix"); settings.standards.c = Standards::C11; settings.standards.cpp = Standards::CPP11; LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "posix.cfg"); // Prohibited functions TEST_CASE(prohibitedFunctions_posix); TEST_CASE(prohibitedFunctions_index); TEST_CASE(prohibitedFunctions_qt_index); // FP when using the Qt function 'index'? TEST_CASE(prohibitedFunctions_rindex); TEST_CASE(prohibitedFunctions_var); // no false positives for variables TEST_CASE(prohibitedFunctions_gets); // dangerous function TEST_CASE(prohibitedFunctions_alloca); TEST_CASE(prohibitedFunctions_declaredFunction); // declared function ticket #3121 TEST_CASE(prohibitedFunctions_std_gets); // test std::gets TEST_CASE(prohibitedFunctions_multiple); // multiple use of obsolete functions TEST_CASE(prohibitedFunctions_c_declaration); // c declared function TEST_CASE(prohibitedFunctions_functionWithBody); // function with body TEST_CASE(prohibitedFunctions_crypt); // Non-reentrant function TEST_CASE(prohibitedFunctions_namespaceHandling); // Invalid function usage TEST_CASE(invalidFunctionUsage1); // TODO TEST_CASE(invalidFunctionUsageStrings); // Math function usage TEST_CASE(mathfunctionCall_fmod); TEST_CASE(mathfunctionCall_sqrt); TEST_CASE(mathfunctionCall_log); TEST_CASE(mathfunctionCall_acos); TEST_CASE(mathfunctionCall_asin); TEST_CASE(mathfunctionCall_pow); TEST_CASE(mathfunctionCall_atan2); TEST_CASE(mathfunctionCall_precision); // Ignored return value TEST_CASE(checkIgnoredReturnValue); // memset.. TEST_CASE(memsetZeroBytes); TEST_CASE(memsetInvalid2ndParam); } void check(const char code[], const char filename[]="test.cpp", const Settings* settings_=nullptr) { // Clear the error buffer.. errout.str(""); if (!settings_) settings_ = &settings; // Tokenize.. Tokenizer tokenizer(settings_, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); CheckFunctions checkFunctions(&tokenizer, settings_, this); checkFunctions.runChecks(&tokenizer, settings_, this); } void prohibitedFunctions_posix() { check("void f()\n" "{\n" " bsd_signal(SIGABRT, SIG_IGN);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolescent function 'bsd_signal' called. It is recommended to use 'sigaction' instead.\n", errout.str()); check("int f()\n" "{\n" " int bsd_signal(0);\n" " return bsd_signal;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " struct hostent *hp;\n" " if(!hp = gethostbyname(\"127.0.0.1\")) {\n" " exit(1);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'gethostbyname' called. It is recommended to use 'getaddrinfo' instead.\n", errout.str()); check("void f()\n" "{\n" " long addr;\n" " addr = inet_addr(\"127.0.0.1\");\n" " if(!hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET)) {\n" " exit(1);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Obsolescent function 'gethostbyaddr' called. It is recommended to use 'getnameinfo' instead.\n", errout.str()); check("void f()\n" "{\n" " usleep( 1000 );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead.\n", errout.str()); } void prohibitedFunctions_index() { check("namespace n1 {\n" " int index(){};\n" "}\n" "int main()\n" "{\n" " n1::index();\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::size_t f()\n" "{\n" " std::size_t index(0);\n" " index++;\n" " return index;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f()\n" "{\n" " return this->index();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " int index( 0 );\n" "}"); ASSERT_EQUALS("", errout.str()); check("const char f()\n" "{\n" " const char var[6] = \"index\";\n" " const char i = index(var, 0);\n" " return i;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead.\n", errout.str()); } void prohibitedFunctions_qt_index() { check("void TDataModel::forceRowRefresh(int row) {\n" " emit dataChanged(index(row, 0), index(row, columnCount() - 1));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead.\n", errout.str()); } void prohibitedFunctions_rindex() { check("void f()\n" "{\n" " int rindex( 0 );\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " const char var[7] = \"rindex\";\n" " print(rindex(var, 0));\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'rindex' called. It is recommended to use 'strrchr' instead.\n", errout.str()); } void prohibitedFunctions_var() { check("class Fred {\n" "public:\n" " Fred() : index(0) { }\n" " int index;\n" "};"); ASSERT_EQUALS("", errout.str()); } void prohibitedFunctions_gets() { check("void f()\n" "{\n" " char *x = gets(a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout.str()); check("void f()\n" "{\n" " foo(x, gets(a));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout.str()); } void prohibitedFunctions_alloca() { check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.cpp"); // #4382 - there are no VLAs in C++ ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'alloca' called.\n", errout.str()); check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (warning) Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n", errout.str()); settings.standards.c = Standards::C89; settings.standards.cpp = Standards::CPP03; check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.cpp"); // #4382 - there are no VLAs in C++ ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.c"); // #7558 - no alternative to alloca in C89 ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " char *x = alloca(10);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); settings.standards.c = Standards::C11; settings.standards.cpp = Standards::CPP11; } // ticket #3121 void prohibitedFunctions_declaredFunction() { check("int ftime ( int a )\n" "{\n" " return a;\n" "}\n" "int main ()\n" "{\n" " int b ; b = ftime ( 1 ) ;\n" " return 0 ;\n" "}"); ASSERT_EQUALS("", errout.str()); } // test std::gets void prohibitedFunctions_std_gets() { check("void f(char * str)\n" "{\n" " char *x = std::gets(str);\n" " char *y = gets(str);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n" "[test.cpp:4]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout.str()); } // multiple use void prohibitedFunctions_multiple() { check("void f(char * str)\n" "{\n" " char *x = std::gets(str);\n" " usleep( 1000 );\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n" "[test.cpp:4]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead.\n", errout.str()); } void prohibitedFunctions_c_declaration() { check("char * gets ( char * c ) ;\n" "int main ()\n" "{\n" " char s [ 10 ] ;\n" " gets ( s ) ;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout.str()); check("int getcontext(ucontext_t *ucp);\n" "int f (ucontext_t *ucp)\n" "{\n" " getcontext ( ucp ) ;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (portability) Obsolescent function 'getcontext' called. Applications are recommended to be rewritten to use POSIX threads.\n", errout.str()); } void prohibitedFunctions_functionWithBody() { check("char * gets ( char * c ) { return c; }\n" "int main ()\n" "{\n" " char s [ 10 ] ;\n" " gets ( s ) ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void prohibitedFunctions_crypt() { check("void f(char *pwd)\n" "{\n" " char *cpwd;" " crypt(pwd, cpwd);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function crypt() is not used.\n" "[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout.str()); check("void f()\n" "{\n" " char *pwd = getpass(\"Password:\");" " char *cpwd;" " crypt(pwd, cpwd);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function crypt() is not used.\n" "[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout.str()); check("int f()\n" "{\n" " int crypt = 0;" " return crypt;\n" "}"); ASSERT_EQUALS("", errout.str()); } void prohibitedFunctions_namespaceHandling() { check("int f()\n" "{\n" " time_t t = 0;" " std::localtime(&t);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'.\n", errout.str()); // Passed as function argument check("int f()\n" "{\n" " printf(\"Magic guess: %d\", getpwent());\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'.\n", errout.str()); // Pass return value check("int f()\n" "{\n" " time_t t = 0;" " struct tm *foo = localtime(&t);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'.\n", errout.str()); // Access via global namespace check("int f()\n" "{\n" " ::getpwent();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function getpwent() is not used.\n" "[test.cpp:3]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'.\n", errout.str()); // Be quiet on function definitions check("int getpwent()\n" "{\n" " return 123;\n" "}"); ASSERT_EQUALS("", errout.str()); // Be quiet on other namespaces check("int f()\n" "{\n" " foobar::getpwent();\n" "}"); ASSERT_EQUALS("", errout.str()); // Be quiet on class member functions check("int f()\n" "{\n" " foobar.getpwent();\n" "}"); ASSERT_EQUALS("", errout.str()); } void invalidFunctionUsage1() { check("int f() { memset(a,b,sizeof(a)!=12); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); check("int f() { memset(a,b,sizeof(a)!=0); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); check("int f() { memset(a,b,!c); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); // Ticket #6990 check("int f(bool c) { memset(a,b,c); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); check("int f() { memset(a,b,true); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); // Ticket #6588 (c mode) check("void record(char* buf, int n) {\n" " memset(buf, 0, n < 255);\n" /* KO */ " memset(buf, 0, n < 255 ? n : 255);\n" /* OK */ "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); // Ticket #6588 (c++ mode) check("void record(char* buf, int n) {\n" " memset(buf, 0, n < 255);\n" /* KO */ " memset(buf, 0, n < 255 ? n : 255);\n" /* OK */ "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout.str()); check("void boolArgZeroIsInvalidButOneIsValid(int param) {\n" " void* buffer = calloc(param > 0, 10);\n" " free(buffer);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid calloc() argument nr 1. The value is 0 or 1 (boolean) but the valid values are '1:'.\n", errout.str()); check("void boolArgZeroIsValidButOneIsInvalid(int param) {\n" " strtol(a, b, param > 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid strtol() argument nr 3. The value is 0 or 1 (boolean) but the valid values are '0,2:36'.\n", errout.str()); check("int f() { strtol(a,b,1); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 1 but the valid values are '0,2:36'.\n", errout.str()); check("int f() { strtol(a,b,10); }"); ASSERT_EQUALS("", errout.str()); } void invalidFunctionUsageStrings() { check("size_t f() { char x = 'x'; return strlen(&x); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("size_t f() { return strlen(&x); }"); ASSERT_EQUALS("", errout.str()); check("size_t f(char x) { return strlen(&x); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("size_t f() { char x = '\\0'; return strlen(&x); }"); ASSERT_EQUALS("", errout.str()); check("size_t f() {\n" " char x;\n" " if (y)\n" " x = '\\0';\n" " else\n" " x = 'a';\n" " return strlen(&x);\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("int f() { char x = '\\0'; return strcmp(\"Hello world\", &x); }"); ASSERT_EQUALS("", errout.str()); check("int f() { char x = 'x'; return strcmp(\"Hello world\", &x); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout.str()); check("size_t f(char x) { char * y = &x; return strlen(y) }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("size_t f(char x) { char * y = &x; char *z = y; return strlen(z) }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("size_t f() { char x = 'x'; char * y = &x; char *z = y; return strlen(z) }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("size_t f() { char x = '\\0'; char * y = &x; char *z = y; return strlen(z) }"); ASSERT_EQUALS("", errout.str()); check("size_t f() { char x[] = \"Hello world\"; return strlen(x) }"); ASSERT_EQUALS("", errout.str()); check("size_t f(char x[]) { return strlen(x) }"); ASSERT_EQUALS("", errout.str()); check("int f(char x, char y) { return strcmp(&x, &y); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 1. A nul-terminated string is required.\n" "[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout.str()); check("size_t f() { char x[] = \"Hello world\"; return strlen(&x[0]) }"); ASSERT_EQUALS("", errout.str()); check("size_t f() { char* x = \"Hello world\"; return strlen(&x[0]) }"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " char x;\n" "};\n" "size_t f() {\n" " S s1 = {0};\n" " S s2;\n;" " s2.x = 'x';\n" " size_t l1 = strlen(&s1.x);\n" " size_t l2 = strlen(&s2.x);\n" " return l1 + l2;\n" "}\n"); TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); check("const char x = 'x'; size_t f() { return strlen(&x); }"); TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str()); check("const char x = 'x'; size_t f() { char y = x; return strlen(&y); }"); ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str()); check("const char x = '\\0'; size_t f() { return strlen(&x); }"); ASSERT_EQUALS("", errout.str()); check("const char x = '\\0'; size_t f() { char y = x; return strlen(&y); }"); ASSERT_EQUALS("", errout.str()); check("size_t f() {\n" " char * a = \"Hello world\";\n" " char ** b = &a;\n" " return strlen(&b[0][0]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("size_t f() {\n" " char ca[] = \"asdf\";\n" " return strlen((char*) &ca);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #5225 check("int main(void)\n" "{\n" " char str[80] = \"hello worl\";\n" " char d = 'd';\n" " strcat(str, &d);\n" " puts(str);\n" " return 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) Invalid strcat() argument nr 2. A nul-terminated string is required.\n", errout.str()); } void mathfunctionCall_sqrt() { // sqrt, sqrtf, sqrtl check("void foo()\n" "{\n" " std::cout << sqrt(-1) << std::endl;\n" " std::cout << sqrtf(-1) << std::endl;\n" " std::cout << sqrtl(-1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid sqrt() argument nr 1. The value is -1 but the valid values are '0.0:'.\n" "[test.cpp:4]: (error) Invalid sqrtf() argument nr 1. The value is -1 but the valid values are '0.0:'.\n" "[test.cpp:5]: (error) Invalid sqrtl() argument nr 1. The value is -1 but the valid values are '0.0:'.\n", errout.str()); // implementation-defined behaviour for "finite values of x<0" only: check("void foo()\n" "{\n" " std::cout << sqrt(-0.) << std::endl;\n" " std::cout << sqrtf(-0.) << std::endl;\n" " std::cout << sqrtl(-0.) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << sqrt(1) << std::endl;\n" " std::cout << sqrtf(1) << std::endl;\n" " std::cout << sqrtl(1) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mathfunctionCall_log() { // log,log10,logf,logl,log10f,log10l,log2,log2f,log2l,log1p,log1pf,log1pl check("void foo()\n" "{\n" " std::cout << log(-2) << std::endl;\n" " std::cout << logf(-2) << std::endl;\n" " std::cout << logl(-2) << std::endl;\n" " std::cout << log10(-2) << std::endl;\n" " std::cout << log10f(-2) << std::endl;\n" " std::cout << log10l(-2) << std::endl;\n" " std::cout << log2(-2) << std::endl;\n" " std::cout << log2f(-2) << std::endl;\n" " std::cout << log2l(-2) << std::endl;\n" " std::cout << log1p(-3) << std::endl;\n" " std::cout << log1pf(-3) << std::endl;\n" " std::cout << log1pl(-3) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -2 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -2 to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -2 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value -2 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value -2 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value -2 to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value -2 to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value -2 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value -2 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -3 to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -3 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -3 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(-1) << std::endl;\n" " std::cout << logf(-1) << std::endl;\n" " std::cout << logl(-1) << std::endl;\n" " std::cout << log10(-1) << std::endl;\n" " std::cout << log10f(-1) << std::endl;\n" " std::cout << log10l(-1) << std::endl;\n" " std::cout << log2(-1) << std::endl;\n" " std::cout << log2f(-1) << std::endl;\n" " std::cout << log2l(-1) << std::endl;\n" " std::cout << log1p(-2) << std::endl;\n" " std::cout << log1pf(-2) << std::endl;\n" " std::cout << log1pl(-2) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -1 to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -1 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value -1 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value -1 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value -1 to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value -1 to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value -1 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value -1 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -2 to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -2 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -2 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(-1.0) << std::endl;\n" " std::cout << logf(-1.0) << std::endl;\n" " std::cout << logl(-1.0) << std::endl;\n" " std::cout << log10(-1.0) << std::endl;\n" " std::cout << log10f(-1.0) << std::endl;\n" " std::cout << log10l(-1.0) << std::endl;\n" " std::cout << log2(-1.0) << std::endl;\n" " std::cout << log2f(-1.0) << std::endl;\n" " std::cout << log2l(-1.0) << std::endl;\n" " std::cout << log1p(-2.0) << std::endl;\n" " std::cout << log1pf(-2.0) << std::endl;\n" " std::cout << log1pl(-2.0) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -1.0 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -1.0 to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -1.0 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value -1.0 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value -1.0 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value -1.0 to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value -1.0 to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value -1.0 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value -1.0 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -2.0 to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -2.0 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -2.0 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(-0.1) << std::endl;\n" " std::cout << logf(-0.1) << std::endl;\n" " std::cout << logl(-0.1) << std::endl;\n" " std::cout << log10(-0.1) << std::endl;\n" " std::cout << log10f(-0.1) << std::endl;\n" " std::cout << log10l(-0.1) << std::endl;\n" " std::cout << log2(-0.1) << std::endl;\n" " std::cout << log2f(-0.1) << std::endl;\n" " std::cout << log2l(-0.1) << std::endl;\n" " std::cout << log1p(-1.1) << std::endl;\n" " std::cout << log1pf(-1.1) << std::endl;\n" " std::cout << log1pl(-1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value -0.1 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value -0.1 to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value -0.1 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value -0.1 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value -0.1 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value -0.1 to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value -0.1 to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value -0.1 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value -0.1 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -1.1 to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -1.1 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -1.1 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(0) << std::endl;\n" " std::cout << logf(0.) << std::endl;\n" " std::cout << logl(0.0) << std::endl;\n" " std::cout << log10(0.0) << std::endl;\n" " std::cout << log10f(0) << std::endl;\n" " std::cout << log10l(0.) << std::endl;\n" " std::cout << log2(0.) << std::endl;\n" " std::cout << log2f(0.0) << std::endl;\n" " std::cout << log2l(0) << std::endl;\n" " std::cout << log1p(-1.) << std::endl;\n" " std::cout << log1pf(-1.0) << std::endl;\n" " std::cout << log1pl(-1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing value 0 to log() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing value 0. to logf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing value 0.0 to logl() leads to implementation-defined result.\n" "[test.cpp:6]: (warning) Passing value 0.0 to log10() leads to implementation-defined result.\n" "[test.cpp:7]: (warning) Passing value 0 to log10f() leads to implementation-defined result.\n" "[test.cpp:8]: (warning) Passing value 0. to log10l() leads to implementation-defined result.\n" "[test.cpp:9]: (warning) Passing value 0. to log2() leads to implementation-defined result.\n" "[test.cpp:10]: (warning) Passing value 0.0 to log2f() leads to implementation-defined result.\n" "[test.cpp:11]: (warning) Passing value 0 to log2l() leads to implementation-defined result.\n" "[test.cpp:12]: (warning) Passing value -1. to log1p() leads to implementation-defined result.\n" "[test.cpp:13]: (warning) Passing value -1.0 to log1pf() leads to implementation-defined result.\n" "[test.cpp:14]: (warning) Passing value -1 to log1pl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << log(1E-3) << std::endl;\n" " std::cout << logf(1E-3) << std::endl;\n" " std::cout << logl(1E-3) << std::endl;\n" " std::cout << log10(1E-3) << std::endl;\n" " std::cout << log10f(1E-3) << std::endl;\n" " std::cout << log10l(1E-3) << std::endl;\n" " std::cout << log2(1E-3) << std::endl;\n" " std::cout << log2f(1E-3) << std::endl;\n" " std::cout << log2l(1E-3) << std::endl;\n" " std::cout << log1p(-1+1E-3) << std::endl;\n" " std::cout << log1pf(-1+1E-3) << std::endl;\n" " std::cout << log1pl(-1+1E-3) << std::endl;\n" " std::cout << log(1.0E-3) << std::endl;\n" " std::cout << logf(1.0E-3) << std::endl;\n" " std::cout << logl(1.0E-3) << std::endl;\n" " std::cout << log10(1.0E-3) << std::endl;\n" " std::cout << log10f(1.0E-3) << std::endl;\n" " std::cout << log10l(1.0E-3) << std::endl;\n" " std::cout << log2(1.0E-3) << std::endl;\n" " std::cout << log2f(1.0E-3) << std::endl;\n" " std::cout << log2l(1.0E-3) << std::endl;\n" " std::cout << log1p(-1+1.0E-3) << std::endl;\n" " std::cout << log1pf(-1+1.0E-3) << std::endl;\n" " std::cout << log1pl(-1+1.0E-3) << std::endl;\n" " std::cout << log(1.0E+3) << std::endl;\n" " std::cout << logf(1.0E+3) << std::endl;\n" " std::cout << logl(1.0E+3) << std::endl;\n" " std::cout << log10(1.0E+3) << std::endl;\n" " std::cout << log10f(1.0E+3) << std::endl;\n" " std::cout << log10l(1.0E+3) << std::endl;\n" " std::cout << log2(1.0E+3) << std::endl;\n" " std::cout << log2f(1.0E+3) << std::endl;\n" " std::cout << log2l(1.0E+3) << std::endl;\n" " std::cout << log1p(1.0E+3) << std::endl;\n" " std::cout << log1pf(1.0E+3) << std::endl;\n" " std::cout << log1pl(1.0E+3) << std::endl;\n" " std::cout << log(2.0) << std::endl;\n" " std::cout << logf(2.0) << std::endl;\n" " std::cout << logf(2.0f) << std::endl;\n" " std::cout << log10(2.0) << std::endl;\n" " std::cout << log10f(2.0) << std::endl;\n" " std::cout << log10f(2.0f) << std::endl;\n" " std::cout << log2(2.0) << std::endl;\n" " std::cout << log2f(2.0) << std::endl;\n" " std::cout << log2f(2.0f) << std::endl;\n" " std::cout << log1p(2.0) << std::endl;\n" " std::cout << log1pf(2.0) << std::endl;\n" " std::cout << log1pf(2.0f) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::string *log(0);\n" "}"); ASSERT_EQUALS("", errout.str()); // #3473 - no warning if "log" is a variable check("Fred::Fred() : log(0) { }"); ASSERT_EQUALS("", errout.str()); // #5748 check("void f() { foo.log(0); }"); ASSERT_EQUALS("", errout.str()); } void mathfunctionCall_acos() { // acos, acosf, acosl check("void foo()\n" "{\n" " return acos(-1) \n" " + acos(0.1) \n" " + acos(0.0001) \n" " + acos(0.01) \n" " + acos(1.0E-1) \n" " + acos(-1.0E-1) \n" " + acos(+1.0E-1) \n" " + acos(0.1E-1) \n" " + acos(+0.1E-1) \n" " + acos(-0.1E-1) \n" " + acosf(-1) \n" " + acosf(0.1) \n" " + acosf(0.0001) \n" " + acosf(0.01) \n" " + acosf(1.0E-1) \n" " + acosf(-1.0E-1) \n" " + acosf(+1.0E-1) \n" " + acosf(0.1E-1) \n" " + acosf(+0.1E-1) \n" " + acosf(-0.1E-1) \n" " + acosl(-1) \n" " + acosl(0.1) \n" " + acosl(0.0001) \n" " + acosl(0.01) \n" " + acosl(1.0E-1) \n" " + acosl(-1.0E-1) \n" " + acosl(+1.0E-1) \n" " + acosl(0.1E-1) \n" " + acosl(+0.1E-1) \n" " + acosl(-0.1E-1); \n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << acos(1.1) << std::endl;\n" " std::cout << acosf(1.1) << std::endl;\n" " std::cout << acosl(1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid acos() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:4]: (error) Invalid acosf() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:5]: (error) Invalid acosl() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << acos(-1.1) << std::endl;\n" " std::cout << acosf(-1.1) << std::endl;\n" " std::cout << acosl(-1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid acos() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:4]: (error) Invalid acosf() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:5]: (error) Invalid acosl() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n", errout.str()); } void mathfunctionCall_asin() { // asin, asinf, asinl check("void foo()\n" "{\n" " return asin(1) \n" " + asin(-1) \n" " + asin(0.1) \n" " + asin(0.0001) \n" " + asin(0.01) \n" " + asin(1.0E-1) \n" " + asin(-1.0E-1) \n" " + asin(+1.0E-1) \n" " + asin(0.1E-1) \n" " + asin(+0.1E-1) \n" " + asin(-0.1E-1) \n" " + asinf(1) \n" " + asinf(-1) \n" " + asinf(0.1) \n" " + asinf(0.0001) \n" " + asinf(0.01) \n" " + asinf(1.0E-1) \n" " + asinf(-1.0E-1) \n" " + asinf(+1.0E-1) \n" " + asinf(0.1E-1) \n" " + asinf(+0.1E-1) \n" " + asinf(-0.1E-1) \n" " + asinl(1) \n" " + asinl(-1) \n" " + asinl(0.1) \n" " + asinl(0.0001) \n" " + asinl(0.01) \n" " + asinl(1.0E-1) \n" " + asinl(-1.0E-1) \n" " + asinl(+1.0E-1) \n" " + asinl(0.1E-1) \n" " + asinl(+0.1E-1) \n" " + asinl(-0.1E-1); \n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << asin(1.1) << std::endl;\n" " std::cout << asinf(1.1) << std::endl;\n" " std::cout << asinl(1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid asin() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:4]: (error) Invalid asinf() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:5]: (error) Invalid asinl() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << asin(-1.1) << std::endl;\n" " std::cout << asinf(-1.1) << std::endl;\n" " std::cout << asinl(-1.1) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Invalid asin() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:4]: (error) Invalid asinf() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" "[test.cpp:5]: (error) Invalid asinl() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n", errout.str()); } void mathfunctionCall_pow() { // pow, powf, powl check("void foo()\n" "{\n" " std::cout << pow(0,-10) << std::endl;\n" " std::cout << powf(0,-10) << std::endl;\n" " std::cout << powl(0,-10) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and -10 to pow() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing values 0 and -10 to powf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing values 0 and -10 to powl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << pow(0,10) << std::endl;\n" " std::cout << powf(0,10) << std::endl;\n" " std::cout << powl(0,10) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mathfunctionCall_atan2() { // atan2 check("void foo()\n" "{\n" " std::cout << atan2(1,1) ;\n" " std::cout << atan2(-1,-1) ;\n" " std::cout << atan2(0.1,1) ;\n" " std::cout << atan2(0.0001,100) ;\n" " std::cout << atan2(0.0,1e-1) ;\n" " std::cout << atan2(1.0E-1,-3) ;\n" " std::cout << atan2(-1.0E-1,+2) ;\n" " std::cout << atan2(+1.0E-1,0) ;\n" " std::cout << atan2(0.1E-1,3) ;\n" " std::cout << atan2(+0.1E-1,1) ;\n" " std::cout << atan2(-0.1E-1,8) ;\n" " std::cout << atan2f(1,1) ;\n" " std::cout << atan2f(-1,-1) ;\n" " std::cout << atan2f(0.1,1) ;\n" " std::cout << atan2f(0.0001,100) ;\n" " std::cout << atan2f(0.0,1e-1) ;\n" " std::cout << atan2f(1.0E-1,-3) ;\n" " std::cout << atan2f(-1.0E-1,+2) ;\n" " std::cout << atan2f(+1.0E-1,0) ;\n" " std::cout << atan2f(0.1E-1,3) ;\n" " std::cout << atan2f(+0.1E-1,1) ;\n" " std::cout << atan2f(-0.1E-1,8) ;\n" " std::cout << atan2l(1,1) ;\n" " std::cout << atan2l(-1,-1) ;\n" " std::cout << atan2l(0.1,1) ;\n" " std::cout << atan2l(0.0001,100) ;\n" " std::cout << atan2l(0.0,1e-1) ;\n" " std::cout << atan2l(1.0E-1,-3) ;\n" " std::cout << atan2l(-1.0E-1,+2) ;\n" " std::cout << atan2l(+1.0E-1,0) ;\n" " std::cout << atan2l(0.1E-1,3) ;\n" " std::cout << atan2l(+0.1E-1,1) ;\n" " std::cout << atan2l(-0.1E-1,8) ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << atan2(0,0) << std::endl;\n" " std::cout << atan2f(0,0) << std::endl;\n" " std::cout << atan2l(0,0) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and 0 to atan2() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing values 0 and 0 to atan2f() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing values 0 and 0 to atan2l() leads to implementation-defined result.\n", errout.str()); } void mathfunctionCall_fmod() { // fmod, fmodl, fmodf check("void foo()\n" "{\n" " std::cout << fmod(1.0,0) << std::endl;\n" " std::cout << fmodf(1.0,0) << std::endl;\n" " std::cout << fmodl(1.0,0) << std::endl;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 1.0 and 0 to fmod() leads to implementation-defined result.\n" "[test.cpp:4]: (warning) Passing values 1.0 and 0 to fmodf() leads to implementation-defined result.\n" "[test.cpp:5]: (warning) Passing values 1.0 and 0 to fmodl() leads to implementation-defined result.\n", errout.str()); check("void foo()\n" "{\n" " std::cout << fmod(1.0,1) << std::endl;\n" " std::cout << fmodf(1.0,1) << std::endl;\n" " std::cout << fmodl(1.0,1) << std::endl;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mathfunctionCall_precision() { check("void foo() {\n" " print(exp(x) - 1);\n" " print(log(1 + x));\n" " print(1 - erf(x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str()); check("void foo() {\n" " print(exp(x) - 1.0);\n" " print(log(1.0 + x));\n" " print(1.0 - erf(x));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str()); check("void foo() {\n" " print(exp(3 + x*f(a)) - 1);\n" " print(log(x*4 + 1));\n" " print(1 - erf(34*x + f(x) - c));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout.str()); check("void foo() {\n" " print(2*exp(x) - 1);\n" " print(1 - erf(x)/2.0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkIgnoredReturnValue() { Settings settings2; settings2.addEnabled("warning"); const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings2.library.load(doc); check("void foo() {\n" " mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout.str()); check("void foo() {\n" " foo::mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function foo::mystrcmp() is not used.\n", errout.str()); check("void f() {\n" " foo x;\n" " x.mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function x.mystrcmp() is not used.\n", errout.str()); check("bool mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition, but it returns a value. Assume it is the one specified in the library. "void foo() {\n" " mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function mystrcmp() is not used.\n", errout.str()); check("void mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition which returns void! "void foo() {\n" " mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " class mystrcmp { mystrcmp() {} };\n" // strcmp is a constructor definition here "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " return mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " return foo::mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if(mystrcmp(a, b));\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " bool b = mystrcmp(a, b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #6194 check("void foo() {\n" " MyStrCmp mystrcmp(x, y);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #6197 check("void foo() {\n" " abc::def.mystrcmp(a,b);\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #6233 check("int main() {\n" " auto lambda = [](double value) {\n" " double rounded = floor(value + 0.5);\n" " printf(\"Rounded value = %f\\n\", rounded);\n" " };\n" " lambda(13.3);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6669 check("void foo(size_t size) {\n" " void * res{malloc(size)};\n" "}"); ASSERT_EQUALS("", errout.str()); // #7447 check("void foo() {\n" " int x{mystrcmp(a,b)};\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #7905 check("void foo() {\n" " int x({mystrcmp(a,b)});\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); // #7979 - code is not well configured check("void foo() {\n" " DEBUG(x(); mystrcmp(a,b););\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" // don't crash " DEBUG(123)(mystrcmp(a,b))(fd);\n" "}", "test.c", &settings2); check("struct teststruct {\n" " int testfunc1() __attribute__ ((warn_unused_result)) { return 1; }\n" " [[nodiscard]] int testfunc2() { return 1; }\n" " void foo() { testfunc1(); testfunc2(); }\n" "};\n" "int main() {\n" " teststruct TestStruct1;\n" " TestStruct1.testfunc1();\n" " TestStruct1.testfunc2();\n" "}", "test.cpp", &settings2); ASSERT_EQUALS("[test.cpp:4]: (warning) Return value of function testfunc1() is not used.\n" "[test.cpp:4]: (warning) Return value of function testfunc2() is not used.\n" "[test.cpp:8]: (warning) Return value of function TestStruct1.testfunc1() is not used.\n" "[test.cpp:9]: (warning) Return value of function TestStruct1.testfunc2() is not used.\n", errout.str()); // #9006 check("template uint8_t b(std::tuple d) {\n" " std::tuple c{std::move(d)};\n" " return std::get<0>(c);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int x; };\n" "template \n" "A f(int x, Ts... xs) {\n" " return {std::move(x), static_cast(xs)...};\n" "}\n" "A g() { return f(1); }\n"); ASSERT_EQUALS("", errout.str()); } void memsetZeroBytes() { check("void f() {\n" " memset(p, 10, 0x0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout.str()); check("void f() {\n" " memset(p, sizeof(p), 0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout.str()); check("void f() {\n" " memset(p, sizeof(p), i);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #6269 false positives in case of overloaded standard library functions check("class c {\n" " void memset( int i );\n" " void f( void ) {\n" " memset( 0 );\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); // #7285 check("void f() {\n" " memset(&tm, sizeof(tm), 0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout.str()); } void memsetInvalid2ndParam() { check("void f() {\n" " int* is = new int[10];\n" " memset(is, 1.0f, 40);\n" " int* is2 = new int[10];\n" " memset(is2, 0.1f, 40);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (portability) The 2nd memset() argument '1.0f' is a float, its representation is implementation defined.\n" "[test.cpp:5]: (portability) The 2nd memset() argument '0.1f' is a float, its representation is implementation defined.\n", errout.str()); check("void f() {\n" " int* is = new int[10];\n" " float g = computeG();\n" " memset(is, g, 40);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (portability) The 2nd memset() argument 'g' is a float, its representation is implementation defined.\n", errout.str()); check("void f() {\n" " int* is = new int[10];\n" " memset(is, 0.0f, 40);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // FP " float x = 2.3f;\n" " memset(a, (x?64:0), 40);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " short ss[] = {1, 2};\n" " memset(ss, 256, 4);\n" " short ss2[2];\n" " memset(ss2, -129, 4);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) The 2nd memset() argument '256' doesn't fit into an 'unsigned char'.\n" "[test.cpp:5]: (warning) The 2nd memset() argument '-129' doesn't fit into an 'unsigned char'.\n", errout.str()); check("void f() {\n" " int is[10];\n" " memset(is, 0xEE, 40);\n" " unsigned char* cs = malloc(256);\n" " memset(cs, -1, 256);\n" " short* ss[30];\n" " memset(ss, -128, 60);\n" " char cs2[30];\n" " memset(cs2, 255, 30);\n" " char cs3[30];\n" " memset(cs3, 0, 30);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int is[10];\n" " const int i = g();\n" " memset(is, 1.0f + i, 40);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (portability) The 2nd memset() argument '1.0f+i' is a float, its representation is implementation defined.\n", errout.str()); } }; REGISTER_TEST(TestFunctions) cppcheck-1.90/test/testgarbage.cpp000066400000000000000000002126751357737443600172360ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "check.h" #include "errorlogger.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include class TestGarbage : public TestFixture { public: TestGarbage() : TestFixture("TestGarbage") { } private: Settings settings; void run() OVERRIDE { settings.debugwarnings = true; settings.addEnabled("style"); settings.addEnabled("warning"); settings.addEnabled("portability"); settings.addEnabled("performance"); settings.addEnabled("information"); settings.inconclusive = true; settings.experimental = true; // don't freak out when the syntax is wrong TEST_CASE(final_class_x); TEST_CASE(wrong_syntax1); TEST_CASE(wrong_syntax2); TEST_CASE(wrong_syntax3); // #3544 TEST_CASE(wrong_syntax4); // #3618 TEST_CASE(wrong_syntax_if_macro); // #2518 - if MACRO() TEST_CASE(wrong_syntax_class_x_y); // #3585 - class x y { }; TEST_CASE(syntax_case_default); TEST_CASE(garbageCode1); TEST_CASE(garbageCode2); // #4300 TEST_CASE(garbageCode3); // #4869 TEST_CASE(garbageCode4); // #4887 TEST_CASE(garbageCode5); // #5168 TEST_CASE(garbageCode6); // #5214 TEST_CASE(garbageCode7); TEST_CASE(garbageCode8); // #5511 TEST_CASE(garbageCode9); // #5604 TEST_CASE(garbageCode10); // #6127 TEST_CASE(garbageCode12); TEST_CASE(garbageCode13); // #2607 TEST_CASE(garbageCode15); // #5203 TEST_CASE(garbageCode16); TEST_CASE(garbageCode17); TEST_CASE(garbageCode18); TEST_CASE(garbageCode20); TEST_CASE(garbageCode21); TEST_CASE(garbageCode22); TEST_CASE(garbageCode23); TEST_CASE(garbageCode24); // #6361 TEST_CASE(garbageCode25); TEST_CASE(garbageCode26); TEST_CASE(garbageCode27); TEST_CASE(garbageCode28); TEST_CASE(garbageCode30); // #5867 TEST_CASE(garbageCode31); // #6539 TEST_CASE(garbageCode33); // #6613 TEST_CASE(garbageCode34); // #6626 TEST_CASE(garbageCode35); // #2604 TEST_CASE(garbageCode36); // #6334 TEST_CASE(garbageCode37); // #5166 TEST_CASE(garbageCode38); // #6666 TEST_CASE(garbageCode40); // #6620 TEST_CASE(garbageCode41); // #6685 TEST_CASE(garbageCode42); // #5760 TEST_CASE(garbageCode43); // #6703 TEST_CASE(garbageCode44); // #6704 TEST_CASE(garbageCode45); // #6608 TEST_CASE(garbageCode46); // #6705 TEST_CASE(garbageCode47); // #6706 TEST_CASE(garbageCode48); // #6712 TEST_CASE(garbageCode49); // #6715 TEST_CASE(garbageCode51); // #6719 TEST_CASE(garbageCode53); // #6721 TEST_CASE(garbageCode54); // #6722 TEST_CASE(garbageCode55); // #6724 TEST_CASE(garbageCode56); // #6713 TEST_CASE(garbageCode57); // #6733 TEST_CASE(garbageCode58); // #6732 TEST_CASE(garbageCode59); // #6735 TEST_CASE(garbageCode60); // #6736 TEST_CASE(garbageCode61); TEST_CASE(garbageCode63); TEST_CASE(garbageCode64); TEST_CASE(garbageCode65); TEST_CASE(garbageCode66); TEST_CASE(garbageCode68); TEST_CASE(garbageCode69); TEST_CASE(garbageCode70); TEST_CASE(garbageCode71); TEST_CASE(garbageCode72); TEST_CASE(garbageCode73); TEST_CASE(garbageCode74); TEST_CASE(garbageCode76); TEST_CASE(garbageCode77); TEST_CASE(garbageCode78); TEST_CASE(garbageCode79); TEST_CASE(garbageCode80); TEST_CASE(garbageCode81); TEST_CASE(garbageCode82); TEST_CASE(garbageCode83); TEST_CASE(garbageCode84); TEST_CASE(garbageCode85); TEST_CASE(garbageCode86); TEST_CASE(garbageCode87); TEST_CASE(garbageCode88); TEST_CASE(garbageCode90); TEST_CASE(garbageCode91); TEST_CASE(garbageCode92); TEST_CASE(garbageCode94); TEST_CASE(garbageCode95); TEST_CASE(garbageCode96); TEST_CASE(garbageCode97); TEST_CASE(garbageCode98); TEST_CASE(garbageCode99); TEST_CASE(garbageCode100); TEST_CASE(garbageCode101); // #6835 TEST_CASE(garbageCode102); // #6846 TEST_CASE(garbageCode103); // #6824 TEST_CASE(garbageCode104); // #6847 TEST_CASE(garbageCode105); // #6859 TEST_CASE(garbageCode106); TEST_CASE(garbageCode107); TEST_CASE(garbageCode108); TEST_CASE(garbageCode109); TEST_CASE(garbageCode110); TEST_CASE(garbageCode111); TEST_CASE(garbageCode112); TEST_CASE(garbageCode114); // #2118 TEST_CASE(garbageCode115); // #5506 TEST_CASE(garbageCode116); // #5356 TEST_CASE(garbageCode117); // #6121 TEST_CASE(garbageCode118); // #5600 TEST_CASE(garbageCode119); // #5598 TEST_CASE(garbageCode120); // #4927 TEST_CASE(garbageCode121); // #2585 TEST_CASE(garbageCode122); // #6303 TEST_CASE(garbageCode123); TEST_CASE(garbageCode125); // 6782, 6834 TEST_CASE(garbageCode126); // #6997 TEST_CASE(garbageCode127); // #6667 TEST_CASE(garbageCode128); // #7018 TEST_CASE(garbageCode129); // #7020 TEST_CASE(garbageCode130); // #7021 TEST_CASE(garbageCode131); // #7023 TEST_CASE(garbageCode132); // #7022 TEST_CASE(garbageCode133); TEST_CASE(garbageCode134); TEST_CASE(garbageCode135); // #4994 TEST_CASE(garbageCode136); // #7033 TEST_CASE(garbageCode137); // #7034 TEST_CASE(garbageCode138); // #6660 TEST_CASE(garbageCode139); // #6659 TEST_CASE(garbageCode140); // #7035 TEST_CASE(garbageCode141); // #7043 TEST_CASE(garbageCode142); // #7050 TEST_CASE(garbageCode143); // #6922 TEST_CASE(garbageCode144); // #6865 TEST_CASE(garbageCode146); // #7081 TEST_CASE(garbageCode147); // #7082 TEST_CASE(garbageCode148); // #7090 TEST_CASE(garbageCode149); // #7085 TEST_CASE(garbageCode150); // #7089 TEST_CASE(garbageCode151); // #4911 TEST_CASE(garbageCode152); // travis after 9c7271a5 TEST_CASE(garbageCode153); TEST_CASE(garbageCode154); // #7112 TEST_CASE(garbageCode156); // #7120 TEST_CASE(garbageCode157); // #7131 TEST_CASE(garbageCode158); // #3238 TEST_CASE(garbageCode159); // #7119 TEST_CASE(garbageCode160); // #7190 TEST_CASE(garbageCode161); // #7200 TEST_CASE(garbageCode162); // #7208 TEST_CASE(garbageCode163); // #7228 TEST_CASE(garbageCode164); // #7234 TEST_CASE(garbageCode165); // #7235 TEST_CASE(garbageCode167); // #7237 TEST_CASE(garbageCode168); // #7246 TEST_CASE(garbageCode169); // #6731 TEST_CASE(garbageCode170); TEST_CASE(garbageCode171); TEST_CASE(garbageCode172); TEST_CASE(garbageCode173); // #6781 TEST_CASE(garbageCode174); // #7356 TEST_CASE(garbageCode175); TEST_CASE(garbageCode176); // #7527 TEST_CASE(garbageCode181); TEST_CASE(garbageCode182); // #4195 TEST_CASE(garbageCode183); // #7505 TEST_CASE(garbageCode184); // #7699 TEST_CASE(garbageCode185); // #6011 TEST_CASE(garbageCode186); // #8151 TEST_CASE(garbageCode187); TEST_CASE(garbageCode188); TEST_CASE(garbageCode189); // #8317 TEST_CASE(garbageCode190); // #8307 TEST_CASE(garbageCode191); // #8333 TEST_CASE(garbageCode192); // #8386 (segmentation fault) TEST_CASE(garbageCode193); // #8740 TEST_CASE(garbageCode194); // #8384 TEST_CASE(garbageCode195); // #8709 TEST_CASE(garbageCode196); // #8265 TEST_CASE(garbageCode197); // #8385 TEST_CASE(garbageCode198); // #8383 TEST_CASE(garbageCode199); // #8752 TEST_CASE(garbageCode200); // #8757 TEST_CASE(garbageCode201); // #8873 TEST_CASE(garbageCode202); // #8907 TEST_CASE(garbageCode203); // #8972 TEST_CASE(garbageCode204); TEST_CASE(garbageCode205); TEST_CASE(garbageCode206); TEST_CASE(garbageCode207); // #8750 TEST_CASE(garbageCode208); // #8753 TEST_CASE(garbageCode209); // #8756 TEST_CASE(garbageCode210); // #8762 TEST_CASE(garbageCode211); // #8764 TEST_CASE(garbageCode212); // #8765 TEST_CASE(garbageCodeFuzzerClientMode1); // test cases created with the fuzzer client, mode 1 TEST_CASE(garbageValueFlow); TEST_CASE(garbageSymbolDatabase); TEST_CASE(garbageAST); TEST_CASE(templateSimplifierCrashes); TEST_CASE(syntaxErrorFirstToken); // Make sure syntax errors are detected and reported TEST_CASE(syntaxErrorLastToken); // Make sure syntax errors are detected and reported TEST_CASE(syntaxErrorCase); TEST_CASE(syntaxErrorFuzzerCliType1); TEST_CASE(cliCode); TEST_CASE(enumTrailingComma); TEST_CASE(nonGarbageCode1); // #8346 } std::string checkCode(const std::string &code, bool cpp = true) { // double the tests - run each example as C as well as C++ const char* const filename = cpp ? "test.cpp" : "test.c"; const char* const alternatefilename = cpp ? "test.c" : "test.cpp"; // run alternate check first. It should only ensure stability - so we catch exceptions here. try { checkCodeInternal(code, alternatefilename); } catch (const InternalError&) { } return checkCodeInternal(code, filename); } std::string checkCodeInternal(const std::string &code, const char* filename) { errout.str(""); // tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename); // call all "runChecks" in all registered Check classes for (std::list::const_iterator it = Check::instances().begin(); it != Check::instances().end(); ++it) { (*it)->runChecks(&tokenizer, &settings, this); } tokenizer.simplifyTokenList2(); return tokenizer.tokens()->stringifyList(false, false, false, true, false, nullptr, nullptr); } std::string getSyntaxError(const char code[]) { Tokenizer tokenizer(&settings, this); std::istringstream istr(code); try { tokenizer.tokenize(istr, "test.cpp"); } catch (InternalError& e) { if (e.id != "syntaxError") return ""; return "[test.cpp:" + MathLib::toString(e.token->linenr()) + "] " + e.errorMessage; } return ""; } void final_class_x() { const char code[] = "class __declspec(dllexport) x final { };"; { errout.str(""); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("", errout.str()); } } void wrong_syntax1() { { const char code[] ="TR(kvmpio, PROTO(int rw), ARGS(rw), TP_(aa->rw;))"; ASSERT_THROW(checkCode(code), InternalError); ASSERT_EQUALS("", errout.str()); } { const char code[] ="struct A { template struct { }; };"; ASSERT_THROW(checkCode(code), InternalError); } { const char code[] ="enum ABC { A,B, typedef enum { C } };"; ASSERT_THROW(checkCode(code), InternalError); } } void wrong_syntax2() { // #3504 const char code[] = "void f() {\n" " X x;\n" " Y y;\n" "}\n" "\n" "void G( template class (j) ) {}"; // don't segfault.. ASSERT_THROW(checkCode(code), InternalError); } void wrong_syntax3() { // #3544 const char code[] = "X #define\n" "{\n" " (\n" " for( #endif typedef typedef cb[N] )\n" " ca[N]; = cb[i]\n" " )\n" "}"; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); try { tokenizer.tokenize(istr, "test.cpp"); assertThrowFail(__FILE__, __LINE__); } catch (InternalError& e) { ASSERT_EQUALS("syntax error", e.errorMessage); ASSERT_EQUALS("syntaxError", e.id); ASSERT_EQUALS(4, e.token->linenr()); } } void wrong_syntax4() { // #3618 const char code[] = "typedef void (x) (int); return x&"; ASSERT_THROW(checkCode(code), InternalError); } void wrong_syntax_if_macro() { // #2518 #4171 ASSERT_THROW(checkCode("void f() { if MACRO(); }"), InternalError); // #4668 - note there is no semicolon after MACRO() ASSERT_THROW(checkCode("void f() { if (x) MACRO() {} }"), InternalError); // #4810 - note there is no semicolon after MACRO() ASSERT_THROW(checkCode("void f() { if (x) MACRO() else ; }"), InternalError); } void wrong_syntax_class_x_y() { // #3585 const char code[] = "class x y { };"; { errout.str(""); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.c"); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("", errout.str()); } { errout.str(""); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); ASSERT_EQUALS("[test.cpp:1]: (information) The code 'class x y {' is not handled. You can use -I or --include to add handling of this code.\n", errout.str()); } } void syntax_case_default() { ASSERT_THROW(checkCode("void f() {switch (n) { case: z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case;: z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case {}: z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case 0?{1}:{2} : z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case 0?1;:{2} : z(); break;}}"), InternalError); ASSERT_THROW(checkCode("void f() {switch (n) { case 0?(1?{3:4}):2 : z(); break;}}"), InternalError); //ticket #4234 ASSERT_THROW(checkCode("( ) { switch break ; { switch ( x ) { case } y break ; : } }"), InternalError); //ticket #4267 ASSERT_THROW(checkCode("f ( ) { switch break; { switch ( x ) { case } case break; -6: ( ) ; } }"), InternalError); // Missing semicolon ASSERT_THROW(checkCode("void foo () { switch(0) case 0 : default : }"), InternalError); } void garbageCode1() { checkCode("struct x foo_t; foo_t typedef y;"); } void garbageCode2() { //#4300 (segmentation fault) TODO_ASSERT_THROW(checkCode("enum { D = 1 struct { } ; } s.b = D;"), InternalError); } void garbageCode3() { //#4849 (segmentation fault in Tokenizer::simplifyStructDecl (invalid code)) TODO_ASSERT_THROW(checkCode("enum { D = 2 s ; struct y { x } ; } { s.a = C ; s.b = D ; }"), InternalError); } void garbageCode4() { // #4887 ASSERT_THROW(checkCode("void f ( ) { = a ; if ( 1 ) if = ( 0 ) ; }"), InternalError); } void garbageCode5() { // #5168 checkCode("( asm : ; void : );"); } void garbageCode6() { // #5214 ASSERT_THROW(checkCode("int b = ( 0 ? ? ) 1 : 0 ;"), InternalError); ASSERT_THROW(checkCode("int a = int b = ( 0 ? ? ) 1 : 0 ;"), InternalError); } void garbageCode7() { ASSERT_THROW(checkCode("1 (int j) { return return (c) * sizeof } y[1];"), InternalError); ASSERT_THROW(checkCode("foo(Args&&...) fn void = { } auto template { { e = T::error }; };\n" "ScopedEnum1 se1; { enum class E : T { e = 0 = e ScopedEnum2 struct UnscopedEnum3 { T{ e = 4 }; };\n" "arr[(int) E::e]; }; UnscopedEnum3 e2 = f()\n" "{ { e = e1; T::error } int test1 ue2; g() { enum class E { e = T::error }; return E::e; } int test2 = } \n" "namespace UnscopedEnum { template struct UnscopedEnum1 { E{ e = T::error }; }; UnscopedEnum1 { enum E : { e = 0 }; };\n" "UnscopedEnum2 ue3; template struct UnscopedEnum3 { enum { }; }; int arr[E::e]; };\n" "UnscopedEnum3 namespace template int f() { enum E { e }; T::error }; return (int) E(); } int test1 int g() { enum E { e = E };\n" "E::e; } int test2 = g(); }"), InternalError); } void garbageCode9() { TODO_ASSERT_THROW(checkCode("enum { e = { } } ( ) { { enum { } } } { e } "), InternalError); } void garbageCode10() { // #6127 checkCode("for( rl=reslist; rl!=NULL; rl=rl->next )"); } void garbageCode12() { // do not crash checkCode("{ g; S (void) { struct } { } int &g; }"); } void garbageCode13() { checkCode("struct C {} {} x"); } void garbageCode15() { // Ticket #5203 ASSERT_THROW(checkCode("int f ( int* r ) { { int s[2] ; f ( s ) ; if ( ) } }"), InternalError); } void garbageCode16() { checkCode("{ } A() { delete }"); // #6080 } void garbageCode17() { ASSERT_THROW(checkCode("void h(int l) {\n" " while\n" // Don't crash (#3870) "}"), InternalError); } void garbageCode18() { ASSERT_THROW(checkCode("switch(){case}"), InternalError); } void garbageCode20() { // #3953 (valgrind errors on garbage code) ASSERT_EQUALS("void f ( 0 * ) ;", checkCode("void f ( 0 * ) ;")); } void garbageCode21() { // Ticket #3486 - Don't crash garbage code ASSERT_THROW(checkCode("void f()\n" "{\n" " (\n" " x;\n" " int a, a2, a2*x; if () ;\n" " )\n" "}"), InternalError); } void garbageCode22() { // Ticket #3480 - Don't crash garbage code ASSERT_THROW(checkCode("int f()\n" "{\n" " return if\n" "}"), InternalError); } void garbageCode23() { //garbage code : don't crash (#3481) checkCode("{\n" " if (1) = x\n" " else abort s[2]\n" "}"); ASSERT_EQUALS("", errout.str()); } void garbageCode24() { // don't crash (example from #6361) ASSERT_THROW(checkCode("float buffer[64];\n" "main (void)\n" "{\n" " char *cptr;\n" " cptr = (char *)buffer;\n" " cptr += (-(long int) buffer & (16 * sizeof (float) - 1));\n" "}\n"), InternalError); } void garbageCode25() { // Ticket #2386 - Segmentation fault upon strange syntax ASSERT_THROW(checkCode("void f() {\n" " switch ( x ) {\n" " case struct Tree : break;\n" " }\n" "}"), InternalError); } void garbageCode26() { // See tickets #2518 #2555 #4171 ASSERT_THROW(checkCode("void f() {\n" " switch MAKEWORD(1)\n" " {\n" " case 0:\n" " return;\n" " }\n" "}"), InternalError); } void garbageCode27() { ASSERT_THROW(checkCode("int f() {\n" " return if\n" "}"), InternalError); } void garbageCode28() { // 5702 checkCode("struct R1 {\n" " int a;\n" " R1 () : a { }\n" "};\n"); } void garbageCode30() { // simply survive - a syntax error would be even better (#5867) checkCode("void f(int x) {\n" " x = 42\n" "}"); } void garbageCode31() { ASSERT_THROW(checkCode("typedef struct{}x[([],)]typedef e y;(y,x 0){}"), InternalError); } void garbageCode33() { // #6613 checkCode("main(()B{});"); } // Bug #6626 crash: Token::astOperand2() const ( do while ) void garbageCode34() { const char code[] = "void foo(void) {\n" " do\n" " while (0);\n" "}"; ASSERT_THROW(checkCode(code), InternalError); } void garbageCode35() { // ticket #2604 segmentation fault ASSERT_THROW(checkCode("sizeof <= A"), InternalError); } void garbageCode36() { // #6334 ASSERT_THROW(checkCode("{ } < class template < > , { = } ; class... >\n" "struct Y { }\n" "class Types { }\n" "( X < int > \"uses template\" ) ( < ( ) \"uses ;" "( int int ::primary \"uses template\" ) int double \"uses )" "::primary , \"uses template\" ;\n"), InternalError); } void garbageCode37() { // #5166 segmentation fault (invalid code) in lib/checkother.cpp:329 ( void * f { } void b ( ) { * f } ) checkCode("void * f { } void b ( ) { * f }"); } void garbageCode38() { // Ticket #6666 checkCode("{ f2 { } } void f3 () { delete[] } { }"); } void garbageCode40() { // #6620 checkCode("{ ( ) () { virtual } ; { } E } A { : { } ( ) } * const ( ) const { }"); // test doesn't seem to work on any platform: ASSERT_THROW(checkCode("{ ( ) () { virtual } ; { } E } A { : { } ( ) } * const ( ) const { }", "test.c"), InternalError); } void garbageCode41() { // #6685 checkCode(" { } { return } *malloc(__SIZE_TYPE__ size); *memcpy(void n); static * const () { memcpy (*slot, 3); } { (); } { }"); } void garbageCode42() { // #5760 checkCode("{ } * const ( ) { }"); } void garbageCode43() { // #6703 ASSERT_THROW(checkCode("int { }; struct A a = { }"), InternalError); } void garbageCode44() { // #6704 ASSERT_THROW(checkCode("{ { }; }; { class A : }; public typedef b;"), InternalError); } void garbageCode45() { // #6608 ASSERT_THROW(checkCode("struct true template < > { = } > struct Types \"s\" ; static_assert < int > ;"), InternalError); } void garbageCode46() { // #6705 checkCode(" { bar(char *x); void foo (int ...) { struct } va_list ap; va_start(ap, size); va_arg(ap, (d)); }"); } void garbageCode47() { // #6706 checkCode(" { { }; }; * new private: B: B;"); } void garbageCode48() { // #6712 checkCode(" { d\" ) d ...\" } int main ( ) { ( ) catch ( A a ) { { } catch ( ) \"\" } }"); } void garbageCode49() { // #6715 ASSERT_THROW(checkCode(" ( ( ) ) { } ( { ( __builtin_va_arg_pack ( ) ) ; } ) { ( int { ( ) ( ( ) ) } ( ) { } ( ) ) += ( ) }"), InternalError); } void garbageCode51() { // #6719 ASSERT_THROW(checkCode(" (const \"C\" ...); struct base { int f2; base (int arg1, int arg2); }; global_base(0x55, 0xff); { ((global_base.f1 0x55) (global_base.f2 0xff)) { } } base::base(int arg1, int arg2) { f2 = }"), InternalError); } void garbageCode53() { // #6721 ASSERT_THROW(checkCode("{ { } }; void foo (struct int i) { x->b[i] = = }"), InternalError); } void garbageCode54() { // #6722 ASSERT_THROW(checkCode("{ typedef long ((pf) p) (); }"), InternalError); } void garbageCode55() { // #6724 checkCode("() __attribute__((constructor)); { } { }"); } void garbageCode56() { // #6713 ASSERT_THROW(checkCode("void foo() { int a = 0; int b = ???; }"), InternalError); } void garbageCode57() { // #6731 ASSERT_THROW(checkCode("{ } if () try { } catch (...) B::~B { }"), InternalError); } void garbageCode58() { // #6732, #6762 ASSERT_THROW(checkCode("{ }> {= ~A()^{} }P { }"), InternalError); ASSERT_THROW(checkCode("{= ~A()^{} }P { } { }> is"), InternalError); } void garbageCode59() { // #6735 ASSERT_THROW(checkCode("{ { } }; char font8x8[256][8]"), InternalError); } void garbageCode60() { // #6736 ASSERT_THROW(checkCode("{ } { } typedef int int_array[]; int_array &right ="), InternalError); } void garbageCode61() { // #6737 ASSERT_THROW(checkCode("{ (const U&) }; { }; { }; struct U : virtual public"), InternalError); } void garbageCode63() { // #6739 ASSERT_THROW(checkCode("{ } { } typedef int u_array[]; typedef u_array &u_array_ref; (u_array_ref arg) { } u_array_ref u_array_ref_gbl_obj0"), InternalError); } void garbageCode64() { // #6740 ASSERT_THROW(checkCode("{ } foo(void (*bar)(void))"), InternalError); } void garbageCode65() { // #6741 ASSERT_THROW(checkCode("{ } { } typedef int u_array[]; typedef u_array &u_array_ref; (u_array_ref arg) { } u_array_ref"), InternalError); } void garbageCode66() { // #6742 ASSERT_THROW(checkCode("{ { } }; { { } }; { }; class bar : public virtual"), InternalError); } void garbageCode68() { // #6745 checkCode("(int a[3]); typedef void (*fp) (void); fp"); } void garbageCode69() { // #6746 ASSERT_THROW(checkCode("{ (make_mess, aux); } typedef void F(void); aux(void (*x)()) { } (void (*y)()) { } F*"), InternalError); } void garbageCode70() { // #6747 ASSERT_THROW(checkCode("{ } __attribute__((constructor)) void"), InternalError); } void garbageCode71() { // #6748 ASSERT_THROW(checkCode("( ) { } typedef void noattr_t ( ) ; noattr_t __attribute__ ( )"), InternalError); } void garbageCode72() { // #6749 ASSERT_THROW(checkCode("{ } { } typedef void voidfn(void); = } } unsigned *d = (b b--) --*d"), InternalError); } void garbageCode78() { // #6756 ASSERT_THROW(checkCode("( ) { [ ] } ( ) { } const_array_of_int ( ) { } typedef int A [ ] [ ] ; A a = { { } { } }"), InternalError); } void garbageCode79() { // #6757 ASSERT_THROW(checkCode("{ } { } typedef void ( func_type ) ( ) ; func_type & ( )"), InternalError); } void garbageCode80() { // #6759 ASSERT_THROW(checkCode("( ) { ; ( ) ; ( * ) [ ] ; [ ] = ( ( ) ( ) h ) ! ( ( ) ) } { ; } { } head heads [ ] = ; = & heads [ 2 ]"), InternalError); } void garbageCode81() { // #6760 ASSERT_THROW(checkCode("{ } [ ] { ( ) } { } typedef void ( *fptr1 ) ( ) const"), InternalError); } void garbageCode82() { // #6761 ASSERT_THROW(checkCode("p(\"Hello \" 14) _yn(const size_t) typedef bool pfunk (*pfunk)(const size_t)"), InternalError); } void garbageCode83() { // #6771 ASSERT_THROW(checkCode("namespace A { class } class A { friend C ; } { } ;"), InternalError); } void garbageCode84() { // #6780 ASSERT_THROW(checkCode("int main ( [ ] ) { " " [ ] ; int i = 0 ; do { } ; } ( [ ] ) { }"), InternalError); // do not crash } void garbageCode85() { // #6784 ASSERT_THROW(checkCode("{ } { } typedef void ( *VoidFunc() ) ( ) ; VoidFunc"), InternalError); // do not crash } void garbageCode86() { // #6785 ASSERT_THROW(checkCode("{ } typedef char ( *( X ) ( void) , char ) ;"), InternalError); // do not crash } void garbageCode87() { // #6788 ASSERT_THROW(checkCode("((X (128))) (int a) { v[ = {} (x 42) a] += }"), InternalError); // do not crash } void garbageCode88() { // #6786 ASSERT_THROW(checkCode("( ) { ( 0 ) { ( ) } } g ( ) { i( ( false ?) ( ) : 1 ) ; } ;"), InternalError); // do not crash } void garbageCode90() { // #6790 ASSERT_THROW(checkCode("{ } { } typedef int u_array [[ ] ; typedef u_array & u_array_ref] ( ) { } u_array_ref_gbl_obj0"), InternalError); // do not crash } void garbageCode91() { // #6791 ASSERT_THROW(checkCode("typedef __attribute__((vector_size (16))) { return[ (v2df){ } ;] }"), InternalError); // throw syntax error } void garbageCode92() { // #6792 ASSERT_THROW(checkCode("template < typename _Tp ( ( ) ; _Tp ) , decltype > { } { ( ) ( ) }"), InternalError); // do not crash } void garbageCode94() { // #6803 //checkCode("typedef long __m256i __attribute__ ( ( ( ) ) )[ ; ( ) { } typedef __m256i __attribute__ ( ( ( ) ) ) < ] ( ) { ; }"); ASSERT_THROW(checkCode("typedef long __m256i __attribute__ ( ( ( ) ) )[ ; ( ) { } typedef __m256i __attribute__ ( ( ( ) ) ) < ] ( ) { ; }"), InternalError); } void garbageCode95() { // #6804 ASSERT_THROW(checkCode("{ } x x ; { } h h [ ] ( ) ( ) { struct x ( x ) ; int __attribute__ ( ) f ( ) { h - > first = & x ; struct x * n = h - > first ; ( ) n > } }"), InternalError); // do not crash } void garbageCode96() { // #6807 ASSERT_THROW(checkCode("typedef J J[ ; typedef ( ) ( ) { ; } typedef J J ;] ( ) ( J cx ) { n } ;"), InternalError); // throw syntax error } void garbageCode97() { // #6808 ASSERT_THROW(checkCode("namespace A {> } class A{ { }} class A : T< ;"), InternalError); } void garbageCode98() { // #6838 ASSERT_THROW(checkCode("for (cocon To::ta@Taaaaaforconst oken aaaaaaaaaaaa5Dl()\n" "const unsigned in;\n" "fon *tok = f);.s(Token i = d-)L;"), InternalError); } void garbageCode99() { // #6726 ASSERT_THROW(checkCode("{ xs :: i(:) ! ! x/5 ! !\n" "i, :: a :: b integer, } foo2(x) :: j(:) \n" "b type(*), d(:), a x :: end d(..), foo end\n" "foo4 b d(..), a a x type(*), b foo2 b"), InternalError); } void garbageCode100() { // #6840 ASSERT_THROW(checkCode("( ) { ( i< ) } int foo ( ) { int i ; ( for ( i => 1 ) ; ) }"), InternalError); } void garbageCode101() { // #6835 // Reported case ASSERT_THROW(checkCode("template < class , =( , int) X = 1 > struct A { } ( ) { = } [ { } ] ( ) { A < void > 0 }"), InternalError); // Reduced case ASSERT_THROW(checkCode("template < class =( , ) X = 1> struct A {}; A a;"), InternalError); } void garbageCode102() { // #6846 checkCode("struct Object { ( ) ; Object & operator= ( Object ) { ( ) { } if ( this != & b ) } }"); } void garbageCode103() { // #6824 ASSERT_THROW(checkCode("a f(r) int * r; { { int s[2]; [f(s); if () ] } }"), InternalError); } void garbageCode104() { // #6847 ASSERT_THROW(checkCode("template < Types > struct S {> ( S < ) S >} { ( ) { } } ( ) { return S < void > ( ) } { ( )> >} { ( ) { } } ( ) { ( ) }"), InternalError); } void garbageCode105() { // #6859 ASSERT_THROW(checkCode("void foo (int i) { int a , for (a 1; a( < 4; a++) if (a) (b b++) (b);) n++; }"), InternalError); } void garbageCode106() { // #6880 ASSERT_THROW(checkCode("[ ] typedef typedef b_array b_array_ref [ ; ] ( ) b_array_ref b_array_ref_gbl_obj0 { ; { b_array_ref b_array_ref_gbl_obj0 } }"), InternalError); } void garbageCode107() { // #6881 TODO_ASSERT_THROW(checkCode("enum { val = 1{ }; { const} }; { } Bar { const int A = val const } ;"), InternalError); } void garbageCode108() { // #6895 "segmentation fault (invalid code) in CheckCondition::isOppositeCond" ASSERT_THROW(checkCode("A( ) { } bool f( ) { ( ) F; ( ) { ( == ) if ( !=< || ( !A( ) && r[2] ) ) ( !A( ) ) ( ) } }"), InternalError); } void garbageCode109() { // #6900 "segmentation fault (invalid code) in CheckStl::runSimplifiedChecks" checkCode("( *const<> (( ) ) { } ( *const ( ) ( ) ) { } ( * const<> ( size_t )) ) { } ( * const ( ) ( ) ) { }"); } void garbageCode110() { // #6902 "segmentation fault (invalid code) in CheckStl::string_c_str" ASSERT_THROW(checkCode("( *const<> ( size_t ) ; foo ) { } * ( *const ( size_t ) ( ) ;> foo )< { }"), InternalError); } void garbageCode111() { // #6907 TODO_ASSERT_THROW(checkCode("enum { FOO = 1( ,) } {{ FOO }} ;"), InternalError); } void garbageCode112() { // #6909 TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ) } {{ }}>> enum { FOO< = ( ) } { { } } ;"), InternalError); } void garbageCode114() { // #2118 checkCode("Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n" " for (unsigned i = 0 ; i < count; i++) {\n" " }\n" "});"); } void garbageCode115() { // #5506 ASSERT_THROW(checkCode("A template < int { int = -1 ; } template < int N > struct B { int [ A < N > :: zero ] ; } ; B < 0 > b ;"), InternalError); } void garbageCode116() { // #5356 ASSERT_THROW(checkCode("struct template struct B { }; B < 0 > b;"), InternalError); } void garbageCode117() { // #6121 TODO_ASSERT_THROW(checkCode("enum E { f = {} };\n" "int a = f;"), InternalError); } void garbageCode118() { // #5600 - missing include causes invalid enum ASSERT_THROW(checkCode("enum {\n" " NUM_OPCODES = \n" // #include "definition" "};\n" "struct bytecode {};\n" "jv jq_next() { opcode = ((opcode) +NUM_OPCODES);\n" "}"), InternalError); } void garbageCode119() { // #5598 checkCode("{ { void foo() { struct }; template struct S { Used x; void bar() } auto f = [this] { }; } };"); } void garbageCode120() { // #4927 checkCode("int main() {\n" " return 0\n" "}"); ASSERT_EQUALS("", errout.str()); } void garbageCode121() { // #2585 ASSERT_THROW(checkCode("abcdef?""?<" "123456?""?>" "+?""?="), InternalError); } void garbageCode122() { // #6303 checkCode("void foo() {\n" "char *a = malloc(10);\n" "a[0]\n" "}"); } void garbageCode123() { ASSERT_THROW(checkCode("namespace pr16989 {\n" " class C {\n" " C tpl_mem(T *) { return }\n" " };\n" "}"), InternalError); } void garbageCode125() { ASSERT_THROW(checkCode("{ T struct B : T valueA_AA ; } T : [ T > ( ) { B } template < T > struct A < > : ] { ( ) { return valueA_AC struct { : } } b A < int > AC ( ) a_aa.M ; ( ) ( ) }"), InternalError); ASSERT_THROW(checkCode("template < Types > struct S :{ ( S < ) S >} { ( ) { } } ( ) { return S < void > ( ) }"), InternalError); } void garbageCode126() { ASSERT_THROW(checkCode("{ } float __ieee754_sinhf ( float x ) { float t , , do { gf_u ( jx ) { } ( 0 ) return ; ( ) { } t } ( 0x42b17180 ) { } }"), InternalError); } void garbageCode127() { // #6667 checkCode("extern \"C\" int printf(const char* fmt, ...);\n" "class A {\n" "public:\n" " int Var;\n" " A(int arg) { Var = arg; }\n" " ~A() { printf(\"A d'tor\\n\"); }\n" "};\n" " const A& foo(const A& arg) { return arg; }\n" " foo(A(12)).Var\n"); } void garbageCode128() { TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ) } {{ }} enum {{ FOO << = } ( ) } {{ }} ;"), InternalError); } void garbageCode129() { ASSERT_THROW(checkCode("operator - ( { } typedef typename x ; ( ) ) { ( { { ( ( ) ) } ( { } ) } ) }"), InternalError); } void garbageCode130() { TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ){ } { { } } { { FOO} = } ( ) } { { } } enumL\" ( enumL\" { { FOO } ( ) } { { } } ;"), InternalError); } void garbageCode131() { ASSERT_THROW(checkCode("( void ) { ( ) } ( ) / { ( ) }"), InternalError); // actually the invalid code should trigger an syntax error... } void garbageCode132() { // #7022 ASSERT_THROW(checkCode("() () { } { () () ({}) i() } void i(void(*ptr) ()) { ptr(!) () }"), InternalError); } void garbageCode133() { ASSERT_THROW(checkCode("void f() {{}"), InternalError); ASSERT_THROW(checkCode("void f()) {}"), InternalError); ASSERT_THROW(checkCode("void f()\n" "{\n" " foo(;\n" "}\n"), InternalError); ASSERT_THROW(checkCode("void f()\n" "{\n" " for(;;){ foo();\n" "}\n"), InternalError); ASSERT_THROW(checkCode("void f()\n" "{\n" " a[10;\n" "}\n"), InternalError); { const char code[] = "{\n" " a(\n" // <- error "}\n" "{\n" " b());\n" "}\n"; ASSERT_EQUALS("[test.cpp:2] Unmatched '('. Configuration: ''.", getSyntaxError(code)); } { const char code[] = "void f() {\n" " int x = 3) + 0;\n" // <- error: unmatched ) "}\n"; ASSERT_EQUALS("[test.cpp:2] Unmatched ')'. Configuration: ''.", getSyntaxError(code)); } { const char code[] = "void f() {\n" " int x = (3] + 0;\n" // <- error: unmatched ] "}\n"; ASSERT_EQUALS("[test.cpp:2] Unmatched ']'. Configuration: ''.", getSyntaxError(code)); } { const char code[] = "void f() {\n" // <- error: unmatched { " {\n" "}\n"; ASSERT_EQUALS("[test.cpp:1] Unmatched '{'. Configuration: ''.", getSyntaxError(code)); } } void garbageCode134() { // Ticket #5605, #5759, #5762, #5774, #5823, #6059 ASSERT_THROW(checkCode("foo() template struct tuple Args> tuple { } main() { foo(); }"), InternalError); ASSERT_THROW(checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"), InternalError); ASSERT_THROW(checkCode("() template < T = typename = x > struct a {} { f () }"), InternalError); ASSERT_THROW(checkCode("template < T = typename = > struct a { f }"), InternalError); checkCode("struct S { int i, j; }; " "template struct X {}; " "X<&S::i, int> x = X<&S::i, int>(); " "X<&S::j, int> y = X<&S::j, int>(); "); checkCode("template struct A {}; " "template <> struct A {}; " "void foo(const void* f = 0) {}"); checkCode("template struct A { " " static const int s = 0; " "}; " "A a;"); checkCode("template class A {}; " "template > class B {}; " "template > class C { " " C() : _a(0), _b(0) {} " " int _a, _b; " "};"); checkCode("template struct A { " " static int i; " "}; " "void f() { A::i = 0; }"); } void garbageCode135() { // #4994 checkCode("long f () {\n" " return a >> extern\n" "}\n" "long a = 1 ;\n" "long b = 2 ;"); } void garbageCode136() { // #7033 ASSERT_THROW(checkCode("{ } () { void f() { node_t * n; for (; -n) {} } } { }"), InternalError); } void garbageCode137() { // #7034 ASSERT_THROW(checkCode("\" \" typedef signed char f; \" \"; void a() { f * s = () &[]; (; ) (; ) }"), InternalError); } void garbageCode138() { // #6660 checkCode("CS_PLUGIN_NAMESPACE_BEGIN(csparser)\n" "{\n" " struct foo\n" " {\n" " union\n" " {};\n" " } halo;\n" "}\n" "CS_PLUGIN_NAMESPACE_END(csparser)"); } void garbageCode139() { // #6659 heap user after free: kernel: sm750_accel.c ASSERT_THROW(checkCode("void hw_copyarea() {\n" " de_ctrl = (nDirection == RIGHT_TO_LEFT) ?\n" " ( (0 & ~(((1 << (1 - (0 ? DE_CONTROL_DIRECTION))) - 1) << (0 ? DE_CONTROL_DIRECTION))) )\n" " : 42;\n" "}"), InternalError); } void garbageCode140() { // #7035 ASSERT_THROW(checkCode("int foo(int align) { int off(= 0 % align; return off) ? \\ align - off : 0; \\ }"), InternalError); } void garbageCode141() { // #7043 TODO_ASSERT_THROW(checkCode("enum { X = << { X } } enum { X = X } = X ;"), InternalError); } void garbageCode142() { // #7050 checkCode("{ } ( ) { void mapGraphs ( ) { node_t * n ; for (!oid n ) { } } } { }"); } void garbageCode143() { // #6922 ASSERT_THROW(checkCode("void neoProgramShadowRegs() {\n" " int i;\n" " Bool noProgramShadowRegs;\n" " if (noProgramShadowRegs) {\n" " } else {\n" " switch (nPtr->NeoPanelWidth) {\n" " case 1280:\n" " VGAwCR(0x64,0x?? );\n" " }\n" " }\n" "}"), InternalError); } void garbageCode144() { // #6865 ASSERT_THROW(checkCode("template < typename > struct A { } ; template < typename > struct A < INVALID > : A < int[ > { }] ;"), InternalError); } void garbageCode146() { // #7081 ASSERT_THROW(checkCode("void foo() {\n" " ? std::cout << pow((, 1) << std::endl;\n" " double () ^ {\n" " int *p don't crash checkCode("void f() {\n" " int a;\n" " do { a=do_something() } while (a);\n" "}"); } void garbageCode152() { // happened in travis, originally from llvm clang code const char* code = "template \n" "static std::string foo(char *Bla) {\n" " while (Bla[1] && Bla[1] != ',') }\n"; checkCode(code); } void garbageCode153() { TODO_ASSERT_THROW(checkCode("enum { X = << { X } } { X X } enum { X = << { ( X ) } } { } X */"), InternalError); } void garbageCode154() { checkCode("\"abc\"[];"); } void garbageCode156() { // #7120 checkCode("struct {}a; d f() { c ? : } {}a.p"); } void garbageCode157() { // #7131 ASSERT_THROW(checkCode("namespace std {\n" " template < typename >\n" " void swap(); \n" "}" "template std::swap\n"), InternalError); } void garbageCode158() { // #3238 checkCode("__FBSDID(\"...\");\n"); } void garbageCode159() { // #7119 ASSERT_THROW(checkCode("({}typedef typename x;typename x!){({{}()})}"), InternalError); } void garbageCode160() { // #7190 ASSERT_THROW(checkCode("f(a,b,c,d)float [ a[],d;int ] b[],c;{} "), InternalError); // don't hang } void garbageCodeFuzzerClientMode1() { ASSERT_THROW(checkCode("void f() { x= name2 & name3 name2 = | 0.1 , | 0.1 , | 0.1 name4 <= >( ); }"), InternalError); ASSERT_THROW(checkCode("void f() { x = , * [ | + 0xff | > 0xff]; }"), InternalError); ASSERT_THROW(checkCode("void f() { x = , | 0xff , 0.1 < ; }"), InternalError); ASSERT_THROW(checkCode("void f() { x = [ 1 || ] ; }"), InternalError); ASSERT_THROW(checkCode("void f1() { x = name6 1 || ? name3 [ ( 1 || +) ] ; }"), InternalError); } void garbageValueFlow() { // #6089 const char* code = "{} int foo(struct, x1, struct x2, x3, int, x5, x6, x7)\n" "{\n" " (foo(s, , 2, , , 5, , 7)) abort()\n" "}\n"; ASSERT_THROW(checkCode(code), InternalError); // 6122 survive garbage code code = "; { int i ; for ( i = 0 ; = 123 ; ) - ; }"; ASSERT_THROW(checkCode(code), InternalError); code = "void f1() { for (int n = 0 n < 10 n++); }"; checkCode(code); } void garbageSymbolDatabase() { checkCode("void f( { u = 1 ; } ) { }"); ASSERT_THROW(checkCode("{ }; void namespace A::f; { g() { int } }"), InternalError); ASSERT_THROW(checkCode("class Foo {}; class Bar : public Foo"), InternalError); checkCode("YY_DECL { switch (yy_act) {\n" " case 65: YY_BREAK\n" " case YY_STATE_EOF(block):\n" " yyterminate(); \n" "} }"); // #5663 } void garbageAST() { ASSERT_THROW(checkCode("N 1024 float a[N], b[N + 3], c[N]; void N; (void) i;\n" "int #define for (i = avx_test i < c[i]; i++)\n" "b[i + 3] = a[i] * {}"), InternalError); // Don't hang (#5787) checkCode("START_SECTION([EXTRA](bool isValid(const String &filename)))"); // Don't crash (#5991) // #8352 ASSERT_THROW(checkCode("else return % name5 name2 - =name1 return enum | { - name3 1 enum != >= 1 >= ++ { { || " "{ return return { | { - name3 1 enum != >= 1 >= ++ { name6 | ; ++}}}}}}}"), InternalError); ASSERT_THROW(checkCode("else return % name5 name2 - =name1 return enum | { - name3 1 enum != >= 1 >= ++ { { || " "{ return return { | { - name3 1 enum != >= 1 >= ++ { { || ; ++}}}}}}}}"), InternalError); } void templateSimplifierCrashes() { checkCode( // #5950 "struct A { \n" " template operator T*();\n" "}; \n" "\n" "template <> A::operator char*(){ return 0; } // specialization\n" "\n" "int main() { \n" " A a;\n" " int *ip = a.operator int*();\n" "}\n" "\n" "namespace PR5742 {\n" " template struct A { };\n" " struct S {\n" " template operator T();\n" " } s;\n" " void f() {\n" " s.operator A >();\n" " }\n" "}\n"); checkCode( // #6034 "template class T, typename... Args>\n" "struct foo > {\n" " const bool value = true;\n" "};\n" "\n" "template\n" "struct int_\n" "{};\n" "\n" "int main() {\n" " foo >::value;\n" "}\n" ); checkCode( // #6117 "template struct something_like_tuple\n" "{};\n" "template struct is_last {\n" " static const bool value = false;\n" "};\n" "template class Tuple, typename ... Head>\n" "struct is_last>\n" "{\n" " static const bool value = true;\n" "};\n" "\n" "#define SA(X) static_assert (X, #X)\n" "\n" "typedef something_like_tuple something_like_tuple_t;\n" "SA ((is_last::value == false));\n" "SA ((is_last::value == false));\n" ); checkCode( // #6225 "template \n" "void templ_fun_with_ty_pack() {}\n" " \n" "namespace PR20047 {\n" " template \n" " struct A {};\n" " using AliasA = A;\n" "}\n" ); // #3449 ASSERT_EQUALS("template < typename T > struct A ;\n" "struct B { template < typename T > struct C } ;\n" "{ } ;", checkCode("template struct A;\n" "struct B { template struct C };\n" "{};")); } void garbageCode161() { //7200 ASSERT_THROW(checkCode("{ }{ if () try { } catch (...)} B : : ~B { }"), InternalError); } void garbageCode162() { //7208 ASSERT_THROW(checkCode("return << >> x return << >> x ", false), InternalError); } void garbageCode163() { //7228 ASSERT_THROW(checkCode("typedef s f[](){typedef d h(;f)}", false), InternalError); } void garbageCode164() { //7234 checkCode("class d{k p;}(){d::d():B<()}"); } void garbageCode165() { //7235 ASSERT_THROW(checkCode("for(;..)", false),InternalError); } void garbageCode167() { //7237 ASSERT_THROW(checkCode("class D00i000{:D00i000::}i"),InternalError); } void garbageCode168() { // 7246 checkCode("long foo(void) { return *bar; }", false); } void garbageCode169() { // 6713 ASSERT_THROW(checkCode("( ) { ( ) ; { return } switch ( ) i\n" "set case break ; default: ( ) }", false), InternalError); } void garbageCode170() { // 7255 ASSERT_THROW(checkCode("d i(){{f*s=typeid(()0,)}}", false), InternalError); } void garbageCode171() { // 7270 ASSERT_THROW(checkCode("(){case()?():}:", false), InternalError); } void garbageCode172() { // #7357 ASSERT_THROW(checkCode("p>,"), InternalError); } void garbageCode173() { // #6781 heap corruption ; TemplateSimplifier::simplifyTemplateInstantiations ASSERT_THROW(checkCode(" template < Types > struct S : >( S < ...Types... > S <) > { ( ) { } } ( ) { return S < void > ( ) }"), InternalError); } void garbageCode174() { // #7356 ASSERT_THROW(checkCode("{r e() { w*constD = (())D = cast< }}"), InternalError); } void garbageCode175() { // #7027 ASSERT_THROW(checkCode("int f() {\n" " int i , j;\n" " for ( i = t3 , i < t1 ; i++ )\n" " for ( j = 0 ; j < = j++ )\n" " return t1 ,\n" "}"), InternalError); } void garbageCode176() { // #7527 checkCode("class t { { struct } enum class f : unsigned { q } b ; operator= ( T ) { switch ( b ) { case f::q: } } { assert ( b ) ; } } { ; & ( t ) ( f::t ) ; } ;"); } void garbageCode181() { ASSERT_THROW(checkCode("int test() { int +; }"), InternalError); } // #4195 - segfault for "enum { int f ( ) { return = } r = f ( ) ; }" void garbageCode182() { ASSERT_THROW(checkCode("enum { int f ( ) { return = } r = f ( ) ; }"), InternalError); } // #7505 - segfault void garbageCode183() { ASSERT_THROW(checkCode("= { int } enum return { r = f() f(); }"), InternalError); } void garbageCode184() { // #7699 checkCode("unsigned int AquaSalSystem::GetDisplayScreenCount() {\n" " NSArray* pScreens = [NSScreen screens];\n" " return pScreens ? [pScreens count] : 1;\n" "}"); } void garbageCode185() { // #6011 crash in libreoffice failure to create proper AST checkCode( "namespace binfilter\n" "{\n" " BOOL EnhWMFReader::ReadEnhWMF()\n" " {\n" " pOut->CreateObject( nIndex, GDI_BRUSH, new WinMtfFillStyle( ReadColor(), ( nStyle == BS_HOLLOW ) ? TRUE : FALSE ) );\n" " return bStatus;\n" " };\n" "}\n"); } // #8151 - segfault due to incorrect template syntax void garbageCode186() { ASSERT_THROW(checkCode("A<>C"), InternalError); } void garbageCode187() { // # 8152 - segfault in handling const std::string inp("0|\0|0>;\n", 8); ASSERT_THROW(checkCode(inp), InternalError); checkCode("template struct S : A< B || C > {};"); // No syntax error: #8390 checkCode("static_assert(A || B, ab);"); } void garbageCode188() { // #8255 ASSERT_THROW(checkCode("{z r(){(){for(;<(x);){if(0==0)}}}}"), InternalError); } void garbageCode189() { // #8317 checkCode("t&n(){()()[](){()}}$"); } void garbageCode190() { // #8307 ASSERT_THROW(checkCode("void foo() {\n" " int i;\n" " i *= 0;\n" " !i <;\n" "}"), InternalError); } void garbageCode191() { // #8333 ASSERT_THROW(checkCode("struct A { int f(const); };"), InternalError); ASSERT_THROW(checkCode("struct A { int f(int, const, char); };"), InternalError); ASSERT_THROW(checkCode("struct A { int f(struct); };"), InternalError); // The following code is valid and should not trigger any error checkCode("struct A { int f ( char ) ; } ;"); } void garbageCode192() { // #8386 (segmentation fault) ASSERT_THROW(checkCode("{(()[((0||0xf||))]0[])}"), InternalError); } // #8740 void garbageCode193() { ASSERT_THROW(checkCode("d f(){!=[]&&0()!=0}"), InternalError); } // #8384 void garbageCode194() { ASSERT_THROW(checkCode("{((()))(return 1||);}"), InternalError); } // #8709 - no garbage but to avoid stability regression void garbageCode195() { checkCode("a b;\n" "void c() {\n" " switch (d) { case b:; }\n" " double e(b);\n" " if(e <= 0) {}\n" "}"); } // #8265 void garbageCode196() { ASSERT_THROW(checkCode("0|,0<= void { ( ()) } 1 name3 return >= >= ( ) >= name5 (\n" " name5 name6 :nam00 [ ()])}))})})})};\n" "}"), InternalError); } // #8752 void garbageCode199() { checkCode("d f(){e n00e0[]n00e0&""0+f=0}"); } // #8757 void garbageCode200() { ASSERT_THROW(checkCode("(){e break,{(case)!{e:[]}}}"), InternalError); } // #8873 void garbageCode201() { ASSERT_THROW(checkCode("void f() { std::string s=\"abc\"; return s + }"), InternalError); } // #8907 void garbageCode202() { ASSERT_THROW(checkCode("void f() { UNKNOWN_MACRO(return); }"), InternalError); ASSERT_THROW(checkCode("void f() { UNKNOWN_MACRO(throw); }"), InternalError); } void garbageCode203() { // #8972 checkCode("{ > () {} }"); checkCode("template <> a > ::b();"); } void garbageCode204() { ASSERT_THROW(checkCode("template ()> c; template a as() {} as>();"), InternalError); } void garbageCode205() { checkCode("class CodeSnippetsEvent : public wxCommandEvent {\n" "public :\n" " CodeSnippetsEvent ( wxEventType commandType = wxEventType , int id = 0 ) ;\n" " CodeSnippetsEvent ( const CodeSnippetsEvent & event ) ;\n" "virtual wxEvent * Clone ( ) const { return new CodeSnippetsEvent ( * this ) ; }\n" "private :\n" " int m_SnippetID ;\n" "} ;\n" "const wxEventType wxEVT_CODESNIPPETS_GETFILELINKS = wxNewEventType ( )\n" "CodeSnippetsEvent :: CodeSnippetsEvent ( wxEventType commandType , int id )\n" ": wxCommandEvent ( commandType , id ) {\n" "}\n" "CodeSnippetsEvent :: CodeSnippetsEvent ( const CodeSnippetsEvent & Event )\n" ": wxCommandEvent ( Event )\n" ", m_SnippetID ( 0 ) {\n" "}"); // don't crash } void garbageCode206() { ASSERT_EQUALS("[test.cpp:1] syntax error: operator", getSyntaxError("void foo() { for (auto operator new : int); }")); ASSERT_EQUALS("[test.cpp:1] syntax error: operator", getSyntaxError("void foo() { for (a operator== :) }")); } void garbageCode207() { // #8750 ASSERT_THROW(checkCode("d f(){(.n00e0(return%n00e0''('')));}"), InternalError); } void garbageCode208() { // #8753 ASSERT_THROW(checkCode("d f(){(for(((((0{t b;((((((((()))))))))}))))))}"), InternalError); } void garbageCode209() { // #8756 ASSERT_THROW(checkCode("{(- -##0xf/-1 0)[]}"), InternalError); } void garbageCode210() { // #8762 ASSERT_THROW(checkCode("{typedef typedef c n00e0[]c000(;n00e0&c000)}"), InternalError); } void garbageCode211() { // #8764 ASSERT_THROW(checkCode("{typedef f typedef[]({typedef e e,>;typedef(((typedef (size_t); foo) { } *(*const (size_t)() ; foo) { }"), InternalError); // #6858 ASSERT_THROW(checkCode(">{ x while (y) z int = }"), InternalError); // #4175 ASSERT_THROW(checkCode("&p(!{}e x){({(0?:?){({})}()})}"), InternalError); // #7118 ASSERT_THROW(checkCode(" { struct { typename D4:typename Base }; };"), InternalError); // #3533 ASSERT_THROW(checkCode(" > template < . > struct Y < T > { = } ;\n"), InternalError); // #6108 } void syntaxErrorLastToken() { ASSERT_THROW(checkCode("int *"), InternalError); // #7821 ASSERT_THROW(checkCode("x[y]"), InternalError); // #2986 ASSERT_THROW(checkCode("( ) &"), InternalError); ASSERT_THROW(checkCode("|| #if #define <="), InternalError); // #2601 ASSERT_THROW(checkCode("f::y:y : \n"), InternalError); ASSERT_THROW(checkCode("++4++ + + E++++++++++ + ch " "tp.oed5[.]"), InternalError); // #7074 ASSERT_THROW(checkCode("d a(){f s=0()8[]s?():0}*()?:0", false), InternalError); // #7236 ASSERT_THROW(checkCode("!2 : #h2 ?:", false), InternalError); // #7769 ASSERT_THROW(checkCode("--"), InternalError); ASSERT_THROW(checkCode("volatile true , test < test < #ifdef __ppc__ true ,"), InternalError); // #4169 ASSERT_THROW(checkCode("a,b--\n"), InternalError); // #2847 ASSERT_THROW(checkCode("x a[0] ="), InternalError); // #2682 ASSERT_THROW(checkCode("auto_ptr\n"), InternalError); // #2967 ASSERT_THROW(checkCode("char a[1]\n"), InternalError); // #2865 ASSERT_THROW(checkCode("<><<"), InternalError); // #2612 ASSERT_THROW(checkCode("z"), InternalError); // #2831 ASSERT_THROW(checkCode("><,f typedef name5 | ( , ;){ } (); }"), InternalError); // #9067 ASSERT_THROW(checkCode("void f() { x= {}( ) ( 'x')[ ] (); }"), InternalError); // #9068 ASSERT_THROW(checkCode("void f() { x= y{ } name5 y[ ] + y ^ name5 ^ name5 for ( ( y y y && y y y && name5 ++ int )); }"), InternalError); // #9069 } void cliCode() { // #8913 /* ASSERT_THROW(checkCode("public ref class LibCecSharp : public CecCallbackMethods {\n" "array ^ FindAdapters(String ^ path) {} \n" "bool GetDeviceInformation(String ^ port, LibCECConfiguration ^configuration, uint32_t timeoutMs) {\n" "bool bReturn(false);\n" "}\n" "};\n"), InternalError); */ } void enumTrailingComma() { ASSERT_THROW(checkCode("enum ssl_shutdown_t {ssl_shutdown_none = 0,ssl_shutdown_close_notify = , } ;"), InternalError); // #8079 } void nonGarbageCode1() { checkCode("template class List {\n" "public:\n" " List();\n" " virtual ~List();\n" " template< class Predicate > u_int DeleteIf( const Predicate &pred );\n" "};\n" "template< class T >\n" "template< class Predicate > int\n" "List::DeleteIf( const Predicate &pred )\n" "{}\n"); // #8749 checkCode( "struct A {\n" " void operator+=(A&) && = delete;\n" "};\n"); // #8788 checkCode( "struct foo;\n" "void f() {\n" " auto fn = []() -> foo* { return new foo(); };\n" "}\n"); } }; REGISTER_TEST(TestGarbage) cppcheck-1.90/test/testimportproject.cpp000066400000000000000000000167661357737443600205520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "importproject.h" #include "settings.h" #include "testsuite.h" #include #include #include class TestImporter : public ImportProject { public: using ImportProject::importCompileCommands; using ImportProject::importCppcheckGuiProject; }; class TestImportProject : public TestFixture { public: TestImportProject() : TestFixture("TestImportProject") { } private: void run() OVERRIDE { TEST_CASE(setDefines); TEST_CASE(setIncludePaths1); TEST_CASE(setIncludePaths2); TEST_CASE(setIncludePaths3); // macro names are case insensitive TEST_CASE(importCompileCommands1); TEST_CASE(importCompileCommands2); // #8563 TEST_CASE(importCompileCommands3); // check with existing trailing / in directory TEST_CASE(importCompileCommands4); // only accept certain file types TEST_CASE(importCompileCommandsArgumentsSection); // Handle arguments section TEST_CASE(importCompileCommandsNoCommandSection); // gracefully handles malformed json TEST_CASE(importCppcheckGuiProject); } void setDefines() const { ImportProject::FileSettings fs; fs.setDefines("A"); ASSERT_EQUALS("A=1", fs.defines); fs.setDefines("A;B;"); ASSERT_EQUALS("A=1;B=1", fs.defines); fs.setDefines("A;;B;"); ASSERT_EQUALS("A=1;B=1", fs.defines); fs.setDefines("A;;B"); ASSERT_EQUALS("A=1;B=1", fs.defines); } void setIncludePaths1() const { ImportProject::FileSettings fs; std::list in(1, "../include"); std::map variables; fs.setIncludePaths("abc/def/", in, variables); ASSERT_EQUALS(1U, fs.includePaths.size()); ASSERT_EQUALS("abc/include/", fs.includePaths.front()); } void setIncludePaths2() const { ImportProject::FileSettings fs; std::list in(1, "$(SolutionDir)other"); std::map variables; variables["SolutionDir"] = "c:/abc/"; fs.setIncludePaths("/home/fred", in, variables); ASSERT_EQUALS(1U, fs.includePaths.size()); ASSERT_EQUALS("c:/abc/other/", fs.includePaths.front()); } void setIncludePaths3() const { // macro names are case insensitive ImportProject::FileSettings fs; std::list in(1, "$(SOLUTIONDIR)other"); std::map variables; variables["SolutionDir"] = "c:/abc/"; fs.setIncludePaths("/home/fred", in, variables); ASSERT_EQUALS(1U, fs.includePaths.size()); ASSERT_EQUALS("c:/abc/other/", fs.includePaths.front()); } void importCompileCommands1() const { const char json[] = "[ { \"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\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("FILESDIR=\"/usr/local/share/Cppcheck\";TEST1=1;TEST2=2", importer.fileSettings.begin()->defines); } void importCompileCommands2() const { const char json[] = "[ { \"directory\": \"/tmp\"," "\"command\": \"gcc -c src.c\"," "\"file\": \"src.c\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("/tmp/src.c", importer.fileSettings.begin()->filename); } void importCompileCommands3() const { const char json[] = "[ { \"directory\": \"/tmp/\"," "\"command\": \"gcc -c src.c\"," "\"file\": \"src.c\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("/tmp/src.c", importer.fileSettings.begin()->filename); } void importCompileCommands4() const { const char json[] = "[ { \"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 importCompileCommandsArgumentsSection() const { const char json[] = "[ { \"directory\": \"/tmp/\"," "\"arguments\": [\"gcc\", \"-c\", \"src.c\"]," "\"file\": \"src.c\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(1, importer.fileSettings.size()); ASSERT_EQUALS("/tmp/src.c", importer.fileSettings.begin()->filename); } void importCompileCommandsNoCommandSection() const { const char json[] = "[ { \"directory\": \"/tmp/\"," "\"file\": \"src.mm\" } ]"; std::istringstream istr(json); TestImporter importer; importer.importCompileCommands(istr); ASSERT_EQUALS(0, importer.fileSettings.size()); } void importCppcheckGuiProject() const { const char xml[] = "\n" "\n" " \n" " out1\n" " true\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n"; std::istringstream istr(xml); Settings s; TestImporter project; ASSERT_EQUALS(true, project.importCppcheckGuiProject(istr, &s)); ASSERT_EQUALS(1, project.guiProject.pathNames.size()); ASSERT_EQUALS("cli/", project.guiProject.pathNames[0]); ASSERT_EQUALS(1, s.includePaths.size()); ASSERT_EQUALS("lib/", s.includePaths.front()); } }; REGISTER_TEST(TestImportProject) cppcheck-1.90/test/testincompletestatement.cpp000066400000000000000000000304241357737443600217200ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkother.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include class TestIncompleteStatement : public TestFixture { public: TestIncompleteStatement() : TestFixture("TestIncompleteStatement") { } private: Settings settings; void check(const char code[], bool inconclusive = false) { // Clear the error buffer.. errout.str(""); settings.inconclusive = inconclusive; // Raw tokens.. std::vector files(1, "test.cpp"); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenize.. Tokenizer tokenizer(&settings, this); tokenizer.createTokens(&tokens2); tokenizer.simplifyTokens1(""); // Check for incomplete statements.. CheckOther checkOther(&tokenizer, &settings, this); checkOther.checkIncompleteStatement(); } void run() OVERRIDE { settings.addEnabled("warning"); TEST_CASE(test1); TEST_CASE(test2); TEST_CASE(test3); TEST_CASE(test4); TEST_CASE(test5); TEST_CASE(test6); TEST_CASE(test7); TEST_CASE(test_numeric); TEST_CASE(void0); // #6327: No fp for statement "(void)0;" TEST_CASE(intarray); TEST_CASE(structarraynull); TEST_CASE(structarray); TEST_CASE(conditionalcall); // ; 0==x ? X() : Y(); TEST_CASE(structinit); // #2462 : ABC abc{1,2,3}; TEST_CASE(returnstruct); TEST_CASE(cast); // #3009 : (struct Foo *)123.a = 1; TEST_CASE(increment); // #3251 : FP for increment TEST_CASE(cpp11init); // #5493 : int i{1}; TEST_CASE(cpp11init2); // #8449 TEST_CASE(cpp11init3); // #8995 TEST_CASE(block); // ({ do_something(); 0; }) TEST_CASE(mapindex); TEST_CASE(commaoperator); TEST_CASE(redundantstmts); TEST_CASE(vardecl); TEST_CASE(archive); // ar & x TEST_CASE(ast); } void test1() { check("void foo()\n" "{\n" " const char def[] =\n" " \"abc\";\n" "}"); ASSERT_EQUALS("", errout.str()); } void test2() { check("void foo()\n" "{\n" " \"abc\";\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant code: Found a statement that begins with string constant.\n", errout.str()); } void test3() { check("void foo()\n" "{\n" " const char *str[] = { \"abc\" };\n" "}"); ASSERT_EQUALS("", errout.str()); } void test4() { check("void foo()\n" "{\n" "const char *a =\n" "{\n" "\"hello \"\n" "\"more \"\n" "\"world\"\n" "};\n" "}"); ASSERT_EQUALS("", errout.str()); } void test5() { check("void foo()\n" "{\n" " 50;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant code: Found a statement that begins with numeric constant.\n", errout.str()); } void test6() { // don't crash check("void f() {\n" " 1 == (two + three);\n" " 2 != (two + three);\n" " (one + two) != (two + three);\n" "}"); } void test7() { // #9335 check("namespace { std::string S = \"\"; }\n" "\n" "class C {\n" "public:\n" " explicit C(const std::string& s);\n" "};\n" "\n" "void f() {\n" " for (C c(S); ; ) {\n" " (void)c;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void test_numeric() { check("struct P {\n" " double a;\n" " double b;\n" "};\n" "void f() {\n" " const P values[2] =\n" " {\n" " { 346.1,114.1 }, { 347.1,111.1 }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } void void0() { // #6327 check("void f() { (void*)0; }"); ASSERT_EQUALS("", errout.str()); check("#define X 0\n" "void f() { X; }"); ASSERT_EQUALS("", errout.str()); } void intarray() { check("int arr[] = { 100/2, 1*100 };"); ASSERT_EQUALS("", errout.str()); } void structarraynull() { check("struct st arr[] = {\n" " { 100/2, 1*100 }\n" " { 90, 70 }\n" "}"); ASSERT_EQUALS("", errout.str()); } void structarray() { check("struct st arr[] = {\n" " { 100/2, 1*100 }\n" " { 90, 70 }\n" "};"); ASSERT_EQUALS("", errout.str()); } void conditionalcall() { check("void f() {\n" " 0==x ? X() : Y();\n" "}"); ASSERT_EQUALS("", errout.str()); } void structinit() { // #2462 - C++11 struct initialization check("void f() {\n" " ABC abc{1,2,3};\n" "}"); ASSERT_EQUALS("", errout.str()); // #6260 - C++11 array initialization check("void foo() {\n" " static const char* a[][2] {\n" " {\"b\", \"\"},\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); // #2482 - false positive for empty struct check("struct A {};"); ASSERT_EQUALS("", errout.str()); // #4387 - C++11 initializer list check("A::A() : abc{0} {}"); ASSERT_EQUALS("", errout.str()); // #5042 - C++11 initializer list check("A::A() : abc::def{0} {}"); ASSERT_EQUALS("", errout.str()); // #4503 - vector init check("void f() { vector v{1}; }"); ASSERT_EQUALS("", errout.str()); } void returnstruct() { check("struct s foo() {\n" " return (struct s){0,0};\n" "}"); ASSERT_EQUALS("", errout.str()); // #4754 check("unordered_map foo() {\n" " return {\n" " {\"hi\", \"there\"},\n" " {\"happy\", \"sad\"}\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct s foo() {\n" " return (struct s){0};\n" "}"); ASSERT_EQUALS("", errout.str()); } void cast() { check("void f() {\n" " ((struct foo *)(0x1234))->xy = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void increment() { check("void f() {\n" " int x = 1;\n" " x++, x++;\n" "}"); ASSERT_EQUALS("", errout.str()); } void cpp11init() { check("void f() {\n" " int x{1};\n" "}"); ASSERT_EQUALS("", errout.str()); } void cpp11init2() { check("x handlers{\n" " { \"mode2\", []() { return 2; } },\n" "};"); ASSERT_EQUALS("", errout.str()); } void cpp11init3() { check("struct A { void operator()(int); };\n" "void f() {\n" "A{}(0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("template struct A { void operator()(int); };\n" "void f() {\n" "A{}(0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void block() { check("void f() {\n" " ({ do_something(); 0; });\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" "out:\n" " ({ do_something(); 0; });\n" "}"); ASSERT_EQUALS("", errout.str()); } void mapindex() { check("void f() {\n" " map[{\"1\",\"2\"}]=0;\n" "}"); ASSERT_EQUALS("", errout.str()); } // #8827 void commaoperator() { check("void foo(int,const char*,int);\n" "void f(int value) {\n" " foo(42,\"test\",42),(value&42);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found suspicious operator ','\n", errout.str()); } // #8451 void redundantstmts() { check("void f1(int x) {\n" " 1;\n" " (1);\n" " (char)1;\n" " ((char)1);\n" " !x;\n" " (!x);\n" " (unsigned int)!x;\n" " ~x;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" "[test.cpp:3]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" "[test.cpp:4]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" "[test.cpp:5]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" "[test.cpp:6]: (warning, inconclusive) Found suspicious operator '!'\n" "[test.cpp:7]: (warning, inconclusive) Found suspicious operator '!'\n" "[test.cpp:9]: (warning, inconclusive) Found suspicious operator '~'\n", errout.str()); check("void f1(int x) { x; }", true); ASSERT_EQUALS("[test.cpp:1]: (warning) Unused variable value 'x'\n", errout.str()); } void vardecl() { // #8984 check("void f() { a::b *c = d(); }", true); ASSERT_EQUALS("", errout.str()); check("void f() { std::vector *c; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { a::b &c = d(); }", true); ASSERT_EQUALS("", errout.str()); check("void f() { std::vector &c; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { a::b &&c = d(); }", true); ASSERT_EQUALS("", errout.str()); check("void f() { std::vector &&c; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { char * const * a, * const * b; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { char * const * a = 0, * volatile restrict * b; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { char * const * a = 0, * volatile const * b; }", true); ASSERT_EQUALS("", errout.str()); } void archive() { check("void f(Archive &ar) {\n" " ar & x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(int ar) {\n" " ar & x;\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '&'\n", errout.str()); } void ast() { check("struct c { void a() const { for (int x=0; x;); } };", true); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestIncompleteStatement) cppcheck-1.90/test/testinternal.cpp000066400000000000000000000510141357737443600174460ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef CHECK_INTERNAL #include "tokenize.h" #include "checkinternal.h" #include "testsuite.h" class TestInternal : public TestFixture { public: TestInternal() : TestFixture("TestInternal") { } private: Settings settings; void run() OVERRIDE { settings.addEnabled("internal"); TEST_CASE(simplePatternInTokenMatch) TEST_CASE(complexPatternInTokenSimpleMatch) TEST_CASE(simplePatternSquareBrackets) TEST_CASE(simplePatternAlternatives) TEST_CASE(missingPercentCharacter) TEST_CASE(unknownPattern) TEST_CASE(redundantNextPrevious) TEST_CASE(internalError); TEST_CASE(orInComplexPattern); TEST_CASE(extraWhitespace); TEST_CASE(checkRedundantTokCheck); } void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check.. CheckInternal checkInternal; checkInternal.runChecks(&tokenizer, &settings, this); } void simplePatternInTokenMatch() { check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \";\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::Match() call: \";\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%or%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::Match() call: \"%or%\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findmatch(tok, \";\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::findmatch() call: \";\"\n", errout.str()); } void complexPatternInTokenSimpleMatch() { check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"%type%\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::findsimplematch() call: \"%type%\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"} !!else\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::findsimplematch() call: \"} !!else\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"foobar\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"%\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void simplePatternSquareBrackets() { check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"[\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"[ ]\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"[]\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"[]\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"] [\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"] [ [abc]\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"] [ [abc]\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"[.,;]\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"[.,;]\"\n", errout.str()); } void simplePatternAlternatives() { check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"||\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"|\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"a|b\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"a|b\"\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"|= 0\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"| 0 )\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingPercentCharacter() { check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo %type% bar\");\n" "}"); ASSERT_EQUALS("", errout.str()); // Missing % at the end of string check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%type\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"%type\"\n", errout.str()); // Missing % in the middle of a pattern check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo %type bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo %type bar\"\n", errout.str()); // Bei quiet on single % check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo % %type% bar\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo % %type % bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo % %type % bar\"\n" "[test.cpp:3]: (error) Unknown pattern used: \"%type %\"\n", errout.str()); // Find missing % also in 'alternatives' pattern check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo|%type|bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo|%type|bar\"\n" , errout.str()); // Make sure we don't take %or% for a broken %oror% check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"foo|%oror%|bar\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void unknownPattern() { check("void f() {\n" " Token::Match(tok, \"%typ%\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Unknown pattern used: \"%typ%\"\n", errout.str()); // Make sure we don't take %or% for a broken %oror% check("void f() {\n" " Token::Match(tok, \"%type%\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantNextPrevious() { check("void f() {\n" " return tok->next()->previous();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::next()' followed by 'Token::previous()' can be simplified.\n", errout.str()); check("void f() {\n" " return tok->tokAt(5)->previous();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::previous()' can be simplified.\n", errout.str()); check("void f() {\n" " return tok->previous()->linkAt(5);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::previous()' followed by 'Token::linkAt()' can be simplified.\n", errout.str()); check("void f() {\n" " tok->next()->previous(foo);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " return tok->next()->next();\n" // Simplification won't make code much shorter/readable "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " return tok->previous()->previous();\n" // Simplification won't make code much shorter/readable "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " return tok->tokAt(foo+bar)->tokAt();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::tokAt()' can be simplified.\n", errout.str()); check("void f() {\n" " return tok->tokAt(foo+bar)->link();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::link()' can be simplified.\n", errout.str()); check("void f() {\n" " tok->tokAt(foo+bar)->link(foo);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " return tok->next()->next()->str();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::next()' followed by 'Token::str()' can be simplified.\n", errout.str()); check("void f() {\n" " return tok->previous()->next()->str();\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::previous()' followed by 'Token::next()' can be simplified.\n", errout.str()); } void internalError() { // Make sure cppcheck does not raise an internal error of Token::Match ( Ticket #3727 ) check("class DELPHICLASS X;\n" "class Y {\n" "private:\n" " X* x;\n" "};\n" "class Z {\n" " char z[1];\n" " Z(){\n" " z[0] = 0;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void orInComplexPattern() { check("void f() {\n" " Token::Match(tok, \"||\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"||\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout.str()); check("void f() {\n" " Token::Match(tok, \"|\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"|\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout.str()); check("void f() {\n" " Token::Match(tok, \"[|+-]\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " Token::Match(tok, \"foo | bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"foo | bar\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout.str()); check("void f() {\n" " Token::Match(tok, \"foo |\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"foo |\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout.str()); check("void f() {\n" " Token::Match(tok, \"bar foo|\");\n" "}"); ASSERT_EQUALS("", errout.str()); } void extraWhitespace() { // whitespace at the end check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%str% \");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \"%str% \"\n", errout.str()); // whitespace at the begin check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \" %str%\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \" %str%\"\n", errout.str()); // two whitespaces or more check("void f() {\n" " const Token *tok;\n" " Token::Match(tok, \"%str% bar\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \"%str% bar\"\n", errout.str()); // test simpleMatch check("void f() {\n" " const Token *tok;\n" " Token::simpleMatch(tok, \"foobar \");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::simpleMatch() call: \"foobar \"\n", errout.str()); // test findmatch check("void f() {\n" " const Token *tok;\n" " Token::findmatch(tok, \"%str% \");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::findmatch() call: \"%str% \"\n", errout.str()); // test findsimplematch check("void f() {\n" " const Token *tok;\n" " Token::findsimplematch(tok, \"foobar \");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::findsimplematch() call: \"foobar \"\n", errout.str()); } void checkRedundantTokCheck() { // findsimplematch check("void f() {\n" " const Token *tok;\n" " if(tok && Token::findsimplematch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); // findmatch check("void f() {\n" " const Token *tok;\n" " if(tok && Token::findmatch(tok, \"%str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); // Match check("void f() {\n" " const Token *tok;\n" " if(tok && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a && tok && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a && b && tok && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a && b && c && tok && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a && b && c && tok && d && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // simpleMatch check("void f() {\n" " const Token *tok;\n" " if(tok && Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); // Match check("void f() {\n" " const Token *tok;\n" " if(tok->previous() && Token::Match(tok->previous(), \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok->previous()\", match-function already checks if it is null.\n", errout.str()); // don't report: // tok->previous() vs tok check("void f() {\n" " const Token *tok;\n" " if(tok->previous() && Token::Match(tok, \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // tok vs tok->previous()) check("void f() {\n" " const Token *tok;\n" " if(tok && Token::Match(tok->previous(), \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // tok->previous() vs tok->previous()->previous()) check("void f() {\n" " const Token *tok;\n" " if(tok->previous() && Token::Match(tok->previous()->previous(), \"5str% foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // if a && fn(a) triggers, make sure !a || !fn(a) triggers as well! check("void f() {\n" " const Token *tok;\n" " if(!tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); check("void f() {\n" " const Token *tok;\n" " if(a || !tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout.str()); // if tok || !Token::simpleMatch... check("void f() {\n" " const Token *tok;\n" " if(tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // if !tok || Token::simpleMatch... check("void f() {\n" " const Token *tok;\n" " if(!tok || Token::simpleMatch(tok, \"foobar\")) {};\n" "}"); ASSERT_EQUALS("", errout.str()); // something more complex check("void f() {\n" " const Token *tok;\n" " if(!tok->previous()->previous() || !Token::simpleMatch(tok->previous()->previous(), \"foobar\")) {};\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok->previous()->previous()\", match-function already checks if it is null.\n", errout.str()); } }; REGISTER_TEST(TestInternal) #endif // #ifdef CHECK_INTERNAL cppcheck-1.90/test/testio.cpp000066400000000000000000012052251357737443600162470ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkio.h" #include "platform.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestIO : public TestFixture { public: TestIO() : TestFixture("TestIO") { } private: Settings settings; void run() OVERRIDE { LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "windows.cfg"); LOAD_LIB_2(settings.library, "qt.cfg"); TEST_CASE(coutCerrMisusage); TEST_CASE(wrongMode_simple); TEST_CASE(wrongMode_complex); TEST_CASE(useClosedFile); TEST_CASE(fileIOwithoutPositioning); TEST_CASE(seekOnAppendedFile); TEST_CASE(fflushOnInputStream); TEST_CASE(testScanf1); // Scanf without field limiters TEST_CASE(testScanf2); TEST_CASE(testScanf3); // #3494 TEST_CASE(testScanf4); // #ticket 2553 TEST_CASE(testScanfArgument); TEST_CASE(testPrintfArgument); TEST_CASE(testPrintfArgumentVariables); TEST_CASE(testPosixPrintfScanfParameterPosition); // #4900 TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902 TEST_CASE(testMicrosoftScanfArgument); TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920 TEST_CASE(testMicrosoftSecurePrintfArgument); TEST_CASE(testMicrosoftSecureScanfArgument); TEST_CASE(testQStringFormatArguments); TEST_CASE(testTernary); // ticket #6182 TEST_CASE(testUnsignedConst); // ticket #6132 TEST_CASE(testAstType); // #7014 TEST_CASE(testPrintf0WithSuffix); // ticket #7069 TEST_CASE(testReturnValueTypeStdLib); TEST_CASE(testPrintfTypeAlias1); TEST_CASE(testPrintfAuto); // #8992 } void check(const char* code, bool inconclusive = false, bool portability = false, Settings::PlatformType platform = Settings::Unspecified) { // Clear the error buffer.. errout.str(""); settings.clearEnabled(); settings.addEnabled("warning"); settings.addEnabled("style"); if (portability) settings.addEnabled("portability"); settings.inconclusive = inconclusive; settings.platform(platform); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check.. CheckIO checkIO(&tokenizer, &settings, this); checkIO.checkWrongPrintfScanfArguments(); // Simplify token list.. tokenizer.simplifyTokenList2(); checkIO.checkCoutCerrMisusage(); checkIO.checkFileUsage(); checkIO.invalidScanf(); } void coutCerrMisusage() { check( "void foo() {\n" " std::cout << std::cout;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str()); check( "void foo() {\n" " std::cout << (std::cout);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str()); check( "void foo() {\n" " std::cout << \"xyz\" << std::cout;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cout'.\n", errout.str()); check( "void foo(int i) {\n" " std::cout << i << std::cerr;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Invalid usage of output stream: '<< std::cerr'.\n", errout.str()); check( "void foo() {\n" " std::cout << \"xyz\";\n" " std::cout << \"xyz\";\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " std::cout << std::cout.good();\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " unknownObject << std::cout;\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " MACRO(std::cout <<, << std::cout)\n" "}"); ASSERT_EQUALS("", errout.str()); } void wrongMode_simple() { // Read mode check("void foo(FILE*& f) {\n" " f = fopen(name, \"r\");\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _wfopen(name, L\"r\");\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfopen(name, _T(\"r\"));\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfopen(name, _T(\"r\"));\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " _wfopen_s(&f, name, L\"r\");\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " _tfopen_s(&f, name, _T(\"r\"));\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " _tfopen_s(&f, name, _T(\"r\"));\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(name, \"r+\");\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = _wfopen(name, L\"r+\");\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfopen(name, _T(\"r+\"));\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfopen(name, _T(\"r+\"));\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " _wfopen_s(&f, name, L\"r+\");\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " _tfopen_s(&f, name, _T(\"r+\"));\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " _tfopen_s(&f, name, _T(\"r+\"));\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = tmpfile();\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); // Write mode check("void foo(FILE*& f) {\n" " f = fopen(name, \"w\");\n" " fwrite(buffer, 5, 6, f);\n" " rewind(f);\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(name, \"w+\");\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = tmpfile();\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // Append mode check("void foo(FILE*& f) {\n" " f = fopen(name, \"a\");\n" " fwrite(buffer, 5, 6, f);\n" " rewind(f);\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Repositioning operation performed on a file opened in append mode has no effect.\n" "[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(name, \"a+\");\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // Variable declared locally check("void foo() {\n" " FILE* f = fopen(name, \"r\");\n" " fwrite(buffer, 5, 6, f);\n" " fclose(f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); // Call unknown function check("void foo(FILE*& f) {\n" " f = fopen(name, \"a\");\n" " fwrite(buffer, 5, 6, f);\n" " bar(f);\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); // freopen and tmpfile check("void foo(FILE*& f) {\n" " f = freopen(name, \"r\", f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _wfreopen(name, L\"r\", f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfreopen(name, _T(\"r\"), f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfreopen(name, _T(\"r\"), f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _wfreopen_s(&f, name, L\"r\", f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfreopen_s(&f, name, _T(\"r\"), f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = _tfreopen_s(&f, name, _T(\"r\"), f);\n" " fwrite(buffer, 5, 6, f);\n" "}", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); // Crash tests check("void foo(FILE*& f) {\n" " f = fopen(name, mode);\n" // No assertion failure (#3830) " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void fopen(std::string const &filepath, std::string const &mode);"); // #3832 } void wrongMode_complex() { check("void foo(FILE* f) {\n" " if(a) f = fopen(name, \"w\");\n" " else f = fopen(name, \"r\");\n" " if(a) fwrite(buffer, 5, 6, f);\n" " else fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f;\n" " if(a) f = fopen(name, \"w\");\n" " else f = fopen(name, \"r\");\n" " if(a) fwrite(buffer, 5, 6, f);\n" " else fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f = fopen(name, \"w\");\n" " if(a) fwrite(buffer, 5, 6, f);\n" " else fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Read operation on a file that was opened only for writing.\n", errout.str()); } void useClosedFile() { check("void foo(FILE*& f) {\n" " fclose(f);\n" " fwrite(buffer, 5, 6, f);\n" " clearerr(f);\n" " fread(buffer, 5, 6, f);\n" " ungetc('a', f);\n" " ungetwc(L'a', f);\n" " rewind(f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n" "[test.cpp:4]: (error) Used file that is not opened.\n" "[test.cpp:5]: (error) Used file that is not opened.\n" "[test.cpp:6]: (error) Used file that is not opened.\n" "[test.cpp:7]: (error) Used file that is not opened.\n" "[test.cpp:8]: (error) Used file that is not opened.\n", errout.str()); check("void foo(FILE*& f) {\n" " if(!ferror(f)) {\n" " fclose(f);\n" " return;" " }\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " fclose(f);\n" " f = fopen(name, \"r\");\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(name, \"r\");\n" " f = g;\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f;\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n", errout.str()); check("void foo() {\n" " FILE* f(stdout);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" // #3965 " FILE* f[3];\n" " f[0] = fopen(name, mode);\n" " fclose(f[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4368: multiple functions check("static FILE *fp = NULL;\n" "\n" "void close()\n" "{\n" " fclose(fp);\n" "}\n" "\n" "void dump()\n" "{\n" " if (fp == NULL) return;\n" " fprintf(fp, \"Here's the output.\\n\");\n" "}\n" "\n" "int main()\n" "{\n" " fp = fopen(\"test.txt\", \"w\");\n" " dump();\n" " close();\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static FILE *fp = NULL;\n" "\n" "void close()\n" "{\n" " fclose(fp);\n" "}\n" "\n" "void dump()\n" "{\n" " fclose(fp);\n" " fprintf(fp, \"Here's the output.\\n\");\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (error) Used file that is not opened.\n", errout.str()); // #4466 check("void chdcd_parse_nero(FILE *infile) {\n" " switch (mode) {\n" " case 0x0300:\n" " fclose(infile);\n" " return;\n" " case 0x0500:\n" " fclose(infile);\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void chdcd_parse_nero(FILE *infile) {\n" " switch (mode) {\n" " case 0x0300:\n" " fclose(infile);\n" " exit(0);\n" " case 0x0500:\n" " fclose(infile);\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #4649 check("void foo() {\n" " struct {FILE *f1; FILE *f2;} a;\n" " a.f1 = fopen(name,mode);\n" " a.f2 = fopen(name,mode);\n" " fclose(a.f1);\n" " fclose(a.f2);\n" "}"); ASSERT_EQUALS("", errout.str()); // #1473 check("void foo() {\n" " FILE *a = fopen(\"aa\", \"r\");\n" " while (fclose(a)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n", errout.str()); // #6823 check("void foo() {\n" " FILE f[2];\n" " f[0] = fopen(\"1\", \"w\");\n" " f[1] = fopen(\"2\", \"w\");\n" " fclose(f[0]);\n" " fclose(f[1]);\n" "}"); ASSERT_EQUALS("", errout.str()); } void fileIOwithoutPositioning() { check("void foo(FILE* f) {\n" " fwrite(buffer, 5, 6, f);\n" " fread(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); check("void foo(FILE* f, bool read) {\n" " if(read)\n" " fread(buffer, 5, 6, f);\n" " else\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " fflush(f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " rewind(f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " fsetpos(f, pos);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " fseek(f, 0, SEEK_SET);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(FILE* f) {\n" " fread(buffer, 5, 6, f);\n" " long pos = ftell(f);\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); // #6452 - member functions check("class FileStream {\n" " void insert(const ByteVector &data, ulong start);\n" " void seek(long offset, Position p);\n" " FileStreamPrivate *d;\n" "};\n" "void FileStream::insert(const ByteVector &data, ulong start) {\n" " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" " seek(writePosition);\n" " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class FileStream {\n" " void insert(const ByteVector &data, ulong start);\n" " FileStreamPrivate *d;\n" "};\n" "void FileStream::insert(const ByteVector &data, ulong start) {\n" " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" " unknown(writePosition);\n" " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class FileStream {\n" " void insert(const ByteVector &data, ulong start);\n" " FileStreamPrivate *d;\n" "};\n" "void known(int);\n" "void FileStream::insert(const ByteVector &data, ulong start) {\n" " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" " known(writePosition);\n" " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); check("class FileStream {\n" " void insert(const ByteVector &data, ulong start);\n" " FileStreamPrivate *d;\n" "};\n" "void known(int);\n" "void FileStream::insert(const ByteVector &data, ulong start) {\n" " int bytesRead = fread(X::data(), 1, bufferLength, d->file);\n" " known(writePosition);\n" " fwrite(X::data(), sizeof(char), buffer.size(), d->file);\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); } void seekOnAppendedFile() { check("void foo() {\n" " FILE* f = fopen(\"\", \"a+\");\n" " fseek(f, 0, SEEK_SET);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f = fopen(\"\", \"w\");\n" " fseek(f, 0, SEEK_SET);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " FILE* f = fopen(\"\", \"a\");\n" " fseek(f, 0, SEEK_SET);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Repositioning operation performed on a file opened in append mode has no effect.\n", errout.str()); check("void foo() {\n" " FILE* f = fopen(\"\", \"a\");\n" " fflush(f);\n" "}"); ASSERT_EQUALS("", errout.str()); // #5578 check("void foo() {\n" " FILE* f = fopen(\"\", \"a\");\n" " fclose(f);\n" " f = fopen(\"\", \"r\");\n" " fseek(f, 0, SEEK_SET);\n" "}"); ASSERT_EQUALS("", errout.str()); // #6566 } void fflushOnInputStream() { check("void foo()\n" "{\n" " fflush(stdin);\n" "}", false, true); ASSERT_EQUALS("[test.cpp:3]: (portability) fflush() called on input stream 'stdin' may result in undefined behaviour on non-linux systems.\n", errout.str()); check("void foo()\n" "{\n" " fflush(stdout);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(path, \"r\");\n" " fflush(f);\n" "}", false, true); ASSERT_EQUALS("[test.cpp:3]: (portability) fflush() called on input stream 'f' may result in undefined behaviour on non-linux systems.\n", errout.str()); check("void foo(FILE*& f) {\n" " f = fopen(path, \"w\");\n" " fflush(f);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); check("void foo(FILE*& f) {\n" " fflush(f);\n" "}", false, true); ASSERT_EQUALS("", errout.str()); } void testScanf1() { check("void foo() {\n" " int a, b;\n" " FILE *file = fopen(\"test\", \"r\");\n" " a = fscanf(file, \"aa %s\", bar);\n" " b = scanf(\"aa %S\", bar);\n" " b = scanf(\"aa %ls\", bar);\n" " sscanf(foo, \"%[^~]\", bar);\n" " scanf(\"%dx%s\", &b, bar);\n" " fclose(file);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) fscanf() without field width limits can crash with huge input data.\n" "[test.cpp:5]: (warning) scanf() without field width limits can crash with huge input data.\n" "[test.cpp:6]: (warning) scanf() without field width limits can crash with huge input data.\n" "[test.cpp:7]: (warning) sscanf() without field width limits can crash with huge input data.\n" "[test.cpp:8]: (warning) scanf() without field width limits can crash with huge input data.\n", errout.str()); } void testScanf2() { check("void foo() {\n" " scanf(\"%5s\", bar);\n" // Width specifier given " scanf(\"%5[^~]\", bar);\n" // Width specifier given " scanf(\"aa%%s\", bar);\n" // No %s " scanf(\"aa%d\", &a);\n" // No %s " scanf(\"aa%ld\", &a);\n" // No %s " scanf(\"%*[^~]\");\n" // Ignore input "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) scanf format string requires 0 parameters but 1 is given.\n", errout.str()); } void testScanf3() { // ticket #3494 check("void f() {\n" " char str[8];\n" " scanf(\"%7c\", str);\n" " scanf(\"%8c\", str);\n" " scanf(\"%9c\", str);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Width 9 given in format string (no. 1) is larger than destination buffer 'str[8]', use %8c to prevent overflowing it.\n", errout.str()); } void testScanf4() { // ticket #2553 check("void f()\n" "{\n" " char str [8];\n" " scanf (\"%70s\",str);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Width 70 given in format string (no. 1) is larger than destination buffer 'str[8]', use %7s to prevent overflowing it.\n", errout.str()); } #define TEST_SCANF_CODE(format, type)\ "void f(){" type " x; scanf(\"" format "\", &x);}" #define TEST_SCANF_ERR(format, formatStr, type)\ "[test.cpp:1]: (warning) " format " in format string (no. 1) requires '" formatStr " *' but the argument type is '" type " *'.\n" #define TEST_SCANF_ERR_AKA(format, formatStr, type, akaType)\ "[test.cpp:1]: (portability) " format " in format string (no. 1) requires '" formatStr " *' but the argument type is '" type " * {aka " akaType " *}'.\n" #define TEST_PRINTF_CODE(format, type)\ "void f(" type " x){printf(\"" format "\", x);}" #define TEST_PRINTF_ERR(format, requiredType, actualType)\ "[test.cpp:1]: (warning) " format " in format string (no. 1) requires '" requiredType "' but the argument type is '" actualType "'.\n" #define TEST_PRINTF_ERR_AKA(format, requiredType, actualType, akaType)\ "[test.cpp:1]: (portability) " format " in format string (no. 1) requires '" requiredType "' but the argument type is '" actualType " {aka " akaType "}'.\n" void testScanfNoWarn(const char *filename, unsigned int linenr, const char* code) { check(code, true, false, Settings::Unix32); assertEquals(filename, linenr, "", errout.str()); check(code, true, false, Settings::Unix64); assertEquals(filename, linenr, "", errout.str()); check(code, true, false, Settings::Win32A); assertEquals(filename, linenr, "", errout.str()); check(code, true, false, Settings::Win64); assertEquals(filename, linenr, "", errout.str()); } void testScanfWarn(const char *filename, unsigned int linenr, const char* code, const char* testScanfErrString) { check(code, true, false, Settings::Unix32); assertEquals(filename, linenr, testScanfErrString, errout.str()); check(code, true, false, Settings::Unix64); assertEquals(filename, linenr, testScanfErrString, errout.str()); check(code, true, false, Settings::Win32A); assertEquals(filename, linenr, testScanfErrString, errout.str()); check(code, true, false, Settings::Win64); assertEquals(filename, linenr, testScanfErrString, errout.str()); } void testScanfWarnAka(const char *filename, unsigned int linenr, const char* code, const char* testScanfErrAkaString, const char* testScanfErrAkaWin64String) { check(code, true, true, Settings::Unix32); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Unix64); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Win32A); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Win64); assertEquals(filename, linenr, testScanfErrAkaWin64String, errout.str()); } void testScanfWarnAkaWin64(const char *filename, unsigned int linenr, const char* code, const char* testScanfErrAkaWin64String) { check(code, true, true, Settings::Unix32); assertEquals(filename, linenr, "", errout.str()); check(code, true, true, Settings::Unix64); assertEquals(filename, linenr, "", errout.str()); check(code, true, true, Settings::Win32A); assertEquals(filename, linenr, "", errout.str()); check(code, true, true, Settings::Win64); assertEquals(filename, linenr, testScanfErrAkaWin64String, errout.str()); } void testScanfWarnAkaWin32(const char *filename, unsigned int linenr, const char* code, const char* testScanfErrAkaString) { check(code, true, true, Settings::Unix32); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Unix64); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Win32A); assertEquals(filename, linenr, testScanfErrAkaString, errout.str()); check(code, true, true, Settings::Win64); assertEquals(filename, linenr, "", errout.str()); } #define TEST_SCANF_NOWARN(FORMAT, FORMATSTR, TYPE) \ testScanfNoWarn(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE)) #define TEST_SCANF_WARN(FORMAT, FORMATSTR, TYPE) \ testScanfWarn(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR(FORMAT, FORMATSTR, TYPE)) #define TEST_SCANF_WARN_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE, AKATYPE_WIN64) \ testScanfWarnAka(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) #define TEST_SCANF_WARN_AKA_WIN64(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64) \ testScanfWarnAkaWin64(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) #define TEST_SCANF_WARN_AKA_WIN32(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32) \ testScanfWarnAkaWin32(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32)) // Macros for printf work just fine with scanf test functions. // TODO - invent better function names #define TEST_PRINTF_NOWARN(FORMAT, FORMATSTR, TYPE) \ testScanfNoWarn(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE)) #define TEST_PRINTF_WARN(FORMAT, FORMATSTR, TYPE) \ testScanfWarn(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR(FORMAT, FORMATSTR, TYPE)) #define TEST_PRINTF_WARN_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE, AKATYPE_WIN64) \ testScanfWarnAka(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) #define TEST_PRINTF_WARN_AKA_WIN64(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64) \ testScanfWarnAkaWin64(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) #define TEST_PRINTF_WARN_AKA_WIN32(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32) \ testScanfWarnAkaWin32(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32)) void testScanfArgument() { check("void foo() {\n" " scanf(\"%1d\", &foo);\n" " sscanf(bar, \"%1d\", &foo);\n" " scanf(\"%1u%1u\", &foo, bar());\n" " scanf(\"%*1x %1x %29s\", &count, KeyName);\n" // #3373 " fscanf(f, \"%7ms\", &ref);\n" // #3461 " sscanf(ip_port, \"%*[^:]:%4d\", &port);\n" // #3468 "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " scanf(\"\", &foo);\n" " scanf(\"%1d\", &foo, &bar);\n" " fscanf(bar, \"%1d\", &foo, &bar);\n" " scanf(\"%*1x %1x %29s\", &count, KeyName, foo);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) scanf format string requires 0 parameters but 1 is given.\n" "[test.cpp:3]: (warning) scanf format string requires 1 parameter but 2 are given.\n" "[test.cpp:4]: (warning) fscanf format string requires 1 parameter but 2 are given.\n" "[test.cpp:5]: (warning) scanf format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " scanf(\"%1d\");\n" " scanf(\"%1u%1u\", bar());\n" " sscanf(bar, \"%1d%1d\", &foo);\n" " scanf(\"%*1x %1x %29s\", &count);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) scanf format string requires 1 parameter but only 0 are given.\n" "[test.cpp:3]: (error) scanf format string requires 2 parameters but only 1 is given.\n" "[test.cpp:4]: (error) sscanf format string requires 2 parameters but only 1 is given.\n" "[test.cpp:5]: (error) scanf format string requires 2 parameters but only 1 is given.\n", errout.str()); check("void foo() {\n" " char input[10];\n" " char output[5];\n" " sscanf(input, \"%3s\", output);\n" " sscanf(input, \"%4s\", output);\n" " sscanf(input, \"%5s\", output);\n" "}", false); ASSERT_EQUALS("[test.cpp:6]: (error) Width 5 given in format string (no. 1) is larger than destination buffer 'output[5]', use %4s to prevent overflowing it.\n", errout.str()); check("void foo() {\n" " char input[10];\n" " char output[5];\n" " sscanf(input, \"%s\", output);\n" " sscanf(input, \"%3s\", output);\n" " sscanf(input, \"%4s\", output);\n" " sscanf(input, \"%5s\", output);\n" "}", true); ASSERT_EQUALS("[test.cpp:5]: (warning, inconclusive) Width 3 given in format string (no. 1) is smaller than destination buffer 'output[5]'.\n" "[test.cpp:7]: (error) Width 5 given in format string (no. 1) is larger than destination buffer 'output[5]', use %4s to prevent overflowing it.\n" "[test.cpp:4]: (warning) sscanf() without field width limits can crash with huge input data.\n", errout.str()); check("void foo() {\n" " const size_t BUFLENGTH(2048);\n" " typedef char bufT[BUFLENGTH];\n" " bufT line= {0};\n" " bufT projectId= {0};\n" " const int scanrc=sscanf(line, \"Project(\\\"{%36s}\\\")\", projectId);\n" " sscanf(input, \"%5s\", output);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int i) {\n" " scanf(\"%h\", &i);\n" " scanf(\"%hh\", &i);\n" " scanf(\"%l\", &i);\n" " scanf(\"%ll\", &i);\n" " scanf(\"%j\", &i);\n" " scanf(\"%z\", &i);\n" " scanf(\"%t\", &i);\n" " scanf(\"%L\", &i);\n" " scanf(\"%I\", &i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) 'h' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:3]: (warning) 'hh' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:4]: (warning) 'l' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:5]: (warning) 'll' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:6]: (warning) 'j' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:7]: (warning) 'z' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:10]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); // Unrecognized (and non-existent in standard library) specifiers. // Perhaps should emit warnings TEST_SCANF_NOWARN("%jb", "intmax_t", "intmax_t"); TEST_SCANF_NOWARN("%jw", "uintmax_t", "uintmax_t"); TEST_SCANF_NOWARN("%zr", "size_t", "size_t"); TEST_SCANF_NOWARN("%tm", "ptrdiff_t", "ptrdiff_t"); TEST_SCANF_NOWARN("%La", "long double", "long double"); TEST_SCANF_NOWARN("%zv", "std::size_t", "std::size_t"); TEST_SCANF_NOWARN("%tp", "std::ptrdiff_t", "std::ptrdiff_t"); TEST_SCANF_WARN("%u", "unsigned int", "bool"); TEST_SCANF_WARN("%u", "unsigned int", "char"); TEST_SCANF_WARN("%u", "unsigned int", "signed char"); TEST_SCANF_WARN("%u", "unsigned int", "unsigned char"); TEST_SCANF_WARN("%u", "unsigned int", "signed short"); TEST_SCANF_WARN("%u", "unsigned int", "unsigned short"); TEST_SCANF_WARN("%u", "unsigned int", "signed int"); TEST_SCANF_NOWARN("%u", "unsigned int", "unsigned int"); TEST_SCANF_WARN("%u", "unsigned int", "signed long"); TEST_SCANF_WARN("%u", "unsigned int", "unsigned long"); TEST_SCANF_WARN("%u", "unsigned int", "signed long long"); TEST_SCANF_WARN("%u", "unsigned int", "unsigned long long"); TEST_SCANF_WARN("%u", "unsigned int", "float"); TEST_SCANF_WARN("%u", "unsigned int", "double"); TEST_SCANF_WARN("%u", "unsigned int", "long double"); TEST_SCANF_WARN("%u", "unsigned int", "void *"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%u", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%u\", \"s3\");\n" " scanf(\"%u\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(long l) {\n" " scanf(\"%u\", l);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'signed long'.\n", errout.str()); TEST_SCANF_WARN("%lu","unsigned long","bool"); TEST_SCANF_WARN("%lu","unsigned long","char"); TEST_SCANF_WARN("%lu","unsigned long","signed char"); TEST_SCANF_WARN("%lu","unsigned long","unsigned char"); TEST_SCANF_WARN("%lu","unsigned long","signed short"); TEST_SCANF_WARN("%lu","unsigned long","unsigned short"); TEST_SCANF_WARN("%lu","unsigned long","signed int"); TEST_SCANF_WARN("%lu","unsigned long","unsigned int"); TEST_SCANF_WARN("%lu","unsigned long","signed long"); TEST_SCANF_NOWARN("%lu","unsigned long","unsigned long"); TEST_SCANF_WARN("%lu","unsigned long","signed long long"); TEST_SCANF_WARN("%lu","unsigned long","unsigned long long"); TEST_SCANF_WARN("%lu","unsigned long","float"); TEST_SCANF_WARN("%lu","unsigned long","double"); TEST_SCANF_WARN("%lu","unsigned long","long double"); TEST_SCANF_WARN("%lu","unsigned long","void *"); TEST_SCANF_WARN_AKA("%lu","unsigned long","size_t","unsigned long","unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","uintmax_t","unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%lu","unsigned long","uintptr_t", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::size_t","unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lu","unsigned long","std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%lu","unsigned long","std::uintptr_t", "unsigned long long"); TEST_SCANF_WARN("%lx","unsigned long","bool"); TEST_SCANF_WARN("%lx","unsigned long","char"); TEST_SCANF_WARN("%lx","unsigned long","signed char"); TEST_SCANF_WARN("%lx","unsigned long","unsigned char"); TEST_SCANF_WARN("%lx","unsigned long","signed short"); TEST_SCANF_WARN("%lx","unsigned long","unsigned short"); TEST_SCANF_WARN("%lx","unsigned long","signed int"); TEST_SCANF_WARN("%lx","unsigned long","unsigned int"); TEST_SCANF_WARN("%lx","unsigned long","signed long"); TEST_SCANF_NOWARN("%lx","unsigned long","unsigned long"); TEST_SCANF_WARN("%lx","unsigned long","signed long long"); TEST_SCANF_WARN("%lx","unsigned long","unsigned long long"); TEST_SCANF_WARN("%lx","unsigned long","float"); TEST_SCANF_WARN("%lx","unsigned long","double"); TEST_SCANF_WARN("%lx","unsigned long","long double"); TEST_SCANF_WARN("%lx","unsigned long","void *"); TEST_SCANF_WARN_AKA("%lx","unsigned long","size_t","unsigned long","unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","uintmax_t","unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%lx","unsigned long","uintptr_t", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::size_t","unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lx","unsigned long","std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%lx","unsigned long","std::uintptr_t", "unsigned long long"); TEST_SCANF_WARN("%ld","long","bool"); TEST_SCANF_WARN("%ld","long","char"); TEST_SCANF_NOWARN("%ld","long","signed long"); TEST_SCANF_WARN("%ld","long","unsigned long"); TEST_SCANF_WARN("%ld","long","void *"); TEST_SCANF_WARN_AKA("%ld","long","size_t","unsigned long","unsigned long long"); TEST_SCANF_WARN_AKA("%ld","long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ld","long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ld","long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN64("%ld","long","std::intptr_t", "signed long long"); TEST_SCANF_WARN_AKA("%ld","long","std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%llu","unsigned long long","bool"); TEST_SCANF_WARN("%llu","unsigned long long","char"); TEST_SCANF_WARN("%llu","unsigned long long","signed char"); TEST_SCANF_WARN("%llu","unsigned long long","unsigned char"); TEST_SCANF_WARN("%llu","unsigned long long","signed short"); TEST_SCANF_WARN("%llu","unsigned long long","unsigned short"); TEST_SCANF_WARN("%llu","unsigned long long","signed int"); TEST_SCANF_WARN("%llu","unsigned long long","unsigned int"); TEST_SCANF_WARN("%llu","unsigned long long","signed long"); TEST_SCANF_WARN("%llu","unsigned long long","unsigned long"); TEST_SCANF_WARN("%llu","unsigned long long","signed long long"); TEST_SCANF_NOWARN("%llu","unsigned long long","unsigned long long"); TEST_SCANF_WARN("%llu","unsigned long long","float"); TEST_SCANF_WARN("%llu","unsigned long long","double"); TEST_SCANF_WARN("%llu","unsigned long long","long double"); TEST_SCANF_WARN("%llu","unsigned long long","void *"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%llu","unsigned long long","uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llu","unsigned long long","std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%llu","unsigned long long","std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%llx","unsigned long long","bool"); TEST_SCANF_WARN("%llx","unsigned long long","char"); TEST_SCANF_WARN("%llx","unsigned long long","signed char"); TEST_SCANF_WARN("%llx","unsigned long long","unsigned char"); TEST_SCANF_WARN("%llx","unsigned long long","signed short"); TEST_SCANF_WARN("%llx","unsigned long long","unsigned short"); TEST_SCANF_WARN("%llx","unsigned long long","signed int"); TEST_SCANF_WARN("%llx","unsigned long long","unsigned int"); TEST_SCANF_WARN("%llx","unsigned long long","signed long"); TEST_SCANF_WARN("%llx","unsigned long long","unsigned long"); TEST_SCANF_WARN("%llx","unsigned long long","signed long long"); TEST_SCANF_NOWARN("%llx","unsigned long long","unsigned long long"); TEST_SCANF_WARN("%llx","unsigned long long","float"); TEST_SCANF_WARN("%llx","unsigned long long","double"); TEST_SCANF_WARN("%llx","unsigned long long","long double"); TEST_SCANF_WARN("%llx","unsigned long long","void *"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%llx","unsigned long long","uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%llx","unsigned long long","std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%llx","unsigned long long","std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%lld","long long","bool"); TEST_SCANF_WARN("%lld","long long","char"); TEST_SCANF_NOWARN("%lld","long long","long long"); TEST_SCANF_WARN("%lld","long long","unsigned long long"); TEST_SCANF_WARN("%lld","long long","void *"); TEST_SCANF_WARN_AKA("%lld","long long","size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lld","long long","intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lld","long long","std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lld","long long","std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%lld","long long","std::intptr_t", "signed long"); TEST_SCANF_WARN("%hu", "unsigned short", "bool"); TEST_SCANF_WARN("%hu", "unsigned short", "char"); TEST_SCANF_WARN("%hu", "unsigned short", "signed char"); TEST_SCANF_WARN("%hu", "unsigned short", "unsigned char"); TEST_SCANF_WARN("%hu", "unsigned short", "signed short"); TEST_SCANF_NOWARN("%hu", "unsigned short", "unsigned short"); TEST_SCANF_WARN("%hu", "unsigned short", "signed int"); TEST_SCANF_WARN("%hu", "unsigned short", "unsigned int"); TEST_SCANF_WARN("%hu", "unsigned short", "signed long"); TEST_SCANF_WARN("%hu", "unsigned short", "unsigned long"); TEST_SCANF_WARN("%hu", "unsigned short", "signed long long"); TEST_SCANF_WARN("%hu", "unsigned short", "unsigned long long"); TEST_SCANF_WARN("%hu", "unsigned short", "float"); TEST_SCANF_WARN("%hu", "unsigned short", "double"); TEST_SCANF_WARN("%hu", "unsigned short", "long double"); TEST_SCANF_WARN("%hu", "unsigned short", "void *"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hu", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%hx", "unsigned short", "bool"); TEST_SCANF_WARN("%hx", "unsigned short", "char"); TEST_SCANF_WARN("%hx", "unsigned short", "signed char"); TEST_SCANF_WARN("%hx", "unsigned short", "unsigned char"); TEST_SCANF_WARN("%hx", "unsigned short", "signed short"); TEST_SCANF_NOWARN("%hx", "unsigned short", "unsigned short"); TEST_SCANF_WARN("%hx", "unsigned short", "signed int"); TEST_SCANF_WARN("%hx", "unsigned short", "unsigned int"); TEST_SCANF_WARN("%hx", "unsigned short", "signed long"); TEST_SCANF_WARN("%hx", "unsigned short", "unsigned long"); TEST_SCANF_WARN("%hx", "unsigned short", "signed long long"); TEST_SCANF_WARN("%hx", "unsigned short", "unsigned long long"); TEST_SCANF_WARN("%hx", "unsigned short", "float"); TEST_SCANF_WARN("%hx", "unsigned short", "double"); TEST_SCANF_WARN("%hx", "unsigned short", "long double"); TEST_SCANF_WARN("%hx", "unsigned short", "void *"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hx", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%hd", "short", "bool"); TEST_SCANF_WARN("%hd", "short", "char"); TEST_SCANF_WARN("%hd", "short", "signed char"); TEST_SCANF_WARN("%hd", "short", "unsigned char"); TEST_SCANF_NOWARN("%hd", "short", "signed short"); TEST_SCANF_WARN("%hd", "short", "unsigned short"); TEST_SCANF_WARN("%hd", "short", "signed int"); TEST_SCANF_WARN("%hd", "short", "unsigned int"); TEST_SCANF_WARN("%hd", "short", "signed long"); TEST_SCANF_WARN("%hd", "short", "void *"); TEST_SCANF_WARN_AKA("%hd", "short", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hd", "short", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN("%hhu", "unsigned char", "bool"); TEST_SCANF_WARN("%hhu", "unsigned char", "char"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed char"); TEST_SCANF_NOWARN("%hhu", "unsigned char", "unsigned char"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed short"); TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned short"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed int"); TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned int"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed long"); TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned long"); TEST_SCANF_WARN("%hhu", "unsigned char", "signed long long"); TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned long long"); TEST_SCANF_WARN("%hhu", "unsigned char", "float"); TEST_SCANF_WARN("%hhu", "unsigned char", "double"); TEST_SCANF_WARN("%hhu", "unsigned char", "long double"); TEST_SCANF_WARN("%hhu", "unsigned char", "void *"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%hhx", "unsigned char", "bool"); TEST_SCANF_WARN("%hhx", "unsigned char", "char"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed char"); TEST_SCANF_NOWARN("%hhx", "unsigned char", "unsigned char"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed short"); TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned short"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed int"); TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned int"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed long"); TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned long"); TEST_SCANF_WARN("%hhx", "unsigned char", "signed long long"); TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned long long"); TEST_SCANF_WARN("%hhx", "unsigned char", "float"); TEST_SCANF_WARN("%hhx", "unsigned char", "double"); TEST_SCANF_WARN("%hhx", "unsigned char", "long double"); TEST_SCANF_WARN("%hhx", "unsigned char", "void *"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%hhd", "char", "bool"); TEST_SCANF_NOWARN("%hhd", "char", "char"); TEST_SCANF_NOWARN("%hhd", "char", "signed char"); TEST_SCANF_WARN("%hhd", "char", "unsigned char"); TEST_SCANF_WARN("%hhd", "char", "signed short"); TEST_SCANF_WARN("%hhd", "char", "void *"); TEST_SCANF_WARN_AKA("%hhd", "char", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Lu", "unsigned long long", "bool"); TEST_SCANF_WARN("%Lu", "unsigned long long", "char"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed char"); TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned char"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed short"); TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned short"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed int"); TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned int"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed long"); TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned long"); TEST_SCANF_WARN("%Lu", "unsigned long long", "signed long long"); TEST_SCANF_NOWARN("%Lu", "unsigned long long", "unsigned long long"); TEST_SCANF_WARN("%Lu", "unsigned long long", "float"); TEST_SCANF_WARN("%Lu", "unsigned long long", "double"); TEST_SCANF_WARN("%Lu", "unsigned long long", "long double"); TEST_SCANF_WARN("%Lu", "unsigned long long", "void *"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lu", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lu", "unsigned long long", "std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%Lx", "unsigned long long", "bool"); TEST_SCANF_WARN("%Lx", "unsigned long long", "char"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed char"); TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned char"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed short"); TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned short"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed int"); TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned int"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed long"); TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned long"); TEST_SCANF_WARN("%Lx", "unsigned long long", "signed long long"); TEST_SCANF_NOWARN("%Lx", "unsigned long long", "unsigned long long"); TEST_SCANF_WARN("%Lx", "unsigned long long", "float"); TEST_SCANF_WARN("%Lx", "unsigned long long", "double"); TEST_SCANF_WARN("%Lx", "unsigned long long", "long double"); TEST_SCANF_WARN("%Lx", "unsigned long long", "void *"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lx", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lx", "unsigned long long", "uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%Lx", "unsigned long long", "std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%Ld", "long long", "bool"); TEST_SCANF_WARN("%Ld", "long long", "char"); TEST_SCANF_WARN("%Ld", "long long", "signed char"); TEST_SCANF_WARN("%Ld", "long long", "unsigned char"); TEST_SCANF_WARN("%Ld", "long long", "signed short"); TEST_SCANF_WARN("%Ld", "long long", "unsigned short"); TEST_SCANF_WARN("%Ld", "long long", "signed int"); TEST_SCANF_WARN("%Ld", "long long", "unsigned int"); TEST_SCANF_WARN("%Ld", "long long", "signed long"); TEST_SCANF_WARN("%Ld", "long long", "unsigned long"); TEST_SCANF_NOWARN("%Ld", "long long", "signed long long"); TEST_SCANF_WARN("%Ld", "long long", "unsigned long long"); TEST_SCANF_WARN("%Ld", "long long", "float"); TEST_SCANF_WARN("%Ld", "long long", "double"); TEST_SCANF_WARN("%Ld", "long long", "long double"); TEST_SCANF_WARN("%Ld", "long long", "void *"); TEST_SCANF_WARN_AKA("%Ld", "long long", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "ssize_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "ptrdiff_t", "signed long"); TEST_SCANF_WARN_AKA("%Ld", "long long", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "intmax_t", "signed long"); TEST_SCANF_WARN_AKA("%Ld", "long long", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Ld", "long long", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "std::ssize_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "std::ptrdiff_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "std::intptr_t", "signed long"); TEST_SCANF_WARN_AKA("%Ld", "long long", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%Ld\", \"s3\");\n" " scanf(\"%Ld\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(int i) {\n" " scanf(\"%Ld\", i);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'signed int'.\n", errout.str()); TEST_SCANF_WARN("%ju", "uintmax_t", "bool"); TEST_SCANF_WARN("%ju", "uintmax_t", "char"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed char"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned char"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed short"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned short"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed int"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned int"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed long"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned long"); TEST_SCANF_WARN("%ju", "uintmax_t", "signed long long"); TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned long long"); TEST_SCANF_WARN("%ju", "uintmax_t", "float"); TEST_SCANF_WARN("%ju", "uintmax_t", "double"); TEST_SCANF_WARN("%ju", "uintmax_t", "long double"); TEST_SCANF_WARN("%ju", "uintmax_t", "void *"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%ju", "uintmax_t", "uintmax_t"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%ju", "uintmax_t", "std::uintmax_t"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%jx", "uintmax_t", "bool"); TEST_SCANF_WARN("%jx", "uintmax_t", "char"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed char"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned char"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed short"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned short"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed int"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned int"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed long"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned long"); TEST_SCANF_WARN("%jx", "uintmax_t", "signed long long"); TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned long long"); TEST_SCANF_WARN("%jx", "uintmax_t", "float"); TEST_SCANF_WARN("%jx", "uintmax_t", "double"); TEST_SCANF_WARN("%jx", "uintmax_t", "long double"); TEST_SCANF_WARN("%jx", "uintmax_t", "void *"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%jx", "uintmax_t", "uintmax_t"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%jx", "uintmax_t", "std::uintmax_t"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%jd", "intmax_t", "long double"); TEST_SCANF_WARN("%jd", "intmax_t", "void *"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%jd", "intmax_t", "intmax_t"); TEST_SCANF_WARN_AKA("%jd", "intmax_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%jd", "intmax_t", "std::intmax_t"); TEST_SCANF_WARN("%zu", "size_t", "bool"); TEST_SCANF_WARN("%zu", "size_t", "char"); TEST_SCANF_WARN("%zu", "size_t", "signed char"); TEST_SCANF_WARN("%zu", "size_t", "unsigned char"); TEST_SCANF_WARN("%zu", "size_t", "signed short"); TEST_SCANF_WARN("%zu", "size_t", "unsigned short"); TEST_SCANF_WARN("%zu", "size_t", "signed int"); TEST_SCANF_WARN("%zu", "size_t", "unsigned int"); TEST_SCANF_WARN("%zu", "size_t", "signed long"); TEST_SCANF_WARN("%zu", "size_t", "unsigned long"); TEST_SCANF_WARN("%zu", "size_t", "signed long long"); TEST_SCANF_WARN("%zu", "size_t", "unsigned long long"); TEST_SCANF_WARN("%zu", "size_t", "float"); TEST_SCANF_WARN("%zu", "size_t", "double"); TEST_SCANF_WARN("%zu", "size_t", "long double"); TEST_SCANF_WARN("%zu", "size_t", "void *"); TEST_SCANF_NOWARN("%zu", "size_t", "size_t"); TEST_SCANF_WARN_AKA("%zu", "size_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%zu", "size_t", "std::size_t"); TEST_SCANF_WARN_AKA("%zu", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%zx", "size_t", "bool"); TEST_SCANF_WARN("%zx", "size_t", "char"); TEST_SCANF_WARN("%zx", "size_t", "signed char"); TEST_SCANF_WARN("%zx", "size_t", "unsigned char"); TEST_SCANF_WARN("%zx", "size_t", "signed short"); TEST_SCANF_WARN("%zx", "size_t", "unsigned short"); TEST_SCANF_WARN("%zx", "size_t", "signed int"); TEST_SCANF_WARN("%zx", "size_t", "unsigned int"); TEST_SCANF_WARN("%zx", "size_t", "signed long"); TEST_SCANF_WARN("%zx", "size_t", "unsigned long"); TEST_SCANF_WARN("%zx", "size_t", "signed long long"); TEST_SCANF_WARN("%zx", "size_t", "unsigned long long"); TEST_SCANF_WARN("%zx", "size_t", "float"); TEST_SCANF_WARN("%zx", "size_t", "double"); TEST_SCANF_WARN("%zx", "size_t", "long double"); TEST_SCANF_WARN("%zx", "size_t", "void *"); TEST_SCANF_NOWARN("%zx", "size_t", "size_t"); TEST_SCANF_WARN_AKA("%zx", "size_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%zx", "size_t", "std::size_t"); TEST_SCANF_WARN_AKA("%zx", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%zx", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%zd", "ssize_t", "bool"); TEST_SCANF_WARN("%zd", "ssize_t", "signed short"); TEST_SCANF_WARN("%zd", "ssize_t", "void *"); TEST_SCANF_WARN_AKA("%zd", "ssize_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%zd", "ssize_t", "ssize_t"); TEST_SCANF_WARN_AKA("%zd", "ssize_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "bool"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "char"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed char"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned char"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed short"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned short"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed int"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned int"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed long long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long long"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "float"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "double"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "long double"); TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "void *"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%tu", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "bool"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "char"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed char"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned char"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed short"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned short"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed int"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned int"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed long long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long long"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "float"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "double"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "long double"); TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "void *"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%tx", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%td", "ptrdiff_t", "long double"); TEST_SCANF_WARN("%td", "ptrdiff_t", "void *"); TEST_SCANF_NOWARN("%td", "ptrdiff_t", "ptrdiff_t"); TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Iu", "size_t", "bool"); TEST_SCANF_WARN("%Iu", "size_t", "char"); TEST_SCANF_WARN("%Iu", "size_t", "signed char"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned char"); TEST_SCANF_WARN("%Iu", "size_t", "signed short"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned short"); TEST_SCANF_WARN("%Iu", "size_t", "signed int"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned int"); TEST_SCANF_WARN("%Iu", "size_t", "signed long"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned long"); TEST_SCANF_WARN("%Iu", "size_t", "signed long long"); TEST_SCANF_WARN("%Iu", "size_t", "unsigned long long"); TEST_SCANF_WARN("%Iu", "size_t", "float"); TEST_SCANF_WARN("%Iu", "size_t", "double"); TEST_SCANF_WARN("%Iu", "size_t", "long double"); TEST_SCANF_WARN("%Iu", "size_t", "void *"); TEST_SCANF_NOWARN("%Iu", "size_t", "size_t"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%Iu", "size_t", "std::size_t"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Iu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Ix", "size_t", "bool"); TEST_SCANF_WARN("%Ix", "size_t", "char"); TEST_SCANF_WARN("%Ix", "size_t", "signed char"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned char"); TEST_SCANF_WARN("%Ix", "size_t", "signed short"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned short"); TEST_SCANF_WARN("%Ix", "size_t", "signed int"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned int"); TEST_SCANF_WARN("%Ix", "size_t", "signed long"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned long"); TEST_SCANF_WARN("%Ix", "size_t", "signed long long"); TEST_SCANF_WARN("%Ix", "size_t", "unsigned long long"); TEST_SCANF_WARN("%Ix", "size_t", "float"); TEST_SCANF_WARN("%Ix", "size_t", "double"); TEST_SCANF_WARN("%Ix", "size_t", "long double"); TEST_SCANF_WARN("%Ix", "size_t", "void *"); TEST_SCANF_NOWARN("%Ix", "size_t", "size_t"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_NOWARN("%Ix", "size_t", "std::size_t"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Ix", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "bool"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "char"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed char"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned char"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed short"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned short"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed int"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned int"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed long long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned long long"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "float"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "double"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "long double"); TEST_SCANF_WARN("%Id", "ptrdiff_t", "void *"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "ssize_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%Id", "ptrdiff_t", "ptrdiff_t"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%Id", "ptrdiff_t", "std::ptrdiff_t"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "bool"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "char"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed char"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned char"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed short"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned short"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed int"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned int"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed long"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned long"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed long long"); TEST_SCANF_NOWARN("%I64u", "unsigned __int64", "unsigned long long"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "float"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "double"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "long double"); TEST_SCANF_WARN("%I64u", "unsigned __int64", "void *"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "size_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintmax_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::size_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::uintmax_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "bool"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "char"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed char"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned char"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed short"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned short"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed int"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned int"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed long"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned long"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed long long"); TEST_SCANF_NOWARN("%I64x", "unsigned __int64", "unsigned long long"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "float"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "double"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "long double"); TEST_SCANF_WARN("%I64x", "unsigned __int64", "void *"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "size_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%I64x", "unsigned __int64", "unsigned __int64"); // TODO TEST_SCANF_WARN("%I64x", "unsigned __int64", "__int64"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintmax_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintptr_t", "unsigned long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::size_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::uintmax_t", "unsigned long"); TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::uintptr_t", "unsigned long"); TEST_SCANF_WARN("%I64d", "__int64", "bool"); TEST_SCANF_WARN("%I64d", "__int64", "signed char"); TEST_SCANF_WARN("%I64d", "__int64", "unsigned char"); TEST_SCANF_WARN("%I64d", "__int64", "void *"); // TODO TEST_SCANF_WARN("%I64d", "__int64", "size_t"); TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "intmax_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "ssize_t", "signed long"); TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "ptrdiff_t", "signed long"); TEST_SCANF_NOWARN("%I64d", "__int64", "__int64"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "bool"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "char"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed char"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned char"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed short"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned short"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed int"); TEST_SCANF_NOWARN("%I32u", "unsigned __int32", "unsigned int"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed long"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned long"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed long long"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned long long"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "float"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "double"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "long double"); TEST_SCANF_WARN("%I32u", "unsigned __int32", "void *"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "bool"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "char"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed char"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned char"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed short"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned short"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed int"); TEST_SCANF_NOWARN("%I32x", "unsigned __int32", "unsigned int"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed long long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned long long"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "float"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "double"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "long double"); TEST_SCANF_WARN("%I32x", "unsigned __int32", "void *"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%I32d", "__int32", "bool"); TEST_SCANF_WARN("%I32d", "__int32", "void *"); TEST_SCANF_WARN_AKA("%I32d", "__int32", "size_t", "unsigned long", "unsigned long long"); //TODO TEST_SCANF_WARN_AKA_WIN32("%I32d", "__int32", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%I32d", "__int32", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_NOWARN("%I32d", "__int32", "__int32"); TEST_SCANF_WARN("%d", "int", "bool"); TEST_SCANF_WARN("%d", "int", "char"); TEST_SCANF_WARN("%d", "int", "signed char"); TEST_SCANF_WARN("%d", "int", "unsigned char"); TEST_SCANF_WARN("%d", "int", "signed short"); TEST_SCANF_WARN("%d", "int", "unsigned short"); TEST_SCANF_NOWARN("%d", "int", "signed int"); TEST_SCANF_WARN("%d", "int", "unsigned int"); TEST_SCANF_WARN("%d", "int", "signed long"); TEST_SCANF_WARN("%d", "int", "unsigned long"); TEST_SCANF_WARN("%d", "int", "signed long long"); TEST_SCANF_WARN("%d", "int", "unsigned long long"); TEST_SCANF_WARN("%d", "int", "float"); TEST_SCANF_WARN("%d", "int", "double"); TEST_SCANF_WARN("%d", "int", "long double"); TEST_SCANF_WARN("%d", "int", "void *"); TEST_SCANF_WARN_AKA("%d", "int", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%d", "int", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%d", "int", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%d", "int", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%d\", \"s3\");\n" " scanf(\"%d\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(long l) {\n" " scanf(\"%d\", l);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'signed long'.\n", errout.str()); TEST_SCANF_WARN("%x", "unsigned int", "bool"); TEST_SCANF_WARN("%x", "unsigned int", "char"); TEST_SCANF_WARN("%x", "unsigned int", "signed char"); TEST_SCANF_WARN("%x", "unsigned int", "unsigned char"); TEST_SCANF_WARN("%x", "unsigned int", "signed short"); TEST_SCANF_WARN("%x", "unsigned int", "unsigned short"); TEST_SCANF_WARN("%x", "unsigned int", "signed int"); TEST_SCANF_NOWARN("%x", "unsigned int", "unsigned int"); TEST_SCANF_WARN("%x", "unsigned int", "signed long"); TEST_SCANF_WARN("%x", "unsigned int", "unsigned long"); TEST_SCANF_WARN("%x", "unsigned int", "signed long long"); TEST_SCANF_WARN("%x", "unsigned int", "unsigned long long"); TEST_SCANF_WARN("%x", "unsigned int", "float"); TEST_SCANF_WARN("%x", "unsigned int", "double"); TEST_SCANF_WARN("%x", "unsigned int", "long double"); TEST_SCANF_WARN("%x", "unsigned int", "void *"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%x", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%x\", \"s3\");\n" " scanf(\"%x\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(long l) {\n" " scanf(\"%x\", l);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'signed long'.\n", errout.str()); TEST_SCANF_WARN("%f", "float", "bool"); TEST_SCANF_WARN("%f", "float", "char"); TEST_SCANF_WARN("%f", "float", "signed char"); TEST_SCANF_WARN("%f", "float", "unsigned char"); TEST_SCANF_WARN("%f", "float", "signed short"); TEST_SCANF_WARN("%f", "float", "unsigned short"); TEST_SCANF_WARN("%f", "float", "signed int"); TEST_SCANF_WARN("%f", "float", "unsigned int"); TEST_SCANF_WARN("%f", "float", "signed long"); TEST_SCANF_WARN("%f", "float", "unsigned long"); TEST_SCANF_WARN("%f", "float", "signed long long"); TEST_SCANF_WARN("%f", "float", "unsigned long long"); TEST_SCANF_NOWARN("%f", "float", "float"); TEST_SCANF_WARN("%f", "float", "double"); TEST_SCANF_WARN("%f", "float", "long double"); TEST_SCANF_WARN("%f", "float", "void *"); TEST_SCANF_WARN_AKA("%f", "float", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%f", "float", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%f", "float", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%f", "float", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%f\", \"s3\");\n" " scanf(\"%f\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(float f) {\n" " scanf(\"%f\", f);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'float'.\n", errout.str()); TEST_SCANF_WARN("%lf", "double", "bool"); TEST_SCANF_WARN("%lf", "double", "char"); TEST_SCANF_WARN("%lf", "double", "signed char"); TEST_SCANF_WARN("%lf", "double", "unsigned char"); TEST_SCANF_WARN("%lf", "double", "signed short"); TEST_SCANF_WARN("%lf", "double", "unsigned short"); TEST_SCANF_WARN("%lf", "double", "signed int"); TEST_SCANF_WARN("%lf", "double", "unsigned int"); TEST_SCANF_WARN("%lf", "double", "signed long"); TEST_SCANF_WARN("%lf", "double", "unsigned long"); TEST_SCANF_WARN("%lf", "double", "signed long long"); TEST_SCANF_WARN("%lf", "double", "unsigned long long"); TEST_SCANF_WARN("%lf", "double", "float"); TEST_SCANF_NOWARN("%lf", "double", "double"); TEST_SCANF_WARN("%lf", "double", "long double"); TEST_SCANF_WARN("%lf", "double", "void *"); TEST_SCANF_WARN_AKA("%lf", "double", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lf", "double", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lf", "double", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%lf", "double", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%Lf", "long double", "bool"); TEST_SCANF_WARN("%Lf", "long double", "char"); TEST_SCANF_WARN("%Lf", "long double", "signed char"); TEST_SCANF_WARN("%Lf", "long double", "unsigned char"); TEST_SCANF_WARN("%Lf", "long double", "signed short"); TEST_SCANF_WARN("%Lf", "long double", "unsigned short"); TEST_SCANF_WARN("%Lf", "long double", "signed int"); TEST_SCANF_WARN("%Lf", "long double", "unsigned int"); TEST_SCANF_WARN("%Lf", "long double", "signed long"); TEST_SCANF_WARN("%Lf", "long double", "unsigned long"); TEST_SCANF_WARN("%Lf", "long double", "signed long long"); TEST_SCANF_WARN("%Lf", "long double", "unsigned long long"); TEST_SCANF_WARN("%Lf", "long double", "float"); TEST_SCANF_WARN("%Lf", "long double", "double"); TEST_SCANF_NOWARN("%Lf", "long double", "long double"); TEST_SCANF_WARN("%Lf", "long double", "void *"); TEST_SCANF_WARN_AKA("%Lf", "long double", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%Lf", "long double", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN("%n", "int", "bool"); TEST_SCANF_WARN("%n", "int", "char"); TEST_SCANF_WARN("%n", "int", "signed char"); TEST_SCANF_WARN("%n", "int", "unsigned char"); TEST_SCANF_WARN("%n", "int", "signed short"); TEST_SCANF_WARN("%n", "int", "unsigned short"); TEST_SCANF_NOWARN("%n", "int", "signed int"); TEST_SCANF_WARN("%n", "int", "unsigned int"); TEST_SCANF_WARN("%n", "int", "signed long"); TEST_SCANF_WARN("%n", "int", "unsigned long"); TEST_SCANF_WARN("%n", "int", "signed long long"); TEST_SCANF_WARN("%n", "int", "unsigned long long"); TEST_SCANF_WARN("%n", "int", "float"); TEST_SCANF_WARN("%n", "int", "double"); TEST_SCANF_WARN("%n", "int", "long double"); TEST_SCANF_WARN("%n", "int", "void *"); TEST_SCANF_WARN_AKA("%n", "int", "size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%n", "int", "ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%n", "int", "intmax_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::size_t", "unsigned long", "unsigned long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::ssize_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::intptr_t", "signed long", "signed long long"); TEST_SCANF_WARN_AKA("%n", "int", "std::uintptr_t", "unsigned long", "unsigned long long"); check("void foo() {\n" " scanf(\"%n\", \"s3\");\n" " scanf(\"%n\", L\"s5W\");\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("void foo(long l) {\n" " scanf(\"%n\", l);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed long'.\n", errout.str()); check("void g() {\n" // #5104 " myvector v1(1);\n" " scanf(\"%d\",&v1[0]);\n" " myvector v2(1);\n" " scanf(\"%u\",&v2[0]);\n" " myvector v3(1);\n" " scanf(\"%x\",&v3[0]);\n" " myvector v4(1);\n" " scanf(\"%lf\",&v4[0]);\n" " myvector v5(1);\n" " scanf(\"%10s\",v5[0]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); { const char * code = "void g() {\n" // #5348 " size_t s1;\n" " ptrdiff_t s2;\n" " ssize_t s3;\n" " scanf(\"%zd\", &s1);\n" " scanf(\"%zd\", &s2);\n" " scanf(\"%zd\", &s3);\n" "}\n"; const char* result("[test.cpp:5]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:6]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n"); const char* result_win64("[test.cpp:5]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:6]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n"); check(code, false, true, Settings::Unix32); ASSERT_EQUALS(result, errout.str()); check(code, false, true, Settings::Unix64); ASSERT_EQUALS(result, errout.str()); check(code, false, true, Settings::Win32A); ASSERT_EQUALS(result, errout.str()); check(code, false, true, Settings::Win64); ASSERT_EQUALS(result_win64, errout.str()); } { check("void g() {\n" " const char c[]=\"42\";\n" " scanf(\"%s\", c);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires a 'char *' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) scanf() without field width limits can crash with huge input data.\n", errout.str()); } } void testPrintfArgument() { check("void foo() {\n" " printf(\"%i\");\n" " printf(\"%i%s\", 123);\n" " printf(\"%i%s%d\", 0, bar());\n" " printf(\"%i%%%s%d\", 0, bar());\n" " printf(\"%idfd%%dfa%s%d\", 0, bar());\n" " fprintf(stderr,\"%u%s\");\n" " snprintf(str,10,\"%u%s\");\n" " sprintf(string1, \"%-*.*s\", 32, string2);\n" // #3364 " snprintf(a, 9, \"%s%d\", \"11223344\");\n" // #3655 "}"); ASSERT_EQUALS("[test.cpp:2]: (error) printf format string requires 1 parameter but only 0 are given.\n" "[test.cpp:3]: (error) printf format string requires 2 parameters but only 1 is given.\n" "[test.cpp:4]: (error) printf format string requires 3 parameters but only 2 are given.\n" "[test.cpp:5]: (error) printf format string requires 3 parameters but only 2 are given.\n" "[test.cpp:6]: (error) printf format string requires 3 parameters but only 2 are given.\n" "[test.cpp:7]: (error) fprintf format string requires 2 parameters but only 0 are given.\n" "[test.cpp:8]: (error) snprintf format string requires 2 parameters but only 0 are given.\n" "[test.cpp:9]: (error) sprintf format string requires 3 parameters but only 2 are given.\n" "[test.cpp:10]: (error) snprintf format string requires 2 parameters but only 1 is given.\n", errout.str()); check("void foo(char *str) {\n" " printf(\"\", 0);\n" " printf(\"%i\", 123, bar());\n" " printf(\"%i%s\", 0, bar(), 43123);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) printf format string requires 0 parameters but 1 is given.\n" "[test.cpp:3]: (warning) printf format string requires 1 parameter but 2 are given.\n" "[test.cpp:4]: (warning) printf format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" // swprintf exists as MSVC extension and as standard function: #4790 " swprintf(string1, L\"%i\", 32, string2);\n" // MSVC implementation " swprintf(string1, L\"%s%s\", L\"a\", string2);\n" // MSVC implementation " swprintf(string1, 6, L\"%i\", 32, string2);\n" // Standard implementation " swprintf(string1, 6, L\"%i%s\", 32, string2);\n" // Standard implementation "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) swprintf format string requires 1 parameter but 2 are given.\n" "[test.cpp:4]: (warning) swprintf format string requires 1 parameter but 2 are given.\n", errout.str()); check("void foo(char *str) {\n" " printf(\"%i\", 0);\n" " printf(\"%i%s\", 123, bar());\n" " printf(\"%i%s%d\", 0, bar(), 43123);\n" " printf(\"%i%%%s%d\", 0, bar(), 43123);\n" " printf(\"%idfd%%dfa%s%d\", 0, bar(), 43123);\n" " printf(\"%\"PRId64\"\", 123);\n" " fprintf(stderr,\"%\"PRId64\"\", 123);\n" " snprintf(str,10,\"%\"PRId64\"\", 123);\n" " fprintf(stderr, \"error: %m\");\n" // #3339 " printf(\"string: %.*s\", len, string);\n" // #3311 " fprintf(stderr, \"%*cText.\", indent, ' ');\n" // #3313 " sprintf(string1, \"%*\", 32);\n" // #3364 "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char* s, const char* s2, std::string s3, int i) {\n" " printf(\"%s%s\", s, s2);\n" " printf(\"%s\", i);\n" " printf(\"%i%s\", i, i);\n" " printf(\"%s\", s3);\n" " printf(\"%s\", \"s4\");\n" " printf(\"%u\", s);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'std::string'.\n" "[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n", errout.str()); check("void foo(char* s, const char* s2, std::string s3, int i) {\n" " printf(\"%jd\", s);\n" " printf(\"%ji\", s);\n" " printf(\"%ju\", s2);\n" " printf(\"%jo\", s3);\n" " printf(\"%jx\", i);\n" " printf(\"%jX\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'char *'.\n" "[test.cpp:3]: (warning) %ji in format string (no. 1) requires 'intmax_t' but the argument type is 'char *'.\n" "[test.cpp:4]: (warning) %ju in format string (no. 1) requires 'uintmax_t' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %jo in format string (no. 1) requires 'uintmax_t' but the argument type is 'std::string'.\n" "[test.cpp:6]: (warning) %jx in format string (no. 1) requires 'uintmax_t' but the argument type is 'signed int'.\n" "[test.cpp:7]: (warning) %jX in format string (no. 1) requires 'uintmax_t' but the argument type is 'signed int'.\n", errout.str()); check("void foo(uintmax_t uim, std::string s3, unsigned int ui, int i) {\n" " printf(\"%ju\", uim);\n" " printf(\"%ju\", ui);\n" " printf(\"%jd\", ui);\n" " printf(\"%jd\", s3);\n" " printf(\"%jd\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %ju in format string (no. 1) requires 'uintmax_t' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'std::string'.\n" "[test.cpp:6]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'signed int'.\n", errout.str()); check("void foo(const int* cpi, const int ci, int i, int* pi, std::string s) {\n" " printf(\"%n\", cpi);\n" " printf(\"%n\", ci);\n" " printf(\"%n\", i);\n" " printf(\"%n\", pi);\n" " printf(\"%n\", s);\n" " printf(\"%n\", \"s4\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed int'.\n" "[test.cpp:6]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'std::string'.\n" "[test.cpp:7]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n", errout.str()); check("void foo() {\n" " printf(\"%n\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, unsigned int u) {\n" " printf(\"%X\", f);\n" " printf(\"%c\", \"s4\");\n" " printf(\"%o\", d);\n" " printf(\"%x\", cpi);\n" " printf(\"%o\", b);\n" " printf(\"%X\", bp);\n" " printf(\"%X\", u);\n" " printf(\"%X\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %c in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %o in format string (no. 1) requires 'unsigned int' but the argument type is 'double'.\n" "[test.cpp:6]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const signed int *'.\n" "[test.cpp:8]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo(const char* cpc, char* pc) {\n" " printf(\"%x\", cpc);\n" " printf(\"%x\", pc);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" "[test.cpp:4]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n", errout.str()); check("class foo {};\n" "void foo() {\n" " printf(\"%x\", L\"s5W\");\n" " printf(\"%X\", L\"s5W\");\n" " printf(\"%c\", L\"s5W\");\n" " printf(\"%o\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" "[test.cpp:4]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" "[test.cpp:5]: (warning) %c in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" "[test.cpp:6]: (warning) %o in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, unsigned int u, unsigned char uc) {\n" " printf(\"%i\", f);\n" " printf(\"%d\", \"s4\");\n" " printf(\"%d\", d);\n" " printf(\"%d\", u);\n" " printf(\"%d\", cpi);\n" " printf(\"%i\", b);\n" " printf(\"%i\", bp);\n" " printf(\"%i\", uc);\n" // char is smaller than int, so there shouldn't be a problem "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'double'.\n" "[test.cpp:6]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:7]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const signed int *'.\n" "[test.cpp:9]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo() {\n" " printf(\"%i\", L\"s5W\");\n" " printf(\"%d\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'const wchar_t *'.\n" "[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, bool bo) {\n" " printf(\"%u\", f);\n" " printf(\"%u\", \"s4\");\n" " printf(\"%u\", d);\n" " printf(\"%u\", i);\n" " printf(\"%u\", cpi);\n" " printf(\"%u\", b);\n" " printf(\"%u\", bp);\n" " printf(\"%u\", bo);\n" // bool shouldn't have a negative sign "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'double'.\n" "[test.cpp:6]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const signed int *'.\n" "[test.cpp:9]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, bool bo) {\n" " printf(\"%u\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, char c) {\n" " printf(\"%p\", f);\n" " printf(\"%p\", c);\n" " printf(\"%p\", bp);\n" " printf(\"%p\", cpi);\n" " printf(\"%p\", b);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %p in format string (no. 1) requires an address but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %p in format string (no. 1) requires an address but the argument type is 'char'.\n", errout.str()); check("class foo {};\n" "void foo(char* pc, const char* cpc, wchar_t* pwc, const wchar_t* cpwc) {\n" " printf(\"%p\", pc);\n" " printf(\"%p\", cpc);\n" " printf(\"%p\", pwc);\n" " printf(\"%p\", cpwc);\n" " printf(\"%p\", \"s4\");\n" " printf(\"%p\", L\"s5W\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("class foo {};\n" "void foo(const int* cpi, foo f, bar b, bar* bp, double d) {\n" " printf(\"%e\", f);\n" " printf(\"%E\", \"s4\");\n" " printf(\"%f\", cpi);\n" " printf(\"%G\", bp);\n" " printf(\"%f\", d);\n" " printf(\"%f\", b);\n" " printf(\"%f\", (float)cpi);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const signed int *'.\n" "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'bar *'.\n", errout.str()); check("class foo {};\n" "void foo(const char* cpc, char* pc) {\n" " printf(\"%e\", cpc);\n" " printf(\"%E\", pc);\n" " printf(\"%f\", cpc);\n" " printf(\"%G\", pc);\n" " printf(\"%f\", pc);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'char *'.\n" "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'char *'.\n" "[test.cpp:7]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'char *'.\n", errout.str()); check("class foo {};\n" "void foo() {\n" " printf(\"%e\", L\"s5W\");\n" " printf(\"%E\", L\"s5W\");\n" " printf(\"%f\", L\"s5W\");\n" " printf(\"%G\", L\"s5W\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n", errout.str()); check("class foo;\n" "void foo(foo f) {\n" " printf(\"%u\", f);\n" " printf(\"%f\", f);\n" " printf(\"%p\", f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" "[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'foo'.\n" "[test.cpp:5]: (warning) %p in format string (no. 1) requires an address but the argument type is 'foo'.\n", errout.str()); // Ticket #4189 (Improve check (printf("%l") not detected)) tests (according to C99 7.19.6.1.7) // False positive tests check("void foo(signed char sc, unsigned char uc, short int si, unsigned short int usi) {\n" " printf(\"%hhx %hhd\", sc, uc);\n" " printf(\"%hd %hu\", si, usi);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hhx in format string (no. 1) requires 'unsigned char' but the argument type is 'signed char'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 2) requires 'char' but the argument type is 'unsigned char'.\n", errout.str()); check("void foo(long long int lli, unsigned long long int ulli, long int li, unsigned long int uli) {\n" " printf(\"%llo %llx\", lli, ulli);\n" " printf(\"%ld %lu\", li, uli);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(intmax_t im, uintmax_t uim, size_t s, ptrdiff_t p, long double ld, std::size_t ss, std::ptrdiff_t sp) {\n" " printf(\"%jd %jo\", im, uim);\n" " printf(\"%zx\", s);\n" " printf(\"%ti\", p);\n" " printf(\"%Lf\", ld);\n" " printf(\"%zx\", ss);\n" " printf(\"%ti\", sp);\n" "}"); ASSERT_EQUALS("", errout.str()); // Unrecognized (and non-existent in standard library) specifiers. // Perhaps should emit warnings check("void foo(intmax_t im, uintmax_t uim, size_t s, ptrdiff_t p, long double ld, std::size_t ss, std::ptrdiff_t sp) {\n" " printf(\"%jb %jw\", im, uim);\n" " printf(\"%zr\", s);\n" " printf(\"%tm\", p);\n" " printf(\"%La\", ld);\n" " printf(\"%zv\", ss);\n" " printf(\"%tp\", sp);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(long long l, ptrdiff_t p, std::ptrdiff_t sp) {\n" " printf(\"%td\", p);\n" " printf(\"%td\", sp);\n" " printf(\"%td\", l);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) %td in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'signed long long'.\n", errout.str()); check("void foo(int i, long double ld) {\n" " printf(\"%zx %zu\", i, ld);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %zx in format string (no. 1) requires 'size_t' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %zu in format string (no. 2) requires 'size_t' but the argument type is 'long double'.\n", errout.str()); check("void foo(unsigned int ui, long double ld) {\n" " printf(\"%zu %zx\", ui, ld);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %zu in format string (no. 1) requires 'size_t' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %zx in format string (no. 2) requires 'size_t' but the argument type is 'long double'.\n", errout.str()); check("void foo(int i, long double ld) {\n" " printf(\"%tx %tu\", i, ld);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %tx in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %tu in format string (no. 2) requires 'unsigned ptrdiff_t' but the argument type is 'long double'.\n", errout.str()); // False negative test check("void foo(unsigned int i) {\n" " printf(\"%h\", i);\n" " printf(\"%hh\", i);\n" " printf(\"%l\", i);\n" " printf(\"%ll\", i);\n" " printf(\"%j\", i);\n" " printf(\"%z\", i);\n" " printf(\"%t\", i);\n" " printf(\"%L\", i);\n" " printf(\"%I\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) 'h' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:3]: (warning) 'hh' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:4]: (warning) 'l' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:5]: (warning) 'll' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:6]: (warning) 'j' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:7]: (warning) 'z' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:10]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); check("void foo(unsigned int i) {\n" " printf(\"%hd\", i);\n" " printf(\"%hhd\", i);\n" " printf(\"%ld\", i);\n" " printf(\"%lld\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'short' but the argument type is 'unsigned int'.\n" "[test.cpp:3]: (warning) %hhd in format string (no. 1) requires 'char' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'unsigned int'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Unix32); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(std::size_t s, std::ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Unix32); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(std::size_t s, std::ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(std::size_t s, std::ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout.str()); check("void foo(size_t s, ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n", errout.str()); check("void foo(std::size_t s, std::ptrdiff_t p) {\n" " printf(\"%zd\", s);\n" " printf(\"%tu\", p);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long long}'.\n" "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long long}'.\n", errout.str()); check("void foo(size_t s, uintmax_t um) {\n" " printf(\"%lu\", s);\n" " printf(\"%lu\", um);\n" " printf(\"%llu\", s);\n" " printf(\"%llu\", um);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:3]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'uintmax_t {aka unsigned long long}'.\n" "[test.cpp:4]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'uintmax_t {aka unsigned long long}'.\n", errout.str()); check("void foo(unsigned int i) {\n" " printf(\"%ld\", i);\n" " printf(\"%lld\", i);\n" " printf(\"%lu\", i);\n" " printf(\"%llu\", i);\n" " printf(\"%lx\", i);\n" " printf(\"%llx\", i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:3]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'unsigned int'.\n" "[test.cpp:6]: (warning) %lx in format string (no. 1) requires 'unsigned long' but the argument type is 'unsigned int'.\n" "[test.cpp:7]: (warning) %llx in format string (no. 1) requires 'unsigned long long' but the argument type is 'unsigned int'.\n", errout.str()); check("void foo(int i, intmax_t im, ptrdiff_t p) {\n" " printf(\"%lld\", i);\n" " printf(\"%lld\", im);\n" " printf(\"%lld\", p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'signed int'.\n", errout.str()); check("void foo(intmax_t im, ptrdiff_t p) {\n" " printf(\"%lld\", im);\n" " printf(\"%lld\", p);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:2]: (portability) %lld in format string (no. 1) requires 'long long' but the argument type is 'intmax_t {aka signed long long}'.\n" "[test.cpp:3]: (portability) %lld in format string (no. 1) requires 'long long' but the argument type is 'ptrdiff_t {aka signed long long}'.\n", errout.str()); check("class Foo {\n" " double d;\n" " struct Bar {\n" " int i;\n" " } bar[2];\n" " struct Baz {\n" " int i;\n" " } baz;\n" "};\n" "int a[10];\n" "Foo f[10];\n" "void foo(const Foo* foo) {\n" " printf(\"%d %f %f %d %f %f\",\n" " foo->d, foo->bar[0].i, a[0],\n" " f[0].d, f[0].baz.i, f[0].bar[0].i);\n" "}"); ASSERT_EQUALS("[test.cpp:13]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'double'.\n" "[test.cpp:13]: (warning) %f in format string (no. 2) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:13]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'int'.\n" "[test.cpp:13]: (warning) %d in format string (no. 4) requires 'int' but the argument type is 'double'.\n" "[test.cpp:13]: (warning) %f in format string (no. 5) requires 'double' but the argument type is 'int'.\n" "[test.cpp:13]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'int'.\n", errout.str()); check("short f() { return 0; }\n" "void foo() { printf(\"%d %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed short'.\n", errout.str()); check("unsigned short f() { return 0; }\n" "void foo() { printf(\"%u %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned short'.\n", errout.str()); check("int f() { return 0; }\n" "void foo() { printf(\"%d %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed int'.\n", errout.str()); check("unsigned int f() { return 0; }\n" "void foo() { printf(\"%u %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned int'.\n", errout.str()); check("long f() { return 0; }\n" "void foo() { printf(\"%ld %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed long'.\n", errout.str()); check("unsigned long f() { return 0; }\n" "void foo() { printf(\"%lu %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned long'.\n", errout.str()); check("long long f() { return 0; }\n" "void foo() { printf(\"%lld %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed long long'.\n", errout.str()); check("unsigned long long f() { return 0; }\n" "void foo() { printf(\"%llu %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned long long'.\n", errout.str()); check("float f() { return 0; }\n" "void foo() { printf(\"%f %d %ld %u %lu %I64d %I64u %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 8) requires 'long double' but the argument type is 'float'.\n" "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'float'.\n", errout.str()); check("double f() { return 0; }\n" "void foo() { printf(\"%f %d %ld %u %lu %I64d %I64u %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 8) requires 'long double' but the argument type is 'double'.\n" "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'double'.\n", errout.str()); check("long double f() { return 0; }\n" "void foo() { printf(\"%Lf %d %ld %u %lu %I64d %I64u %f %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %f in format string (no. 8) requires 'double' but the argument type is 'long double'.\n" "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'long double'.\n", errout.str()); check("int f() { return 0; }\n" "void foo() { printf(\"%I64d %I64u %I64x %d\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %I64d in format string (no. 1) requires '__int64' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %I64x in format string (no. 3) requires 'unsigned __int64' but the argument type is 'signed int'.\n", errout.str()); check("long long f() { return 0; }\n" "void foo() { printf(\"%I32d %I32u %I32x %lld\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is 'signed long long'.\n" "[test.cpp:2]: (warning) %I32x in format string (no. 3) requires 'unsigned __int32' but the argument type is 'signed long long'.\n", errout.str()); check("unsigned long long f() { return 0; }\n" "void foo() { printf(\"%I32d %I32u %I32x %llx\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is 'unsigned long long'.\n" "[test.cpp:2]: (warning) %I32x in format string (no. 3) requires 'unsigned __int32' but the argument type is 'unsigned long long'.\n", errout.str()); check("signed char f() { return 0; }\n" "void foo() { printf(\"%Id %Iu %Ix %hhi\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'signed char'.\n" "[test.cpp:2]: (warning) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'signed char'.\n" "[test.cpp:2]: (warning) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'signed char'.\n", errout.str()); check("unsigned char f() { return 0; }\n" "void foo() { printf(\"%Id %Iu %Ix %hho\", f(), f(), f(), f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'unsigned char'.\n", errout.str()); check("namespace bar { int f() { return 0; } }\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar::f(), bar::f(), bar::f(), bar::f(), bar::f(), bar::f()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f;\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", f.i, f.i, f.i, f.i, f.i, f.i); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { unsigned int u; } f;\n" "void foo() { printf(\"%u %d %ld %f %Lf %p\", f.u, f.u, f.u, f.u, f.u, f.u); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'unsigned int'.\n", errout.str()); check("struct Fred { unsigned int ui() { return 0; } } f;\n" "void foo() { printf(\"%u %d %ld %f %Lf %p\", f.ui(), f.ui(), f.ui(), f.ui(), f.ui(), f.ui()); }"); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'unsigned int'.\n", errout.str()); // #4975 check("void f(int len, int newline) {\n" " printf(\"%s\", newline ? a : str + len);\n" " printf(\"%s\", newline + newline);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f;\n" "struct Fred & bar() { };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f;\n" "const struct Fred & bar() { };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f;\n" "static const struct Fred & bar() { };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f[2];\n" "struct Fred * bar() { return f; };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f[2];\n" "const struct Fred * bar() { return f; };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int i; } f[2];\n" "static const struct Fred * bar() { return f; };\n" "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout.str()); check("struct Fred { int32_t i; } f;\n" "struct Fred & bar() { };\n" "void foo() { printf(\"%d %ld %u %lu %f %Lf\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); ASSERT_EQUALS("[test.cpp:3]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %lu in format string (no. 4) requires 'unsigned long' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 5) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %Lf in format string (no. 6) requires 'long double' but the argument type is 'signed int'.\n", errout.str()); // #4984 check("void f(double *x) {\n" " printf(\"%f\", x[0]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int array[10];\n" "int * foo() { return array; }\n" "void f() {\n" " printf(\"%f\", foo()[0]);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout.str()); check("struct Base { int length() { } };\n" "struct Derived : public Base { };\n" "void foo(Derived * d) {\n" " printf(\"%f\", d.length());\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout.str()); check("std::vector v;\n" "void foo() {\n" " printf(\"%d %u %f\", v[0], v[0], v[0]);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'signed int'.\n", errout.str()); // #4999 (crash) check("int bar(int a);\n" "void foo() {\n" " printf(\"%d\", bar(0));\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::vector v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str()); check("std::vector v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long long}'.\n", errout.str()); check("std::vector v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Unix32); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str()); check("std::vector v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str()); check("class Fred : public std::vector {} v;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout.str()); check("class Fred : public std::vector {} v;\n" "void foo() {\n" " printf(\"%d %u %f\", v[0], v[0], v[0]);\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n" "[test.cpp:3]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'int'.\n", errout.str()); check("std::string s;\n" "void foo() {\n" " printf(\"%s %p %u %d %f\", s.c_str(), s.c_str(), s.c_str(), s.c_str(), s.c_str());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %d in format string (no. 4) requires 'int' but the argument type is 'const char *'.\n" "[test.cpp:3]: (warning) %f in format string (no. 5) requires 'double' but the argument type is 'const char *'.\n", errout.str()); check("std::vector array;\n" "char * p = 0;\n" "char q[] = \"abc\";\n" "char r[10] = { 0 };\n" "size_t s;\n" "void foo() {\n" " printf(\"%zu %zu\", array.size(), s);\n" " printf(\"%u %u %u\", p, q, r);\n" " printf(\"%u %u\", array.size(), s);\n" " printf(\"%lu %lu\", array.size(), s);\n" " printf(\"%llu %llu\", array.size(), s);\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:8]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n" "[test.cpp:8]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'char *'.\n" "[test.cpp:8]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'char *'.\n" "[test.cpp:9]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:9]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:10]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:10]: (portability) %lu in format string (no. 2) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:11]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'std::size_t {aka unsigned long}'.\n" "[test.cpp:11]: (portability) %llu in format string (no. 2) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); check("bool b; bool bf();\n" "char c; char cf();\n" "signed char sc; signed char scf();\n" "unsigned char uc; unsigned char ucf();\n" "short s; short sf();\n" "unsigned short us; unsigned short usf();\n" "size_t st; size_t stf();\n" "ptrdiff_t pt; ptrdiff_t ptf();\n" "char * pc; char * pcf();\n" "char cl[] = \"123\";\n" "char ca[3];\n" "void foo() {\n" " printf(\"%td %zd %d %d %d %d %d %d %d %d %d %d %d\", pt, pt, b, c, sc, uc, s, us, st, pt, pc, cl, ca);\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (portability) %zd in format string (no. 2) requires 'ssize_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (portability) %d in format string (no. 9) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (portability) %d in format string (no. 10) requires 'int' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (warning) %d in format string (no. 11) requires 'int' but the argument type is 'char *'.\n" "[test.cpp:13]: (warning) %d in format string (no. 12) requires 'int' but the argument type is 'char *'.\n" "[test.cpp:13]: (warning) %d in format string (no. 13) requires 'int' but the argument type is 'char *'.\n", errout.str()); check("bool b; bool bf();\n" "char c; char cf();\n" "signed char sc; signed char scf();\n" "unsigned char uc; unsigned char ucf();\n" "short s; short sf();\n" "unsigned short us; unsigned short usf();\n" "size_t st; size_t stf();\n" "ptrdiff_t pt; ptrdiff_t ptf();\n" "char * pc; char * pcf();\n" "char cl[] = \"123\";\n" "char ca[3];\n" "void foo() {\n" " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\", b, c, sc, uc, s, us, st, pt, pc, cl, ca);\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'bool'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'signed char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 4) requires 'long' but the argument type is 'unsigned char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 5) requires 'long' but the argument type is 'signed short'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 6) requires 'long' but the argument type is 'unsigned short'.\n" "[test.cpp:13]: (portability) %ld in format string (no. 7) requires 'long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (portability) %ld in format string (no. 8) requires 'long' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 9) requires 'long' but the argument type is 'char *'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 10) requires 'long' but the argument type is 'char *'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 11) requires 'long' but the argument type is 'char *'.\n", errout.str()); check("bool b; bool bf();\n" "char c; char cf();\n" "signed char sc; signed char scf();\n" "unsigned char uc; unsigned char ucf();\n" "short s; short sf();\n" "unsigned short us; unsigned short usf();\n" "size_t st; size_t stf();\n" "ptrdiff_t pt; ptrdiff_t ptf();\n" "char * pc; char * pcf();\n" "char cl[] = \"123\";\n" "char ca[3];\n" "void foo() {\n" " printf(\"%td %zd %d %d %d %d %d %d %d %d %d\", ptf(), ptf(), bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (portability) %zd in format string (no. 2) requires 'ssize_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (portability) %d in format string (no. 9) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (portability) %d in format string (no. 10) requires 'int' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (warning) %d in format string (no. 11) requires 'int' but the argument type is 'char *'.\n", errout.str()); check("bool b; bool bf();\n" "char c; char cf();\n" "signed char sc; signed char scf();\n" "unsigned char uc; unsigned char ucf();\n" "short s; short sf();\n" "unsigned short us; unsigned short usf();\n" "size_t st; size_t stf();\n" "ptrdiff_t pt; ptrdiff_t ptf();\n" "char * pc; char * pcf();\n" "char cl[] = \"123\";\n" "char ca[3];\n" "void foo() {\n" " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld\", bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:13]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'bool'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'signed char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 4) requires 'long' but the argument type is 'unsigned char'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 5) requires 'long' but the argument type is 'signed short'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 6) requires 'long' but the argument type is 'unsigned short'.\n" "[test.cpp:13]: (portability) %ld in format string (no. 7) requires 'long' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:13]: (portability) %ld in format string (no. 8) requires 'long' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:13]: (warning) %ld in format string (no. 9) requires 'long' but the argument type is 'char *'.\n", errout.str()); check("struct A {};\n" "class B : public std::vector {} b;\n" "class C : public std::vector {} c;\n" "std::string s;\n" "void foo() {\n" " printf(\"%zu %u\", b.size(), b.size());\n" " printf(\"%p %d\", b[0], b[0]);\n" " printf(\"%p %d\", c[0], c[0]);\n" " printf(\"%p %d\", s.c_str(), s.c_str());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:6]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:7]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const int *'.\n" "[test.cpp:8]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const struct A *'.\n" "[test.cpp:9]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const char *'.\n", errout.str()); check("class A : public std::vector {} a;\n" "class B : public std::string {} b;\n" "std::string s;\n" "void foo() {\n" " printf(\"%p %d\", a[0].c_str(), a[0].c_str());\n" " printf(\"%c %p\", b[0], b[0]);\n" " printf(\"%c %p\", s[0], s[0]);\n" "}\n", false, false, Settings::Unix64); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const char *'.\n" "[test.cpp:6]: (warning) %p in format string (no. 2) requires an address but the argument type is 'char'.\n" "[test.cpp:7]: (warning) %p in format string (no. 2) requires an address but the argument type is 'char'.\n", errout.str()); check("template \n" "struct buffer {\n" " size_t size();\n" "};\n" "buffer b;\n" "void foo() {\n" " printf(\"%u\", b.size());\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:7]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); check("DWORD a;\n" "DWORD_PTR b;\n" "void foo() {\n" " printf(\"%u %u\", a, b);\n" "}\n", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'DWORD {aka unsigned long}'.\n" "[test.cpp:4]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'DWORD_PTR {aka unsigned long}'.\n", errout.str()); check("unsigned long a[] = { 1, 2 };\n" "void foo() {\n" " printf(\"%d %d %x \", a[0], a[0], a[0]);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned long'.\n" "[test.cpp:3]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned long'.\n" "[test.cpp:3]: (warning) %x in format string (no. 3) requires 'unsigned int' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo (wchar_t c) {\n" // ticket #5051 false positive " printf(\"%c\", c);\n" "}\n", false, false, Settings::Win64); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " printf(\"%f %d\", static_cast(1.0f), reinterpret_cast(0));\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const void *'.\n", errout.str()); check("void foo() {\n" " UNKNOWN * u;\n" " printf(\"%d %x %u %f\", u[i], u[i], u[i], u[i]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " long * l;\n" " printf(\"%d %x %u %f\", l[i], l[i], l[i], l[i]);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'signed long'.\n" "[test.cpp:3]: (warning) %x in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long'.\n" "[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'signed long'.\n" "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed long'.\n", errout.str()); check("void f() {\n" // #5104 " myvector v1(1,0);\n" " printf(\"%d\",v1[0]);\n" " myvector v2(1,0);\n" " printf(\"%d\",v2[0]);\n" " myvector v3(1,0);\n" " printf(\"%u\",v3[0]);\n" " myvector v4(1,0);\n" " printf(\"%x\",v4[0]);\n" " myvector v5(1,0);\n" " printf(\"%f\",v5[0]);\n" " myvector v6(1,0);\n" " printf(\"%u\",v6[0]);\n" " myvector v7(1,0);\n" " printf(\"%s\",v7[0]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("std::vector v;\n" // #5151 "void foo() {\n" " printf(\"%c %u %f\", v.at(32), v.at(32), v.at(32));\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'char'.\n" "[test.cpp:3]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'char'.\n", errout.str()); // #5195 (segmentation fault) check("void T::a(const std::vector& vx) {\n" " printf(\"%f\", vx.at(0));\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #5486 check("void foo() {\n" " ssize_t test = 0;\n" " printf(\"%zd\", test);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #6009 check("extern std::string StringByReturnValue();\n" "extern int IntByReturnValue();\n" "void MyFunction() {\n" " printf( \"%s - %s\", StringByReturnValue(), IntByReturnValue() );\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'std::string'.\n" "[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is 'signed int'.\n", errout.str()); check("template \n" "struct Array {\n" " T data[S];\n" " T & operator [] (size_t i) { return data[i]; }\n" "};\n" "void foo() {\n" " Array array1;\n" " Array array2;\n" " printf(\"%u %u\", array1[0], array2[0]);\n" "}\n"); ASSERT_EQUALS("[test.cpp:9]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'int'.\n" "[test.cpp:9]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'float'.\n", errout.str()); // Ticket #7445 check("struct S { unsigned short x; } s = {0};\n" "void foo() {\n" " printf(\"%d\", s.x);\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #7601 check("void foo(int i, unsigned int ui, long long ll, unsigned long long ull) {\n" " printf(\"%Ld %Lu %Ld %Lu\", i, ui, ll, ull);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %Lu in format string (no. 2) requires 'unsigned long long' but the argument type is 'unsigned int'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hhd %hhd %hhd %hhd %hhd %hhd %hhd %hhd\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hhd in format string (no. 2) requires 'char' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 3) requires 'char' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 4) requires 'char' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 5) requires 'char' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 6) requires 'char' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 7) requires 'char' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hhd in format string (no. 8) requires 'char' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hhu in format string (no. 1) requires 'unsigned char' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 3) requires 'unsigned char' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 4) requires 'unsigned char' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 5) requires 'unsigned char' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 6) requires 'unsigned char' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 7) requires 'unsigned char' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hhu in format string (no. 8) requires 'unsigned char' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hhx in format string (no. 1) requires 'unsigned char' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 3) requires 'unsigned char' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 4) requires 'unsigned char' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 5) requires 'unsigned char' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 6) requires 'unsigned char' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 7) requires 'unsigned char' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hhx in format string (no. 8) requires 'unsigned char' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hd %hd %hd %hd %hd %hd %hd %hd\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'short' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 2) requires 'short' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 4) requires 'short' but the argument type is 'unsigned short'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 5) requires 'short' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 6) requires 'short' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 7) requires 'short' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hd in format string (no. 8) requires 'short' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hu %hu %hu %hu %hu %hu %hu %hu\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hu in format string (no. 1) requires 'unsigned short' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 2) requires 'unsigned short' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 3) requires 'unsigned short' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 5) requires 'unsigned short' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 6) requires 'unsigned short' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 7) requires 'unsigned short' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hu in format string (no. 8) requires 'unsigned short' but the argument type is 'unsigned long'.\n", errout.str()); check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" " printf(\"%hx %hx %hx %hx %hx %hx %hx %hx\", c, uc, s, us, i, ui, l, ul);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) %hx in format string (no. 1) requires 'unsigned short' but the argument type is 'char'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 2) requires 'unsigned short' but the argument type is 'unsigned char'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 3) requires 'unsigned short' but the argument type is 'signed short'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 5) requires 'unsigned short' but the argument type is 'signed int'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 6) requires 'unsigned short' but the argument type is 'unsigned int'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 7) requires 'unsigned short' but the argument type is 'signed long'.\n" "[test.cpp:2]: (warning) %hx in format string (no. 8) requires 'unsigned short' but the argument type is 'unsigned long'.\n", errout.str()); // #7837 - Use ValueType for function call check("struct S {\n" " double (* f)(double);\n" "};\n" "\n" "void foo(struct S x) {\n" " printf(\"%f\", x.f(4.0));\n" "}"); ASSERT_EQUALS("", errout.str()); } void testPrintfArgumentVariables() { TEST_PRINTF_NOWARN("%u", "unsigned int", "bool"); TEST_PRINTF_WARN("%u", "unsigned int", "char"); TEST_PRINTF_WARN("%u", "unsigned int", "signed char"); TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned char"); TEST_PRINTF_WARN("%u", "unsigned int", "signed short"); TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned short"); TEST_PRINTF_WARN("%u", "unsigned int", "signed int"); TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned int"); TEST_PRINTF_WARN("%u", "unsigned int", "signed long"); TEST_PRINTF_WARN("%u", "unsigned int", "unsigned long"); TEST_PRINTF_WARN("%u", "unsigned int", "signed long long"); TEST_PRINTF_WARN("%u", "unsigned int", "unsigned long long"); TEST_PRINTF_WARN("%u", "unsigned int", "float"); TEST_PRINTF_WARN("%u", "unsigned int", "double"); TEST_PRINTF_WARN("%u", "unsigned int", "long double"); TEST_PRINTF_WARN("%u", "unsigned int", "void *"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%u", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%x", "unsigned int", "bool"); //TODO TEST_PRINTF_WARN("%x", "unsigned int", "char"); //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed char"); TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned char"); //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed short"); TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned short"); //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed int"); TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned int"); TEST_PRINTF_WARN("%x", "unsigned int", "signed long"); TEST_PRINTF_WARN("%x", "unsigned int", "unsigned long"); TEST_PRINTF_WARN("%x", "unsigned int", "signed long long"); TEST_PRINTF_WARN("%x", "unsigned int", "unsigned long long"); TEST_PRINTF_WARN("%x", "unsigned int", "float"); TEST_PRINTF_WARN("%x", "unsigned int", "double"); TEST_PRINTF_WARN("%x", "unsigned int", "long double"); TEST_PRINTF_WARN("%x", "unsigned int", "void *"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%x", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%lu","unsigned long","bool"); TEST_PRINTF_WARN("%lu","unsigned long","char"); TEST_PRINTF_WARN("%lu","unsigned long","signed char"); TEST_PRINTF_WARN("%lu","unsigned long","unsigned char"); TEST_PRINTF_WARN("%lu","unsigned long","signed short"); TEST_PRINTF_WARN("%lu","unsigned long","unsigned short"); TEST_PRINTF_WARN("%lu","unsigned long","signed int"); TEST_PRINTF_WARN("%lu","unsigned long","unsigned int"); TEST_PRINTF_WARN("%lu","unsigned long","signed long"); TEST_PRINTF_NOWARN("%lu","unsigned long","unsigned long"); TEST_PRINTF_WARN("%lu","unsigned long","signed long long"); TEST_PRINTF_WARN("%lu","unsigned long","unsigned long long"); TEST_PRINTF_WARN("%lu","unsigned long","float"); TEST_PRINTF_WARN("%lu","unsigned long","double"); TEST_PRINTF_WARN("%lu","unsigned long","long double"); TEST_PRINTF_WARN("%lu","unsigned long","void *"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","size_t","unsigned long","unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","unsigned ptrdiff_t", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","uintmax_t","unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","uintptr_t", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::size_t","unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","std::uintmax_t", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lu","unsigned long","std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","std::uintptr_t", "unsigned long long"); TEST_PRINTF_WARN("%lx","unsigned long","bool"); TEST_PRINTF_WARN("%lx","unsigned long","char"); TEST_PRINTF_WARN("%lx","unsigned long","signed char"); TEST_PRINTF_WARN("%lx","unsigned long","unsigned char"); TEST_PRINTF_WARN("%lx","unsigned long","signed short"); TEST_PRINTF_WARN("%lx","unsigned long","unsigned short"); TEST_PRINTF_WARN("%lx","unsigned long","signed int"); TEST_PRINTF_WARN("%lx","unsigned long","unsigned int"); //TODO TEST_PRINTF_WARN("%lx","unsigned long","signed long"); TEST_PRINTF_NOWARN("%lx","unsigned long","unsigned long"); TEST_PRINTF_WARN("%lx","unsigned long","signed long long"); TEST_PRINTF_WARN("%lx","unsigned long","unsigned long long"); TEST_PRINTF_WARN("%lx","unsigned long","float"); TEST_PRINTF_WARN("%lx","unsigned long","double"); TEST_PRINTF_WARN("%lx","unsigned long","long double"); TEST_PRINTF_WARN("%lx","unsigned long","void *"); TEST_PRINTF_WARN_AKA("%lx","unsigned long","size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","ssize_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","ptrdiff_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","unsigned ptrdiff_t", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","intmax_t", "signed long long"); TEST_PRINTF_WARN_AKA("%lx","unsigned long","uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","intptr_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","uintptr_t", "unsigned long long"); TEST_PRINTF_WARN_AKA("%lx","unsigned long","std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::ssize_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::ptrdiff_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::intmax_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::uintmax_t", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::intptr_t", "signed long long"); TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","std::uintptr_t", "unsigned long long"); TEST_PRINTF_WARN("%llu","unsigned long long","bool"); TEST_PRINTF_WARN("%llu","unsigned long long","char"); TEST_PRINTF_WARN("%llu","unsigned long long","signed char"); TEST_PRINTF_WARN("%llu","unsigned long long","unsigned char"); TEST_PRINTF_WARN("%llu","unsigned long long","signed short"); TEST_PRINTF_WARN("%llu","unsigned long long","unsigned short"); TEST_PRINTF_WARN("%llu","unsigned long long","signed int"); TEST_PRINTF_WARN("%llu","unsigned long long","unsigned int"); TEST_PRINTF_WARN("%llu","unsigned long long","signed long"); TEST_PRINTF_WARN("%llu","unsigned long long","unsigned long"); TEST_PRINTF_WARN("%llu","unsigned long long","signed long long"); TEST_PRINTF_NOWARN("%llu","unsigned long long","unsigned long long"); TEST_PRINTF_WARN("%llu","unsigned long long","float"); TEST_PRINTF_WARN("%llu","unsigned long long","double"); TEST_PRINTF_WARN("%llu","unsigned long long","long double"); TEST_PRINTF_WARN("%llu","unsigned long long","void *"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%llu","unsigned long long","std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","std::uintptr_t", "unsigned long"); TEST_PRINTF_WARN("%llx","unsigned long long","bool"); TEST_PRINTF_WARN("%llx","unsigned long long","char"); TEST_PRINTF_WARN("%llx","unsigned long long","signed char"); TEST_PRINTF_WARN("%llx","unsigned long long","unsigned char"); TEST_PRINTF_WARN("%llx","unsigned long long","signed short"); TEST_PRINTF_WARN("%llx","unsigned long long","unsigned short"); TEST_PRINTF_WARN("%llx","unsigned long long","signed int"); TEST_PRINTF_WARN("%llx","unsigned long long","unsigned int"); TEST_PRINTF_WARN("%llx","unsigned long long","signed long"); TEST_PRINTF_WARN("%llx","unsigned long long","unsigned long"); //TODO TEST_PRINTF_WARN("%llx","unsigned long long","signed long long"); TEST_PRINTF_NOWARN("%llx","unsigned long long","unsigned long long"); TEST_PRINTF_WARN("%llx","unsigned long long","float"); TEST_PRINTF_WARN("%llx","unsigned long long","double"); TEST_PRINTF_WARN("%llx","unsigned long long","long double"); TEST_PRINTF_WARN("%llx","unsigned long long","void *"); TEST_PRINTF_WARN_AKA("%llx","unsigned long long","size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","ptrdiff_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","intmax_t", "signed long"); TEST_PRINTF_WARN_AKA("%llx","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","intptr_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%llx","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::ptrdiff_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::intmax_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::intptr_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","std::uintptr_t", "unsigned long"); TEST_PRINTF_WARN("%hu", "unsigned short", "bool"); TEST_PRINTF_WARN("%hu", "unsigned short", "char"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed char"); TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned char"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed short"); TEST_PRINTF_NOWARN("%hu", "unsigned short", "unsigned short"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed int"); TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned int"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed long"); TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned long"); TEST_PRINTF_WARN("%hu", "unsigned short", "signed long long"); TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned long long"); TEST_PRINTF_WARN("%hu", "unsigned short", "float"); TEST_PRINTF_WARN("%hu", "unsigned short", "double"); TEST_PRINTF_WARN("%hu", "unsigned short", "long double"); TEST_PRINTF_WARN("%hu", "unsigned short", "void *"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%hx", "unsigned short", "bool"); TEST_PRINTF_WARN("%hx", "unsigned short", "char"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed char"); TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned char"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed short"); TEST_PRINTF_NOWARN("%hx", "unsigned short", "unsigned short"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed int"); TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned int"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed long"); TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned long"); TEST_PRINTF_WARN("%hx", "unsigned short", "signed long long"); TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned long long"); TEST_PRINTF_WARN("%hx", "unsigned short", "float"); TEST_PRINTF_WARN("%hx", "unsigned short", "double"); TEST_PRINTF_WARN("%hx", "unsigned short", "long double"); TEST_PRINTF_WARN("%hx", "unsigned short", "void *"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "bool"); TEST_PRINTF_WARN("%hhu", "unsigned char", "char"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed char"); TEST_PRINTF_NOWARN("%hhu", "unsigned char", "unsigned char"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed short"); TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned short"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed int"); TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned int"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "signed long long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned long long"); TEST_PRINTF_WARN("%hhu", "unsigned char", "float"); TEST_PRINTF_WARN("%hhu", "unsigned char", "double"); TEST_PRINTF_WARN("%hhu", "unsigned char", "long double"); TEST_PRINTF_WARN("%hhu", "unsigned char", "void *"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "bool"); TEST_PRINTF_WARN("%hhx", "unsigned char", "char"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed char"); TEST_PRINTF_NOWARN("%hhx", "unsigned char", "unsigned char"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed short"); TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned short"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed int"); TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned int"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "signed long long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned long long"); TEST_PRINTF_WARN("%hhx", "unsigned char", "float"); TEST_PRINTF_WARN("%hhx", "unsigned char", "double"); TEST_PRINTF_WARN("%hhx", "unsigned char", "long double"); TEST_PRINTF_WARN("%hhx", "unsigned char", "void *"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "bool"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "char"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed char"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned char"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed short"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned short"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed int"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned int"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed long"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned long"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed long long"); TEST_PRINTF_NOWARN("%Lu", "unsigned long long", "unsigned long long"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "float"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "double"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "long double"); TEST_PRINTF_WARN("%Lu", "unsigned long long", "void *"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "size_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "std::size_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "std::uintptr_t", "unsigned long"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "bool"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "char"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed char"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned char"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed short"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned short"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed int"); //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned int"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed long"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned long"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed long long"); //TODO TEST_PRINTF_NOWARN("%Lx", "unsigned long long", "unsigned long long"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "float"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "double"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "long double"); TEST_PRINTF_WARN("%Lx", "unsigned long long", "void *"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "bool"); TEST_PRINTF_WARN("%ju", "uintmax_t", "char"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed char"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned char"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed short"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned short"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed int"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned int"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "signed long long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned long long"); TEST_PRINTF_WARN("%ju", "uintmax_t", "float"); TEST_PRINTF_WARN("%ju", "uintmax_t", "double"); TEST_PRINTF_WARN("%ju", "uintmax_t", "long double"); TEST_PRINTF_WARN("%ju", "uintmax_t", "void *"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_NOWARN("%ju", "uintmax_t", "uintmax_t"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "bool"); TEST_PRINTF_WARN("%jx", "uintmax_t", "char"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed char"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned char"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed short"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned short"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed int"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned int"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "signed long long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned long long"); TEST_PRINTF_WARN("%jx", "uintmax_t", "float"); TEST_PRINTF_WARN("%jx", "uintmax_t", "double"); TEST_PRINTF_WARN("%jx", "uintmax_t", "long double"); TEST_PRINTF_WARN("%jx", "uintmax_t", "void *"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_NOWARN("%jx", "uintmax_t", "uintmax_t"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%zu", "size_t", "bool"); TEST_PRINTF_WARN("%zu", "size_t", "char"); TEST_PRINTF_WARN("%zu", "size_t", "signed char"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned char"); TEST_PRINTF_WARN("%zu", "size_t", "signed short"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned short"); TEST_PRINTF_WARN("%zu", "size_t", "signed int"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned int"); TEST_PRINTF_WARN("%zu", "size_t", "signed long"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned long"); TEST_PRINTF_WARN("%zu", "size_t", "signed long long"); TEST_PRINTF_WARN("%zu", "size_t", "unsigned long long"); TEST_PRINTF_WARN("%zu", "size_t", "float"); TEST_PRINTF_WARN("%zu", "size_t", "double"); TEST_PRINTF_WARN("%zu", "size_t", "long double"); TEST_PRINTF_WARN("%zu", "size_t", "void *"); TEST_PRINTF_NOWARN("%zu", "size_t", "size_t"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%zu", "size_t", "std::size_t"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%zx", "size_t", "bool"); TEST_PRINTF_WARN("%zx", "size_t", "char"); TEST_PRINTF_WARN("%zx", "size_t", "signed char"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned char"); TEST_PRINTF_WARN("%zx", "size_t", "signed short"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned short"); TEST_PRINTF_WARN("%zx", "size_t", "signed int"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned int"); TEST_PRINTF_WARN("%zx", "size_t", "signed long"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned long"); TEST_PRINTF_WARN("%zx", "size_t", "signed long long"); TEST_PRINTF_WARN("%zx", "size_t", "unsigned long long"); TEST_PRINTF_WARN("%zx", "size_t", "float"); TEST_PRINTF_WARN("%zx", "size_t", "double"); TEST_PRINTF_WARN("%zx", "size_t", "long double"); TEST_PRINTF_WARN("%zx", "size_t", "void *"); TEST_PRINTF_NOWARN("%zx", "size_t", "size_t"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%zx", "size_t", "std::size_t"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%zx", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "bool"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "char"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed char"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned char"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed short"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned short"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed int"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned int"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed long long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long long"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "float"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "double"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "long double"); TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "void *"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_NOWARN("%tu", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "bool"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "char"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed char"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned char"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed short"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned short"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed int"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned int"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed long long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long long"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "float"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "double"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "long double"); TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "void *"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); //TODO TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_NOWARN("%tx", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); //TODO TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%Iu", "size_t", "bool"); TEST_PRINTF_WARN("%Iu", "size_t", "char"); TEST_PRINTF_WARN("%Iu", "size_t", "signed char"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned char"); TEST_PRINTF_WARN("%Iu", "size_t", "signed short"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned short"); TEST_PRINTF_WARN("%Iu", "size_t", "signed int"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned int"); TEST_PRINTF_WARN("%Iu", "size_t", "signed long"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned long"); TEST_PRINTF_WARN("%Iu", "size_t", "signed long long"); TEST_PRINTF_WARN("%Iu", "size_t", "unsigned long long"); TEST_PRINTF_WARN("%Iu", "size_t", "float"); TEST_PRINTF_WARN("%Iu", "size_t", "double"); TEST_PRINTF_WARN("%Iu", "size_t", "long double"); TEST_PRINTF_WARN("%Iu", "size_t", "void *"); TEST_PRINTF_NOWARN("%Iu", "size_t", "size_t"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%Iu", "size_t", "std::size_t"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Iu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%Ix", "size_t", "bool"); TEST_PRINTF_WARN("%Ix", "size_t", "char"); TEST_PRINTF_WARN("%Ix", "size_t", "signed char"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned char"); TEST_PRINTF_WARN("%Ix", "size_t", "signed short"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned short"); TEST_PRINTF_WARN("%Ix", "size_t", "signed int"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned int"); TEST_PRINTF_WARN("%Ix", "size_t", "signed long"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned long"); TEST_PRINTF_WARN("%Ix", "size_t", "signed long long"); TEST_PRINTF_WARN("%Ix", "size_t", "unsigned long long"); TEST_PRINTF_WARN("%Ix", "size_t", "float"); TEST_PRINTF_WARN("%Ix", "size_t", "double"); TEST_PRINTF_WARN("%Ix", "size_t", "long double"); TEST_PRINTF_WARN("%Ix", "size_t", "void *"); TEST_PRINTF_NOWARN("%Ix", "size_t", "size_t"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_NOWARN("%Ix", "size_t", "std::size_t"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%Ix", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "bool"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "char"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed char"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned char"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed short"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned short"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed int"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned int"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed long"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned long"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed long long"); TEST_PRINTF_NOWARN("%I64u", "unsigned __int64", "unsigned long long"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "float"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "double"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "long double"); TEST_PRINTF_WARN("%I64u", "unsigned __int64", "void *"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "size_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::size_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "std::uintptr_t", "unsigned long"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "bool"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "char"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed char"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned char"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed short"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned short"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed int"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned int"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed long"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned long"); //TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed long long"); TEST_PRINTF_NOWARN("%I64x", "unsigned __int64", "unsigned long long"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "float"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "double"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "long double"); TEST_PRINTF_WARN("%I64x", "unsigned __int64", "void *"); //TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "size_t"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "ptrdiff_t", "signed long"); //TODO TEST_PRINTF_NOWARN("%I64x", "unsigned __int64", "unsigned __int64"); // TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "__int64"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "intmax_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "intptr_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintptr_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::size_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::ptrdiff_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::intmax_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::uintmax_t", "unsigned long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::intptr_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "std::uintptr_t", "unsigned long"); TEST_PRINTF_WARN("%I64d", "__int64", "bool"); TEST_PRINTF_WARN("%I64d", "__int64", "signed char"); TEST_PRINTF_WARN("%I64d", "__int64", "unsigned char"); TEST_PRINTF_WARN("%I64d", "__int64", "void *"); // TODO TEST_PRINTF_WARN("%I64d", "__int64", "size_t"); TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "intmax_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "ssize_t", "signed long"); TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "ptrdiff_t", "signed long"); TEST_PRINTF_NOWARN("%I64d", "__int64", "__int64"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "bool"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "char"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed char"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned char"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed short"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned short"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed int"); TEST_PRINTF_NOWARN("%I32u", "unsigned __int32", "unsigned int"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed long"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned long"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed long long"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned long long"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "float"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "double"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "long double"); TEST_PRINTF_WARN("%I32u", "unsigned __int32", "void *"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "bool"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "char"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed char"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned char"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed short"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned short"); //TODO TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed int"); TEST_PRINTF_NOWARN("%I32x", "unsigned __int32", "unsigned int"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed long long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned long long"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "float"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "double"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "long double"); TEST_PRINTF_WARN("%I32x", "unsigned __int32", "void *"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); } void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings check("void foo() {" " int bar;" " printf(\"%1$d\", 1);" " printf(\"%1$d, %d, %1$d\", 1, 2);" " scanf(\"%1$d\", &bar);" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " int bar;\n" " printf(\"%1$d\");\n" " printf(\"%1$d, %d, %4$d\", 1, 2, 3);\n" " scanf(\"%2$d\", &bar);\n" " printf(\"%0$f\", 0.0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) printf format string requires 1 parameter but only 0 are given.\n" "[test.cpp:4]: (warning) printf: referencing parameter 4 while 3 arguments given\n" "[test.cpp:5]: (warning) scanf: referencing parameter 2 while 1 arguments given\n" "[test.cpp:6]: (warning) printf: parameter positions start at 1, not 0\n" "", errout.str()); } void testMicrosoftPrintfArgument() { check("void foo() {\n" " size_t s;\n" " ptrdiff_t p;\n" " __int32 i32;\n" " unsigned __int32 u32;\n" " __int64 i64;\n" " unsigned __int64 u64;\n" " printf(\"%Id %Iu %Ix\", s, s, s);\n" " printf(\"%Id %Iu %Ix\", p, p, p);\n" " printf(\"%I32d %I32u %I32x\", i32, i32, i32);\n" " printf(\"%I32d %I32u %I32x\", u32, u32, u32);\n" " printf(\"%I64d %I64u %I64x\", i64, i64, i64);\n" " printf(\"%I64d %I64u %I64x\", u64, u64, u64);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'size_t {aka unsigned long}'.\n" "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is '__int32 {aka signed int}'.\n" "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is '__int64 {aka signed long long}'.\n" "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64' but the argument type is 'unsigned __int64 {aka unsigned long long}'.\n", errout.str()); check("void foo() {\n" " size_t s;\n" " ptrdiff_t p;\n" " __int32 i32;\n" " unsigned __int32 u32;\n" " __int64 i64;\n" " unsigned __int64 u64;\n" " printf(\"%Id %Iu %Ix\", s, s, s);\n" " printf(\"%Id %Iu %Ix\", p, p, p);\n" " printf(\"%I32d %I32u %I32x\", i32, i32, i32);\n" " printf(\"%I32d %I32u %I32x\", u32, u32, u32);\n" " printf(\"%I64d %I64u %I64x\", i64, i64, i64);\n" " printf(\"%I64d %I64u %I64x\", u64, u64, u64);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'size_t {aka unsigned long long}'.\n" "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n" "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n" "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is '__int32 {aka signed int}'.\n" "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is '__int64 {aka signed long long}'.\n" "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64' but the argument type is 'unsigned __int64 {aka unsigned long long}'.\n", errout.str()); check("void foo() {\n" " size_t s;\n" " int i;\n" " printf(\"%I\", s);\n" " printf(\"%I6\", s);\n" " printf(\"%I6x\", s);\n" " printf(\"%I16\", s);\n" " printf(\"%I16x\", s);\n" " printf(\"%I32\", s);\n" " printf(\"%I64\", s);\n" " printf(\"%I%i\", s, i);\n" " printf(\"%I6%i\", s, i);\n" " printf(\"%I6x%i\", s, i);\n" " printf(\"%I16%i\", s, i);\n" " printf(\"%I16x%i\", s, i);\n" " printf(\"%I32%i\", s, i);\n" " printf(\"%I64%i\", s, i);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:5]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:6]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:7]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:9]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:10]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:11]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:12]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:13]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:14]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:15]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:16]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:17]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); // ticket #5264 check("void foo(LPARAM lp, WPARAM wp, LRESULT lr) {\n" " printf(\"%Ix %Ix %Ix\", lp, wp, lr);\n" "}\n", false, true, Settings::Win64); ASSERT_EQUALS("", errout.str()); check("void foo(LPARAM lp, WPARAM wp, LRESULT lr) {\n" " printf(\"%Ix %Ix %Ix\", lp, wp, lr);\n" "}\n", false, true, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo(UINT32 a, ::UINT32 b, Fred::UINT32 c) {\n" " printf(\"%d %d %d\", a, b, c);\n" "};\n", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (portability) %d in format string (no. 1) requires 'int' but the argument type is 'UINT32 {aka unsigned int}'.\n" "[test.cpp:2]: (portability) %d in format string (no. 2) requires 'int' but the argument type is 'UINT32 {aka unsigned int}'.\n", errout.str()); check("void foo(LPCVOID a, ::LPCVOID b, Fred::LPCVOID c) {\n" " printf(\"%d %d %d\", a, b, c);\n" "};\n", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const void *'.\n" "[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const void *'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s = -2;\n" // In MSVC, SSIZE_T is available in capital letters using #include " int i;\n" " printf(\"%zd\", s);\n" " printf(\"%zd%i\", s, i);\n" " printf(\"%zu\", s);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long}'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s = -2;\n" // In MSVC, SSIZE_T is available in capital letters using #include " int i;\n" " printf(\"%zd\", s);\n" " printf(\"%zd%i\", s, i);\n" " printf(\"%zu\", s);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long long}'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s = -2;\n" // Under Unix, ssize_t has to be written in small letters. Not Cppcheck, but the compiler will report this. " int i;\n" " printf(\"%zd\", s);\n" " printf(\"%zd%i\", s, i);\n" " printf(\"%zu\", s);\n" "}", false, true, Settings::Unix64); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " typedef SSIZE_T ssize_t;\n" // Test using typedef " ssize_t s = -2;\n" " int i;\n" " printf(\"%zd\", s);\n" " printf(\"%zd%i\", s, i);\n" " printf(\"%zu\", s);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:7]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long long}'.\n", errout.str()); } void testMicrosoftScanfArgument() { check("void foo() {\n" " size_t s;\n" " ptrdiff_t p;\n" " __int32 i32;\n" " unsigned __int32 u32;\n" " __int64 i64;\n" " unsigned __int64 u64;\n" " scanf(\"%Id %Iu %Ix\", &s, &s, &s);\n" " scanf(\"%Id %Iu %Ix\", &p, &p, &p);\n" " scanf(\"%I32d %I32u %I32x\", &i32, &i32, &i32);\n" " scanf(\"%I32d %I32u %I32x\", &u32, &u32, &u32);\n" " scanf(\"%I64d %I64u %I64x\", &i64, &i64, &i64);\n" " scanf(\"%I64d %I64u %I64x\", &u64, &u64, &u64);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n" "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n" "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" "[test.cpp:10]: (portability) %I32x in format string (no. 3) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n" "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" "[test.cpp:12]: (portability) %I64x in format string (no. 3) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64 *' but the argument type is 'unsigned __int64 * {aka unsigned long long *}'.\n", errout.str()); check("void foo() {\n" " size_t s;\n" " ptrdiff_t p;\n" " __int32 i32;\n" " unsigned __int32 u32;\n" " __int64 i64;\n" " unsigned __int64 u64;\n" " scanf(\"%Id %Iu %Ix\", &s, &s, &s);\n" " scanf(\"%Id %Iu %Ix\", &p, &p, &p);\n" " scanf(\"%I32d %I32u %I32x\", &i32, &i32, &i32);\n" " scanf(\"%I32d %I32u %I32x\", &u32, &u32, &u32);\n" " scanf(\"%I64d %I64u %I64x\", &i64, &i64, &i64);\n" " scanf(\"%I64d %I64u %I64x\", &u64, &u64, &u64);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n" "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n" "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" "[test.cpp:10]: (portability) %I32x in format string (no. 3) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n" "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" "[test.cpp:12]: (portability) %I64x in format string (no. 3) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64 *' but the argument type is 'unsigned __int64 * {aka unsigned long long *}'.\n", errout.str()); check("void foo() {\n" " size_t s;\n" " int i;\n" " scanf(\"%I\", &s);\n" " scanf(\"%I6\", &s);\n" " scanf(\"%I6x\", &s);\n" " scanf(\"%I16\", &s);\n" " scanf(\"%I16x\", &s);\n" " scanf(\"%I32\", &s);\n" " scanf(\"%I64\", &s);\n" " scanf(\"%I%i\", &s, &i);\n" " scanf(\"%I6%i\", &s, &i);\n" " scanf(\"%I6x%i\", &s, &i);\n" " scanf(\"%I16%i\", &s, &i);\n" " scanf(\"%I16x%i\", &s, &i);\n" " scanf(\"%I32%i\", &s, &i);\n" " scanf(\"%I64%i\", &s, &i);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:5]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:6]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:7]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:9]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:10]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:11]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:12]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:13]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:14]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:15]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:16]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:17]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); check("void foo() {\n" " SSIZE_T s;\n" // In MSVC, SSIZE_T is available in capital letters using #include " int i;\n" " scanf(\"%zd\", &s);\n" " scanf(\"%zd%i\", &s, &i);\n" " scanf(\"%zu\", &s);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long *}'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s;\n" // In MSVC, SSIZE_T is available in capital letters using #include " int i;\n" " scanf(\"%zd\", &s);\n" " scanf(\"%zd%i\", &s, &i);\n" " scanf(\"%zu\", &s);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long long *}'.\n", errout.str()); check("void foo() {\n" " SSIZE_T s;\n" // Under Unix, ssize_t has to be written in small letters. Not Cppcheck, but the compiler will report this. " int i;\n" " scanf(\"%zd\", &s);\n" " scanf(\"%zd%i\", &s, &i);\n" " scanf(\"%zu\", &s);\n" "}", false, true, Settings::Unix64); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " typedef SSIZE_T ssize_t;\n" // Test using typedef " ssize_t s;\n" " int i;\n" " scanf(\"%zd\", &s);\n" " scanf(\"%zd%i\", &s, &i);\n" " scanf(\"%zu\", &s);\n" "}", false, true, Settings::Win64); ASSERT_EQUALS("[test.cpp:7]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long long *}'.\n", errout.str()); } void testMicrosoftCStringFormatArguments() { // ticket #4920 check("void foo() {\n" " unsigned __int32 u32;\n" " String string;\n" " string.Format(\"%I32d\", u32);\n" " string.AppendFormat(\"%I32d\", u32);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " unsigned __int32 u32;\n" " CString string;\n" " string.Format(\"%I32d\", u32);\n" " string.AppendFormat(\"%I32d\", u32);\n" "}", false, true, Settings::Unix32); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " unsigned __int32 u32;\n" " CString string;\n" " string.Format(\"%I32d\", u32);\n" " string.AppendFormat(\"%I32d\", u32);\n" " CString::Format(\"%I32d\", u32);\n" "}", false, true, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:5]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" "[test.cpp:6]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n", errout.str()); } void testMicrosoftSecurePrintfArgument() { check("void foo() {\n" " int i;\n" " unsigned int u;\n" " _tprintf_s(_T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) _tprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " _tprintf_s(_T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) _tprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " printf_s(\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) printf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " wprintf_s(L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) wprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" " unsigned int u;\n" " _stprintf_s(str, sizeof(str) / sizeof(TCHAR), _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _stprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" " unsigned int u;\n" " _stprintf_s(str, sizeof(str) / sizeof(TCHAR), _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _stprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " char str[10];\n" " int i;\n" " unsigned int u;\n" " sprintf_s(str, sizeof(str), \"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) sprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " char str[10];\n" " int i;\n" " unsigned int u;\n" " sprintf_s(str, \"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) sprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " wchar_t str[10];\n" " int i;\n" " unsigned int u;\n" " swprintf_s(str, sizeof(str) / sizeof(wchar_t), L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) swprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " wchar_t str[10];\n" " int i;\n" " unsigned int u;\n" " swprintf_s(str, L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) swprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" " unsigned int u;\n" " _sntprintf_s(str, sizeof(str) / sizeof(TCHAR), _TRUNCATE, _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _sntprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " TCHAR str[10];\n" " int i;\n" " unsigned int u;\n" " _sntprintf_s(str, sizeof(str) / sizeof(TCHAR), _TRUNCATE, _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _sntprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " char str[10];\n" " int i;\n" " unsigned int u;\n" " _snprintf_s(str, sizeof(str), _TRUNCATE, \"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _snprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " wchar_t str[10];\n" " int i;\n" " unsigned int u;\n" " _snwprintf_s(str, sizeof(str) / sizeof(wchar_t), _TRUNCATE, L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:5]: (warning) _snwprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " _ftprintf_s(fp, _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) _ftprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " _ftprintf_s(fp, _T(\"%d %u\"), u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) _ftprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " fprintf_s(fp, \"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) fprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " fwprintf_s(fp, L\"%d %u\", u, i, 0);\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" "[test.cpp:4]: (warning) fwprintf_s format string requires 2 parameters but 3 are given.\n", errout.str()); check("void foo() {\n" " char lineBuffer [600];\n" " const char * const format = \"%15s%17s%17s%17s%17s\";\n" " sprintf_s(lineBuffer, 600, format, \"type\", \"sum\", \"avg\", \"min\", \"max\");\n" " sprintf_s(lineBuffer, format, \"type\", \"sum\", \"avg\", \"min\", \"max\");\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " const char * const format1 = \"%15s%17s%17s%17s%17s\";\n" " const char format2[] = \"%15s%17s%17s%17s%17s\";\n" " const char * const format3 = format1;\n" " int i = 0;\n" " sprintf_s(lineBuffer, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf(lineBuffer, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf(lineBuffer, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf(lineBuffer, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " printf(format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " printf(format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " printf(format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, 100, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, 100, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" " sprintf_s(lineBuffer, 100, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" "}\n", true, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:6]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:7]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:7]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:8]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:8]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:9]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:9]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" "[test.cpp:10]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:10]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" "[test.cpp:11]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:11]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" "[test.cpp:12]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:12]: (warning) printf format string requires 5 parameters but 6 are given.\n" "[test.cpp:13]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:13]: (warning) printf format string requires 5 parameters but 6 are given.\n" "[test.cpp:14]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:14]: (warning) printf format string requires 5 parameters but 6 are given.\n" "[test.cpp:15]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:15]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:16]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:16]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" "[test.cpp:17]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" "[test.cpp:17]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n", errout.str()); } void testMicrosoftSecureScanfArgument() { check("void foo() {\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _tscanf_s(_T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) _tscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _tscanf_s(_T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) _tscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " char str[10];\n" " scanf_s(\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) scanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " int i;\n" " unsigned int u;\n" " wchar_t str[10];\n" " wscanf_s(L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) wscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void f() {\n" " char str[8];\n" " scanf_s(\"%8c\", str, sizeof(str))\n" " scanf_s(\"%9c\", str, sizeof(str))\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:4]: (error) Width 9 given in format string (no. 1) is larger than destination buffer 'str[8]', use %8c to prevent overflowing it.\n", errout.str()); check("void foo() {\n" " TCHAR txt[100];\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _stscanf_s(txt, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:6]: (warning) _stscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " TCHAR txt[100];\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _stscanf_s(txt, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:6]: (warning) _stscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " char txt[100];\n" " int i;\n" " unsigned int u;\n" " char str[10];\n" " sscanf_s(txt, \"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:6]: (warning) sscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " wchar_t txt[100];\n" " int i;\n" " unsigned int u;\n" " wchar_t str[10];\n" " swscanf_s(txt, L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:6]: (warning) swscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _ftscanf_s(fp, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) _ftscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " TCHAR str[10];\n" " _ftscanf_s(fp, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) _ftscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " char str[10];\n" " fscanf_s(fp, \"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) fscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo(FILE * fp) {\n" " int i;\n" " unsigned int u;\n" " wchar_t str[10];\n" " fwscanf_s(fp, L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" "[test.cpp:5]: (warning) fwscanf_s format string requires 6 parameters but 7 are given.\n", errout.str()); check("void foo() {\n" " WCHAR msStr1[5] = {0};\n" " wscanf_s(L\"%4[^-]\", msStr1, _countof(msStr1));\n" "}\n", false, false, Settings::Win32W); ASSERT_EQUALS("", errout.str()); } void testQStringFormatArguments() { check("void foo(float f) {\n" " QString string;\n" " string.sprintf(\"%d\", f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'float'.\n", errout.str()); check("void foo(float f) {\n" " QString string;\n" " string = QString::asprintf(\"%d\", f);\n" "}", false, false, Settings::Win32A); ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'float'.\n", errout.str()); } void testTernary() { // ticket #6182 check("void test(const std::string &val) {\n" " printf(\"%s\", val.empty() ? \"I like to eat bananas\" : val.c_str());\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testUnsignedConst() { // ticket #6321 check("void test() {\n" " unsigned const x = 5;\n" " printf(\"%u\", x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testAstType() { // ticket #7014 check("void test() {\n" " printf(\"%c\", \"hello\"[0]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " printf(\"%lld\", (long long)1);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " printf(\"%i\", (short *)x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'signed short *'.\n", errout.str()); check("int (*fp)();\n" // #7178 - function pointer call "void test() {\n" " printf(\"%i\", fp());\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testPrintf0WithSuffix() { // ticket #7069 check("void foo() {\n" " printf(\"%u %lu %llu\", 0U, 0UL, 0ULL);\n" " printf(\"%u %lu %llu\", 0u, 0ul, 0ull);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testReturnValueTypeStdLib() { check("void f() {\n" " const char *s = \"0\";\n" " printf(\"%ld%lld\", atol(s), atoll(s));\n" "}"); ASSERT_EQUALS("", errout.str()); // 8141 check("void f(int i) {\n" " printf(\"%f\", imaxabs(i));\n" "}\n", false, true, Settings::Unix64); ASSERT_EQUALS("[test.cpp:2]: (portability) %f in format string (no. 1) requires 'double' but the argument type is 'intmax_t {aka signed long}'.\n", errout.str()); } void testPrintfTypeAlias1() { check("using INT = int;\n\n" "using PINT = INT *;\n" "using PCINT = const PINT;\n" "INT i;\n" "PINT pi;\n" "PCINT pci;" "void foo() {\n" " printf(\"%d %p %p\", i, pi, pci);\n" "};"); ASSERT_EQUALS("", errout.str()); check("using INT = int;\n\n" "using PINT = INT *;\n" "using PCINT = const PINT;\n" "INT i;\n" "PINT pi;\n" "PCINT pci;" "void foo() {\n" " printf(\"%f %f %f\", i, pi, pci);\n" "};"); ASSERT_EQUALS("[test.cpp:8]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n" "[test.cpp:8]: (warning) %f in format string (no. 2) requires 'double' but the argument type is 'signed int *'.\n" "[test.cpp:8]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'const signed int *'.\n", errout.str()); } void testPrintfAuto() { // #8992 check("void f() {\n" " auto s = sizeof(int);\n" " printf(\"%zu\", s);\n" " printf(\"%f\", s);\n" "}\n", false, true); ASSERT_EQUALS("[test.cpp:4]: (portability) %f in format string (no. 1) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n", errout.str()); } }; REGISTER_TEST(TestIO) cppcheck-1.90/test/testleakautovar.cpp000066400000000000000000002063651357737443600201630ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkleakautovar.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include class TestLeakAutoVar : public TestFixture { public: TestLeakAutoVar() : TestFixture("TestLeakAutoVar") { } private: Settings settings; void run() OVERRIDE { int id = 0; while (!Library::ismemory(++id)); settings.library.setalloc("malloc", id, -1); settings.library.setrealloc("realloc", id, -1); settings.library.setdealloc("free", id, 1); while (!Library::isresource(++id)); settings.library.setalloc("fopen", id, -1); settings.library.setrealloc("freopen", id, -1, 3); settings.library.setdealloc("fclose", id, 1); settings.library.smartPointers.insert("std::shared_ptr"); settings.library.smartPointers.insert("std::unique_ptr"); const char xmldata[] = "\n" "\n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); // Assign TEST_CASE(assign1); TEST_CASE(assign2); TEST_CASE(assign3); TEST_CASE(assign4); TEST_CASE(assign5); TEST_CASE(assign6); TEST_CASE(assign7); TEST_CASE(assign8); TEST_CASE(assign9); TEST_CASE(assign10); TEST_CASE(assign11); // #3942: x = a(b(p)); TEST_CASE(assign12); // #4236: FP. bar(&x); // TODO TEST_CASE(assign13); // #4237: FP. char*&ref=p; p=malloc(10); free(ref); TEST_CASE(assign14); TEST_CASE(assign15); TEST_CASE(assign16); TEST_CASE(assign17); // #9047 TEST_CASE(assign18); TEST_CASE(assign19); TEST_CASE(isAutoDealloc); TEST_CASE(realloc1); TEST_CASE(realloc2); TEST_CASE(realloc3); TEST_CASE(freopen1); TEST_CASE(freopen2); TEST_CASE(deallocuse1); TEST_CASE(deallocuse2); TEST_CASE(deallocuse3); TEST_CASE(deallocuse4); // TODO TEST_CASE(deallocuse5); // #4018: FP. free(p), p = 0; TEST_CASE(deallocuse6); // #4034: FP. x = p = f(); TEST_CASE(deallocuse7); // #6467, #6469, #6473 TEST_CASE(deallocuse8); // #1765 TEST_CASE(doublefree1); TEST_CASE(doublefree2); TEST_CASE(doublefree3); // #4914 TEST_CASE(doublefree4); // #5451 - FP when exit is called TEST_CASE(doublefree5); // #5522 TEST_CASE(doublefree6); // #7685 TEST_CASE(doublefree7); TEST_CASE(doublefree8); TEST_CASE(doublefree9); TEST_CASE(doublefree10); // #8706 // exit TEST_CASE(exit1); TEST_CASE(exit2); TEST_CASE(exit3); // handling function calls TEST_CASE(functioncall1); // goto TEST_CASE(goto1); TEST_CASE(goto2); // if/else TEST_CASE(ifelse1); TEST_CASE(ifelse2); TEST_CASE(ifelse3); TEST_CASE(ifelse4); TEST_CASE(ifelse5); TEST_CASE(ifelse6); // #3370 TEST_CASE(ifelse7); // #5576 - if (fd < 0) TEST_CASE(ifelse8); // #5747 - if (fd == -1) TEST_CASE(ifelse9); // #5273 - if (X(p==NULL, 0)) TEST_CASE(ifelse10); // #8794 - if (!(x!=NULL)) TEST_CASE(ifelse11); // #8365 - if (NULL == (p = malloc(4))) TEST_CASE(ifelse12); // #8340 - if ((*p = malloc(4)) == NULL) TEST_CASE(ifelse13); // #8392 TEST_CASE(ifelse14); // #9130 - if (x == (char*)NULL) TEST_CASE(ifelse15); // #9206 - if (global_ptr = malloc(1)) // switch TEST_CASE(switch1); // loops TEST_CASE(loop1); // mismatching allocation/deallocation TEST_CASE(mismatchAllocDealloc); TEST_CASE(smartPointerDeleter); TEST_CASE(smartPointerRelease); // Execution reaches a 'return' TEST_CASE(return1); TEST_CASE(return2); TEST_CASE(return3); TEST_CASE(return4); TEST_CASE(return5); TEST_CASE(return6); // #8282 return {p, p} TEST_CASE(return7); // #9343 return (uint8_t*)x TEST_CASE(return8); // General tests: variable type, allocation type, etc TEST_CASE(test1); TEST_CASE(test2); TEST_CASE(test3); // #3954 - reference pointer TEST_CASE(test4); // #5923 - static pointer TEST_CASE(test5); // unknown type // Execution reaches a 'throw' TEST_CASE(throw1); TEST_CASE(throw2); // Possible leak => Further configuration is needed for complete analysis TEST_CASE(configuration1); TEST_CASE(configuration2); TEST_CASE(configuration3); TEST_CASE(configuration4); TEST_CASE(ptrptr); TEST_CASE(nestedAllocation); TEST_CASE(testKeywords); // #6767 TEST_CASE(inlineFunction); // #3989 TEST_CASE(smartPtrInContainer); // #8262 TEST_CASE(recursiveCountLimit); // #5872 #6157 #9097 } void check(const char code[], bool cpp = false) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, cpp?"test.cpp":"test.c"); // Check for leaks.. CheckLeakAutoVar c; settings.checkLibrary = true; settings.addEnabled("information"); c.runChecks(&tokenizer, &settings, this); } void checkP(const char code[], bool cpp = false) { // Clear the error buffer.. errout.str(""); // Raw tokens.. std::vector files(1, cpp?"test.cpp":"test.c"); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenizer.. Tokenizer tokenizer(&settings, this); tokenizer.createTokens(&tokens2); tokenizer.simplifyTokens1(""); // Check for leaks.. CheckLeakAutoVar c; settings.checkLibrary = true; settings.addEnabled("information"); c.runChecks(&tokenizer, &settings, this); } void assign1() { check("void f() {\n" " char *p = malloc(10);\n" " p = NULL;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void assign2() { check("void f() {\n" " char *p = malloc(10);\n" " char *q = p;\n" " free(q);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign3() { check("void f() {\n" " char *p = malloc(10);\n" " char *q = p + 1;\n" " free(q - 1);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign4() { check("void f() {\n" " char *a = malloc(10);\n" " a += 10;\n" " free(a - 10);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign5() { check("void foo()\n" "{\n" " char *p = new char[100];\n" " list += p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign6() { // #2806 - FP when there is redundant assignment check("void foo() {\n" " char *p = malloc(10);\n" " p = strcpy(p,q);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign7() { check("void foo(struct str *d) {\n" " struct str *p = malloc(10);\n" " d->p = p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign8() { // linux list check("void foo(struct str *d) {\n" " struct str *p = malloc(10);\n" " d->p = &p->x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign9() { check("void foo() {\n" " char *p = x();\n" " free(p);\n" " p = NULL;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign10() { check("void foo() {\n" " char *p;\n" " if (x) { p = malloc(10); }\n" " if (!x) { p = NULL; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign11() { // #3942 - FP for x = a(b(p)); check("void f() {\n" " char *p = malloc(10);\n" " x = a(b(p));\n" "}"); ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function b() should have / configuration\n", errout.str()); } void assign12() { // #4236: FP. bar(&x) check("void f() {\n" " char *p = malloc(10);\n" " free(p);\n" " bar(&p);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign13() { // #4237: FP. char *&ref=p; p=malloc(10); free(ref); check("void f() {\n" " char *p;\n" " char * &ref = p;\n" " p = malloc(10);\n" " free(ref);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign14() { check("void f(int x) {\n" " char *p;\n" " if (x && (p = malloc(10))) { }" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); check("void f(int x) {\n" " char *p;\n" " if (x && (p = new char[10])) { }" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); } void assign15() { // #8120 check("void f() {\n" " baz *p;\n" " p = malloc(sizeof *p);\n" " free(p);\n" " p = malloc(sizeof *p);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign16() { check("void f() {\n" " char *p = malloc(10);\n" " free(p);\n" " if (p=dostuff()) *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign17() { // #9047 check("void f() {\n" " char *p = (char*)malloc(10);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); check("void f() {\n" " char *p = (char*)(int*)malloc(10);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void assign18() { check("void f(int x) {\n" " char *p;\n" " if (x && (p = (char*)malloc(10))) { }" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); check("void f(int x) {\n" " char *p;\n" " if (x && (p = (char*)(int*)malloc(10))) { }" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void assign19() { check("void f() {\n" " char *p = malloc(10);\n" " free((void*)p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void isAutoDealloc() { check("void f() {\n" " char *p = new char[100];" "}", true); ASSERT_EQUALS("[test.cpp:2]: (error) Memory leak: p\n", errout.str()); check("void f() {\n" " Fred *fred = new Fred;" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " std::string *str = new std::string;" "}", true); TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Memory leak: str\n", "", errout.str()); check("class TestType {\n" // #9028 "public:\n" " char ca[12];\n" "};\n" "void f() {\n" " TestType *tt = new TestType();\n" "}", true); ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: tt\n", errout.str()); } void realloc1() { check("void f() {\n" " void *p = malloc(10);\n" " void *q = realloc(p, 20);\n" " free(q)\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc2() { check("void f() {\n" " void *p = malloc(10);\n" " void *q = realloc(p, 20);\n" "}"); ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout.str()); } void realloc3() { check("void f() {\n" " char *p = malloc(10);\n" " char *q = (char*) realloc(p, 20);\n" "}"); ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout.str()); } void freopen1() { check("void f() {\n" " void *p = fopen(name,a);\n" " void *q = freopen(name, b, p);\n" " fclose(q)\n" "}"); ASSERT_EQUALS("", errout.str()); } void freopen2() { check("void f() {\n" " void *p = fopen(name,a);\n" " void *q = freopen(name, b, p);\n" "}"); ASSERT_EQUALS("[test.c:4]: (error) Resource leak: q\n", errout.str()); } void deallocuse1() { check("void f(char *p) {\n" " free(p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str()); check("void f(char *p) {\n" " free(p);\n" " char c = *p;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str()); } void deallocuse2() { check("void f(char *p) {\n" " free(p);\n" " strcpy(a, p);\n" "}"); TODO_ASSERT_EQUALS("error (free,use)", "[test.c:3]: (information) --check-library: Function strcpy() should have configuration\n", errout.str()); check("void f(char *p) {\n" // #3041 - assigning pointer when it's used " free(p);\n" " strcpy(a, p=b());\n" "}"); TODO_ASSERT_EQUALS("", "[test.c:3]: (information) --check-library: Function strcpy() should have configuration\n", errout.str()); } void deallocuse3() { check("void f(struct str *p) {\n" " free(p);\n" " p = p->next;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str()); } void deallocuse4() { check("void f(char *p) {\n" " free(p);\n" " return p;\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", errout.str()); check("void f(char *p) {\n" " if (!p) free(p);\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *p) {\n" " if (!p) delete p;\n" " return p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(char *p) {\n" " if (!p) delete [] p;\n" " return p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(void* p) {\n" " if (a) {\n" " free(p);\n" " return;\n" " }\n" " g(p);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void deallocuse5() { // #4018 check("void f(char *p) {\n" " free(p), p = 0;\n" " *p = 0;\n" // <- Make sure pointer info is reset. It is NOT a freed pointer dereference "}"); ASSERT_EQUALS("", errout.str()); } void deallocuse6() { // #4034 check("void f(char *p) {\n" " free(p);\n" " x = p = foo();\n" // <- p is not dereferenced "}"); ASSERT_EQUALS("", errout.str()); } void deallocuse7() { // #6467, #6469, #6473, #6648 check("struct Foo { int* ptr; };\n" "void f(Foo* foo) {\n" " delete foo->ptr;\n" " foo->ptr = new Foo; \n" "}", true); ASSERT_EQUALS("", errout.str()); check("struct Foo { int* ptr; };\n" "void f(Foo* foo) {\n" " delete foo->ptr;\n" " x = *foo->ptr; \n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'ptr' after it is deallocated / released\n", errout.str()); check("void parse() {\n" " struct Buf {\n" " Buf(uint32_t len) : m_buf(new uint8_t[len]) {}\n" " ~Buf() { delete[]m_buf; }\n" " uint8_t *m_buf;\n" " };\n" "}", true); ASSERT_EQUALS("", errout.str()); check("struct Foo {\n" " Foo();\n" " Foo* ptr;\n" " void func();\n" "};\n" "void bar(Foo* foo) {\n" " delete foo->ptr;\n" " foo->ptr = new Foo;\n" " foo->ptr->func();\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(void (*conv)(char**)) {\n" " char * ptr=(char*)malloc(42);\n" " free(ptr);\n" " (*conv)(&ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void deallocuse8() { // #1765 check("void f() {\n" " int *ptr = new int;\n" " delete(ptr);\n" " *ptr = 0;\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'ptr' after it is deallocated / released\n", errout.str()); } void doublefree1() { // #3895 check("void f(char *p) {\n" " if (x)\n" " free(p);\n" " else\n" " p = 0;\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:3] -> [test.c:6]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p) {\n" " free(p);\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p, char *r) {\n" " free(p);\n" " free(r);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo() {\n" " free(p);\n" " free(r);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " if (x < 3) free(p);\n" " else { if (x > 9) free(p); }\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " free(p);\n" " getNext(&p);\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " free(p);\n" " bar();\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p) {\n" " free(p);\n" " printf(\"Freed memory at location %x\", p);\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(FILE *p) {\n" " fclose(p);\n" " fclose(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Resource handle 'p' freed twice.\n", errout.str()); check( "void foo(FILE *p, FILE *r) {\n" " fclose(p);\n" " fclose(r);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(FILE *p) {\n" " if (x < 3) fclose(p);\n" " else { if (x > 9) fclose(p); }\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(FILE *p) {\n" " fclose(p);\n" " gethandle(&p);\n" " fclose(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(FILE *p) {\n" " fclose(p);\n" " gethandle();\n" " fclose(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Resource handle 'p' freed twice.\n", errout.str()); check( "void foo(Data* p) {\n" " free(p->a);\n" " free(p->b);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void f() {\n" " char *p; p = malloc(100);\n" " if (x) {\n" " free(p);\n" " exit();\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void f() {\n" " char *p; p = malloc(100);\n" " if (x) {\n" " free(p);\n" " x = 0;\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:4] -> [test.c:7]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void f() {\n" " char *p; p = do_something();\n" " free(p);\n" " p = do_something();\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete p;\n" " delete p;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p, char *r) {\n" " delete p;\n" " delete r;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(P p) {\n" " delete p.x;\n" " delete p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char **p) {\n" " delete p[0];\n" " delete p[1];\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete p;\n" " getNext(&p);\n" " delete p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete p;\n" " bar();\n" " delete p;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p) {\n" " delete[] p;\n" " delete[] p;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void foo(char *p, char *r) {\n" " delete[] p;\n" " delete[] r;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete[] p;\n" " getNext(&p);\n" " delete[] p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(char *p) {\n" " delete[] p;\n" " bar();\n" " delete[] p;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "LineMarker::~LineMarker() {\n" " delete pxpm;\n" "}\n" "LineMarker &LineMarker::operator=(const LineMarker &) {\n" " delete pxpm;\n" " pxpm = NULL;\n" " return *this;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo()\n" "{\n" " int* ptr; ptr = NULL;\n" " try\n" " {\n" " ptr = new int(4);\n" " }\n" " catch(...)\n" " {\n" " delete ptr;\n" " throw;\n" " }\n" " delete ptr;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "int foo()\n" "{\n" " int* a; a = new int;\n" " bool doDelete; doDelete = true;\n" " if (a != 0)\n" " {\n" " doDelete = false;\n" " delete a;\n" " }\n" " if(doDelete)\n" " delete a;\n" " return 0;\n" "}", true); TODO_ASSERT_EQUALS("", "[test.cpp:8] -> [test.cpp:11]: (error) Memory pointed to by 'a' is freed twice.\n", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " while(true) {\n" " x = new char[100];\n" " if (y++ > 100)\n" " break;\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " for (int i = 0; i < 10000; i++) {\n" " x = new char[100];\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}", true); TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " while (isRunning()) {\n" " x = new char[100];\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}", true); TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " while (isRunning()) {\n" " x = malloc(100);\n" " free(x);\n" " }\n" " free(x);\n" "}"); TODO_ASSERT_EQUALS("[test.c:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " for (;;) {\n" " x = new char[100];\n" " if (y++ > 100)\n" " break;\n" " delete[] x;\n" " }\n" " delete[] x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void foo(int y)\n" "{\n" " char * x; x = NULL;\n" " do {\n" " x = new char[100];\n" " if (y++ > 100)\n" " break;\n" " delete[] x;\n" " } while (true);\n" " delete[] x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void f()\n" "{\n" " char *p; p = 0;\n" " if (x < 100) {\n" " p = malloc(10);\n" " free(p);\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:6] -> [test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); check( "void MyFunction()\n" "{\n" " char* data; data = new char[100];\n" " try\n" " {\n" " }\n" " catch(err)\n" " {\n" " delete[] data;\n" " MyThrow(err);\n" " }\n" " delete[] data;\n" "}\n" "void MyThrow(err)\n" "{\n" " throw(err);\n" "}", true); ASSERT_EQUALS("", errout.str()); check( "void MyFunction()\n" "{\n" " char* data; data = new char[100];\n" " try\n" " {\n" " }\n" " catch(err)\n" " {\n" " delete[] data;\n" " MyExit(err);\n" " }\n" " delete[] data;\n" "}\n" "void MyExit(err)\n" "{\n" " exit(err);\n" "}", true); ASSERT_EQUALS("", errout.str()); check( // #6252 "struct Wrapper {\n" " Thing* m_thing;\n" " Wrapper() : m_thing(0) {\n" " }\n" " ~Wrapper() {\n" " delete m_thing;\n" " }\n" " void changeThing() {\n" " delete m_thing;\n" " m_thing = new Thing;\n" " }\n" "};", true); ASSERT_EQUALS("", errout.str()); // #7401 check("void pCodeLabelDestruct(pCode *pc) {\n" " free(PCL(pc)->label);\n" " free(pc);\n" "}"); ASSERT_EQUALS("", errout.str()); } void doublefree2() { // #3891 check("void *f(int a) {\n" " char *p = malloc(10);\n" " if (a == 2) { free(p); return ((void*)1); }\n" " free(p);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void doublefree3() { // #4914 check("void foo() {\n" " bool done = false;\n" " do {\n" " char *bar = malloc(10)\n" " if(condition()) {\n" " free(bar);\n" " continue;\n" " }\n" " done = true;\n" " free(bar)\n" " } while(!done);\n" " return;" "}" ); ASSERT_EQUALS("", errout.str()); } void doublefree4() { // #5451 - exit check("void f(char *p) {\n" " if (x) {\n" " free(p);\n" " exit(1);\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void doublefree5() { // #5522 check("void f(char *p) {\n" " free(p);\n" " x = (q == p);\n" " free(p);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); } void doublefree6() { // #7685 check("void do_wordexp(FILE *f) {\n" " free(getword(f));\n" " fclose(f);\n" "}", /*cpp=*/false); ASSERT_EQUALS("", errout.str()); } void doublefree7() { check("void f(char *p, int x) {\n" " free(p);\n" " if (x && (p = malloc(10)))\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *p, int x) {\n" " delete[] p;\n" " if (x && (p = new char[10]))\n" " delete[] p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void doublefree8() { check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x(i);\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " delete i;\n" " std::unique_ptr x(i);\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x{i};\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::shared_ptr x(i);\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::shared_ptr x{i};\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); // Check for use-after-free FP check("void f() {\n" " int * i = new int;\n" " std::shared_ptr x{i};\n" " *i = 123;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * i = new int[1];\n" " std::unique_ptr x(i);\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str()); } void doublefree9() { check("struct foo {\n" " int* get(int) { return new int(); }\n" "};\n" "void f(foo* b) {\n" " std::unique_ptr x(b->get(0));\n" " std::unique_ptr y(b->get(1));\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void doublefree10() { check("void f(char* s) {\n" " char *p = malloc(strlen(s));\n" " if (p != NULL) {\n" " strcat(p, s);\n" " if (strlen(s) != 10)\n" " free(p); p = NULL;\n" " }\n" " if (p != NULL)\n" " free(p);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f(char* s) {\n" " char *p = malloc(strlen(s));\n" " if (p != NULL) {\n" " strcat(p, s);\n" " if (strlen(s) != 10)\n" " free(p), p = NULL;\n" " }\n" " if (p != NULL)\n" " free(p);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void exit1() { check("void f() {\n" " char *p = malloc(10);\n" " exit(0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void exit2() { check("void f() {\n" " char *p = malloc(10);\n" " fatal_error();\n" "}"); ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function fatal_error() should have configuration\n" "[test.c:4]: (information) --check-library: Function fatal_error() should have / configuration\n", errout.str()); } void exit3() { check("void f() {\n" " char *p = malloc(100);\n" " if (x) {\n" " free(p);\n" " ::exit(0);\n" " }" " free(p);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char *p = malloc(100);\n" " if (x) {\n" " free(p);\n" " std::exit(0);\n" " }" " free(p);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void functioncall1() { check("void f(struct S *p) {\n" " p->x = malloc(10);\n" " free(p->x);\n" " p->x = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void goto1() { check("static void f() {\n" " int err = -ENOMEM;\n" " char *reg = malloc(100);\n" " if (err) {\n" " free(reg);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void goto2() { // #4231 check("static char * f() {\n" "x:\n" " char *p = malloc(100);\n" " if (err) {\n" " free(p);\n" " goto x;\n" " }\n" " return p;\n" // no error since there is a goto "}"); ASSERT_EQUALS("", errout.str()); } void ifelse1() { check("int f() {\n" " char *p = NULL;\n" " if (x) { p = malloc(10); }\n" " else { return 0; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse2() { check("int f() {\n" " char *p = NULL;\n" " if (x) { p = malloc(10); }\n" " else { return 0; }\n" "}"); ASSERT_EQUALS("[test.c:5]: (error) Memory leak: p\n", errout.str()); } void ifelse3() { check("void f() {\n" " char *p = malloc(10);\n" " if (!p) { return; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); check("char * f(size_t size) {" " void *p = malloc(1);" " if (!p && size != 0)" " return NULL;" " return p;" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char *p = malloc(10);\n" " if (p) { } else { return; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); // #3866 - UNLIKELY check("void f() {\n" " char *p = malloc(10);\n" " if (UNLIKELY(!p)) { return; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse4() { check("void f(int x) {\n" " char *p;\n" " if (x) { p = malloc(10); }\n" " if (x) { free(p); }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " char *p;\n" " if (x) { p = malloc(10); }\n" " if (!x) { return; }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse5() { check("void f() {\n" " char *p = malloc(10);\n" " if (!p && x) { p = malloc(10); }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse6() { // #3370 check("void f(int x) {\n" " int *a = malloc(20);\n" " if (x)\n" " free(a);\n" " else\n" " a = 0;\n" "}"); ASSERT_EQUALS("[test.c:6]: (error) Memory leak: a\n", errout.str()); } void ifelse7() { // #5576 check("void f() {\n" " int x = malloc(20);\n" " if (x < 0)\n" // assume negative value indicates its unallocated " return;\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse8() { // #5747 check("void f() {\n" " int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" " if (fd == -1)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse9() { // #5273 check("void f() {\n" " char *p = malloc(100);\n" " if (dostuff(p==NULL,0))\n" " return;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse10() { // #8794 check("void f() {\n" " void *x = malloc(1U);\n" " if (!(x != NULL))\n" " return;\n" " free(x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse11() { // #8365 check("void f() {\n" " void *p;\n" " if (NULL == (p = malloc(4)))\n" " return;\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse12() { // #8340 check("void f(char **p) {\n" " if ((*p = malloc(4)) == NULL)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse13() { // #8392 check("int f(int fd, const char *mode) {\n" " char *path;\n" " if (fd == -1 || (path = (char *)malloc(10)) == NULL)\n" " return 1;\n" " free(path);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int fd, const char *mode) {\n" " char *path;\n" " if ((path = (char *)malloc(10)) == NULL || fd == -1)\n" " return 1;\n" // <- memory leak " free(path);\n" " return 0;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4] memory leak", "", errout.str()); } void ifelse14() { // #9130 check("char* f() {\n" " char* buf = malloc(10);\n" " if (buf == (char*)NULL)\n" " return NULL;\n" " return buf;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse15() { // #9206 check("struct SSS { int a; };\n" "SSS* global_ptr;\n" "void test_alloc() {\n" " if ( global_ptr = new SSS()) {}\n" " return;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("FILE* hFile;\n" "int openFile( void ) {\n" " if ((hFile = fopen(\"1.txt\", \"wb\" )) == NULL) return 0;\n" " return 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void switch1() { check("void f() {\n" " char *p = 0;\n" " switch (x) {\n" " case 123: p = malloc(100); break;\n" " default: return;\n" " }\n" " free(p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void loop1() { // test the handling of { } check("void f() {\n" " char *p;\n" " for (i=0;i<5;i++) { }\n" " if (x) { free(p) }\n" " else { a = p; }\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatchAllocDealloc() { check("void f() {\n" " FILE*f=fopen(fname,a);\n" " free(f);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " free((void*)f);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f() {\n" " char *cPtr = new char[100];\n" " delete[] cPtr;\n" " cPtr = new char[100]('x');\n" " delete[] cPtr;\n" " cPtr = new char[100];\n" " delete cPtr;\n" "}", true); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str()); check("void f() {\n" " char *cPtr = new char[100];\n" " free(cPtr);\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str()); check("void f() {\n" " char *cPtr = new (buf) char[100];\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * i = new int[1];\n" " std::unique_ptr x(i);\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x(i);\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str()); check("void f() {\n" " void* a = malloc(1);\n" " void* b = freopen(f, p, a);\n" " free(b);\n" "}"); ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: a\n" "[test.c:3] -> [test.c:4]: (error) Mismatching allocation and deallocation: b\n", errout.str()); check("void f() {\n" " void* a;\n" " void* b = realloc(a, 10);\n" " free(b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * i = new int;\n" " int * j = realloc(i, 2 * sizeof(int));\n" " delete[] j;\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n" "[test.cpp:3] -> [test.cpp:4]: (error) Mismatching allocation and deallocation: j\n", errout.str()); } void smartPointerDeleter() { check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::unique_ptr fp{f};\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::unique_ptr fp{f, &fclose};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, &fclose};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("struct deleter { void operator()(FILE* f) { fclose(f); }};\n" "void f() {\n" " FILE*f=fopen(fname,a);\n" " std::unique_ptr fp{f};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int * create();\n" "void destroy(int * x);\n" "void f() {\n" " int x * = create()\n" " std::unique_ptr xp{x, &destroy()};\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("int * create();\n" "void destroy(int * x);\n" "void f() {\n" " int x * = create()\n" " std::unique_ptr xp(x, &destroy());\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, [](FILE* x) { fclose(x); }};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, +[](FILE* x) { fclose(x); }};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, [](FILE* x) { free(f); }};\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr fp{f, [](FILE* x) {}};\n" "}", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str()); check("class C;\n" "void f() {\n" " C* c = new C{};\n" " std::shared_ptr a{c, [](C*) {}};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("class C;\n" "void f() {\n" " C* c = new C{};\n" " std::shared_ptr a{c, [](C* x) { delete x; }};\n" "}", true); ASSERT_EQUALS("", errout.str()); } void smartPointerRelease() { check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x(i);\n" " x.release();\n" " delete i;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * i = new int;\n" " std::unique_ptr x(i);\n" " x.release();\n" "}\n", true); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: i\n", errout.str()); } void return1() { check("int f() {\n" " char *p = malloc(100);\n" " return 123;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void return2() { check("char *f() {\n" " char *p = malloc(100);\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void return3() { check("struct dev * f() {\n" " struct ABC *abc = malloc(100);\n" " return &abc->dev;\n" "}"); ASSERT_EQUALS("", errout.str()); } void return4() { // ticket #3862 // avoid false positives check("void f(char *p, int x) {\n" " if (x==12) {\n" " free(p);\n" " throw 1;\n" " }\n" " free(p);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(char *p, int x) {\n" " if (x==12) {\n" " delete p;\n" " throw 1;\n" " }\n" " delete p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(char *p, int x) {\n" " if (x==12) {\n" " delete [] p;\n" " throw 1;\n" " }\n" " delete [] p;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void return5() { // ticket #6397 - conditional allocation/deallocation and conditional return // avoid false positives check("void f(int *p, int x) {\n" " if (x != 0) {\n" " free(p);\n" " }\n" " if (x != 0) {\n" " return;\n" " }\n" " *p = 0;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void return6() { // #8282 check("std::pair f(size_t n) {\n" " char* p = (char* )malloc(n);\n" " return {p, p};\n" "}", true); ASSERT_EQUALS("", errout.str()); } void return7() { // #9343 check("uint8_t *f() {\n" " void *x = malloc(1);\n" " return (uint8_t *)x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("uint8_t f() {\n" " void *x = malloc(1);\n" " return (uint8_t)x;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str()); check("void** f() {\n" " void *x = malloc(1);\n" " return (void**)x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return (long long)x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return (void*)(short)x;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return (mytype)x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" // Do not crash " void *x = malloc(1);\n" " return (mytype)y;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str()); } void return8() { check("void* f() {\n" " void *x = malloc(1);\n" " return (x);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return ((x));\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void* f() {\n" " void *x = malloc(1);\n" " return ((((x))));\n" "}", true); ASSERT_EQUALS("", errout.str()); check("char* f() {\n" " void *x = malloc(1);\n" " return (char*)(x);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void test1() { // 3809 check("void f(double*&p) {\n" " p = malloc(0x100);\n" "}"); ASSERT_EQUALS("", errout.str()); } void test2() { // 3899 check("struct Fred {\n" " char *p;\n" " void f1() { free(p); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void test3() { // 3954 - reference pointer check("void f() {\n" " char *&p = x();\n" " p = malloc(10);\n" "};"); ASSERT_EQUALS("", errout.str()); } void test4() { // 5923 - static pointer check("void f() {\n" " static char *p;\n" " if (!p) p = malloc(10);\n" " if (x) { free(p); p = 0; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void test5() { // unknown type check("void f() { Fred *p = malloc(10); }", true); ASSERT_EQUALS("[test.cpp:1]: (error) Memory leak: p\n", errout.str()); check("void f() { Fred *p = malloc(10); }", false); ASSERT_EQUALS("[test.c:1]: (error) Memory leak: p\n", errout.str()); check("void f() { Fred *p = new Fred; }", true); ASSERT_EQUALS("", errout.str()); check("void f() { Fred fred = malloc(10); }", true); ASSERT_EQUALS("", errout.str()); } void throw1() { // 3987 - Execution reach a 'throw' check("void f() {\n" " char *p = malloc(10);\n" " throw 123;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); check("void f() {\n" " char *p;\n" " try {\n" " p = malloc(10);\n" " throw 123;\n" " } catch (...) { }\n" " free(p);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void throw2() { // do not miss ::NS::Except() check("namespace NS {\n" " class Except {\n" " };\n" "}\n" "void foo(int i)\n" "{\n" " int *pi = new int;\n" " if (i == 42) {\n" " delete pi;\n" " throw ::NS::Except();\n" " }\n" " delete pi;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void configuration1() { // Possible leak => configuration is required for complete analysis // The user should be able to "white list" and "black list" functions. // possible leak. If the function 'x' deallocates the pointer or // takes the address, there is no leak. check("void f() {\n" " char *p = malloc(10);\n" " x(p);\n" "}"); ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function x() should have configuration\n" "[test.c:4]: (information) --check-library: Function x() should have / configuration\n", errout.str()); } void configuration2() { // possible leak. If the function 'x' deallocates the pointer or // takes the address, there is no leak. check("void f() {\n" " char *p = malloc(10);\n" " x(&p);\n" "}"); ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function x() should have configuration\n" "[test.c:4]: (information) --check-library: Function x() should have / configuration\n", errout.str()); } void configuration3() { const char * code = "void f() {\n" " char *p = malloc(10);\n" " if (set_data(p)) { }\n" "}"; check(code); ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n", errout.str()); check(code, true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function set_data() should have / configuration\n", errout.str()); code = "void f() {\n" " char *p = malloc(10);\n" " if (set_data(p)) { return; }\n" "}"; check(code); ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function set_data() should have / configuration\n" "[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n" , errout.str()); check(code, true); ASSERT_EQUALS("[test.cpp:3]: (information) --check-library: Function set_data() should have / configuration\n" "[test.cpp:4]: (information) --check-library: Function set_data() should have / configuration\n" , errout.str()); } void configuration4() { check("void f() {\n" " char *p = malloc(10);\n" " int ret = set_data(p);\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n", errout.str()); } void ptrptr() { check("void f() {\n" " char **p = malloc(10);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str()); } void nestedAllocation() { check("void QueueDSMCCPacket(unsigned char *data, int length) {\n" " unsigned char *dataCopy = malloc(length * sizeof(unsigned char));\n" " m_dsmccQueue.enqueue(new DSMCCPacket(dataCopy));\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function DSMCCPacket() should have / configuration\n", errout.str()); check("void QueueDSMCCPacket(unsigned char *data, int length) {\n" " unsigned char *dataCopy = malloc(length * sizeof(unsigned char));\n" " m_dsmccQueue.enqueue(new DSMCCPacket(somethingunrelated));\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: dataCopy\n", errout.str()); check("void f() {\n" " char *buf = new char[1000];\n" " clist.push_back(new (std::nothrow) C(buf));\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function C() should have / configuration\n", errout.str()); } void testKeywords() { check("int main(int argc, char **argv) {\n" " double *new = malloc(1*sizeof(double));\n" " free(new);\n" " return 0;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void inlineFunction() { check("int test() {\n" " char *c;\n" " int ret() {\n" " free(c);\n" " return 0;\n" " }\n" " c = malloc(128);\n" " return ret();\n" "}"); ASSERT_EQUALS("", errout.str()); } // #8262 void smartPtrInContainer() { check("std::list< std::shared_ptr > mList;\n" "void test(){\n" " int *pt = new int(1);\n" " mList.push_back(std::shared_ptr(pt));\n" "}\n", true ); ASSERT_EQUALS("", errout.str()); } void recursiveCountLimit() { // #5872 #6157 #9097 ASSERT_THROW(checkP("#define ONE else if (0) { }\n" "#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n" "#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n" "#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n" "void foo() {\n" " if (0) { }\n" " THOU THOU\n" "}"), InternalError); ASSERT_NO_THROW(checkP("#define ONE if (0) { }\n" "#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n" "#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n" "#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n" "void foo() {\n" " if (0) { }\n" " THOU THOU\n" "}")); } }; REGISTER_TEST(TestLeakAutoVar) class TestLeakAutoVarWindows : public TestFixture { public: TestLeakAutoVarWindows() : TestFixture("TestLeakAutoVarWindows") { } private: Settings settings; void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.c"); // Check for leaks.. CheckLeakAutoVar checkLeak; checkLeak.runChecks(&tokenizer, &settings, this); } void run() OVERRIDE { LOAD_LIB_2(settings.library, "windows.cfg"); TEST_CASE(heapDoubleFree); } void heapDoubleFree() { check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" " HeapFree(MyHeap, 0, b);" " HeapDestroy(MyHeap);" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {" " int *a = HeapAlloc(GetProcessHeap(), 0, sizeof(int));" " int *b = HeapAlloc(GetProcessHeap(), 0, sizeof(int));" " HeapFree(GetProcessHeap(), 0, a);" " HeapFree(GetProcessHeap(), 0, b);" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" " HeapDestroy(MyHeap);" "}"); ASSERT_EQUALS("[test.c:1]: (error) Memory leak: b\n", errout.str()); check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" " HeapFree(MyHeap, 0, b);" "}"); TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap", "", errout.str()); check("void f() {" " HANDLE MyHeap = HeapCreate(0, 0, 0);" " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" " HeapFree(MyHeap, 0, a);" "}"); TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: MyHeap\n" "[test.c:1] (error) Memory leak: b", "[test.c:1]: (error) Memory leak: b\n", errout.str()); } }; REGISTER_TEST(TestLeakAutoVarWindows) cppcheck-1.90/test/testlibrary.cpp000066400000000000000000001273351357737443600173100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "errorlogger.h" #include "library.h" #include "settings.h" #include "standards.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include #include #define ASSERT_EQ(expected, actual) ASSERT(expected == actual) class TestLibrary : public TestFixture { public: TestLibrary() : TestFixture("TestLibrary") { } private: Settings settings; void run() OVERRIDE { TEST_CASE(empty); TEST_CASE(function); TEST_CASE(function_match_scope); TEST_CASE(function_match_args); TEST_CASE(function_match_args_default); TEST_CASE(function_match_var); TEST_CASE(function_arg); TEST_CASE(function_arg_any); TEST_CASE(function_arg_variadic); TEST_CASE(function_arg_direction); TEST_CASE(function_arg_valid); TEST_CASE(function_arg_minsize); TEST_CASE(function_namespace); TEST_CASE(function_method); TEST_CASE(function_baseClassMethod); // calling method in base class TEST_CASE(function_warn); TEST_CASE(memory); TEST_CASE(memory2); // define extra "free" allocation functions TEST_CASE(memory3); TEST_CASE(resource); TEST_CASE(podtype); TEST_CASE(container); TEST_CASE(version); TEST_CASE(loadLibErrors); } static Library::Error readLibrary(Library& library, const char* xmldata) { tinyxml2::XMLDocument doc; doc.Parse(xmldata); return library.load(doc); } void empty() const { // Reading an empty library file is considered to be OK const char xmldata[] = "\n"; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.functions.empty()); } void function() const { const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" ""; TokenList tokenList(nullptr); std::istringstream istr("foo();"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(library.functions.size(), 1U); ASSERT(library.functions.at("foo").argumentChecks.empty()); ASSERT(library.isnotnoreturn(tokenList.front())); } void function_match_scope() const { const char xmldata[] = "\n" "\n" " \n" " " " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); { TokenList tokenList(nullptr); std::istringstream istr("fred.foo(123);"); // <- wrong scope, not library function tokenList.createTokens(istr); ASSERT(library.isNotLibraryFunction(tokenList.front()->tokAt(2))); } { TokenList tokenList(nullptr); std::istringstream istr("Fred::foo(123);"); // <- wrong scope, not library function tokenList.createTokens(istr); ASSERT(library.isNotLibraryFunction(tokenList.front()->tokAt(2))); } } void function_match_args() const { const char xmldata[] = "\n" "\n" " \n" " " " \n" ""; TokenList tokenList(nullptr); std::istringstream istr("foo();"); // <- too few arguments, not library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.isNotLibraryFunction(tokenList.front())); } void function_match_args_default() const { const char xmldata[] = "\n" "\n" " \n" " " " " " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); { TokenList tokenList(nullptr); std::istringstream istr("foo();"); // <- too few arguments, not library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); ASSERT(library.isNotLibraryFunction(tokenList.front())); } { TokenList tokenList(nullptr); std::istringstream istr("foo(a);"); // <- library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); ASSERT(!library.isNotLibraryFunction(tokenList.front())); } { TokenList tokenList(nullptr); std::istringstream istr("foo(a, b);"); // <- library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); ASSERT(!library.isNotLibraryFunction(tokenList.front())); } { TokenList tokenList(nullptr); std::istringstream istr("foo(a, b, c);"); // <- too much arguments, not library function tokenList.createTokens(istr); Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); tokenList.createAst(); ASSERT(library.isNotLibraryFunction(tokenList.front())); } } void function_match_var() const { const char xmldata[] = "\n" "\n" " \n" " " " \n" ""; TokenList tokenList(nullptr); std::istringstream istr("Fred foo(123);"); // <- Variable declaration, not library function tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); tokenList.front()->next()->varId(1); Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.isNotLibraryFunction(tokenList.front()->next())); } void function_arg() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[1].notuninit); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[2].notnull); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[3].formatstr); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[4].strz); ASSERT_EQUALS(false, library.functions["foo"].argumentChecks[4].optional); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[5].notbool); ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[5].optional); } void function_arg_any() const { const char xmldata[] = "\n" "\n" "\n" " \n" "\n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[-1].notuninit); } void function_arg_variadic() const { const char xmldata[] = "\n" "\n" "\n" " \n" " \n" "\n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[-1].notuninit); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d,e);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); ASSERT_EQUALS(false, library.isuninitargbad(tokenList.front(), 1)); ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 2)); ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 3)); ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 4)); } void function_arg_direction() const { const char xmldata[] = "\n" "\n" "\n" " \n" " \n" " \n" " \n" "\n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); ASSERT(Library::ArgumentChecks::Direction::DIR_IN == library.getArgDirection(tokenList.front(), 1)); ASSERT(Library::ArgumentChecks::Direction::DIR_OUT == library.getArgDirection(tokenList.front(), 2)); ASSERT(Library::ArgumentChecks::Direction::DIR_INOUT == library.getArgDirection(tokenList.front(), 3)); ASSERT(Library::ArgumentChecks::Direction::DIR_UNKNOWN == library.getArgDirection(tokenList.front(), 4)); } void function_arg_valid() const { const char xmldata[] = "\n" "\n" " \n" " 1:\n" " -7:0\n" " 1:5,8\n" " -1,5\n" " :1,5\n" " 1.5:\n" " -6.7:-5.5,-3.3:-2.7\n" " 0.0:\n" " :2.0\n" " 0.0\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d,e,f,g,h,i,j);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); // 1- ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 1, -10)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 1, -10.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 1, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 1, 0.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 1, 1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 1, 1.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 1, 10)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 1, 10.0)); // -7-0 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 2, -10)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -10.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -7.5)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -7.1)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, -7)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -7.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, -3)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -3.0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -3.5)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, 0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, 0.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, 0.5)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 2, 1)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, 1.0)); // 1-5,8 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 0.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 1.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 3)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 3.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 5)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 5.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 6)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 6.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 7)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 7.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 8)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 8.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 9)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 9.0)); // -1,5 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 4, -10)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, -10.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 4, -1)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, -1.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, 5.000001)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, 5.5)); // :1,5 ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 5, -10)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 5, -10.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 5, 1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 5, 1.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 5, 2)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 5, 2.0)); // 1.5: ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 6, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 6, 0.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 6, 1)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 6, 1.499999)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 6, 1.5)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 6, 2)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 6, 10)); // -6.7:-5.5,-3.3:-2.7 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, -7)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -7.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -6.7000001)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -6.7)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 7, -6)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -6.0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -5.5)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -5.4999999)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -3.3000001)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -3.3)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 7, -3)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -3.0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -2.7)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -2.6999999)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, -2)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -2.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 0.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 3)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 3.0)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 6)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 6.0)); // 0.0: ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 8, -1)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 8, -1.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 8, -0.00000001)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 8, 0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 0.0)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 0.000000001)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 8, 1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 1.0)); // :2.0 ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 9, -1)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 9, -1.0)); ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 9, 2)); ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 9, 2.0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 9, 2.00000001)); ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 9, 200)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 9, 200.0)); // 0.0 ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 10, 0)); ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 10, 0.0)); } void function_arg_minsize() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); TokenList tokenList(nullptr); std::istringstream istr("foo(a,b,c,d);"); tokenList.createTokens(istr); tokenList.front()->next()->astOperand1(tokenList.front()); // arg1: type=strlen arg2 const std::vector *minsizes = library.argminsizes(tokenList.front(),1); ASSERT_EQUALS(true, minsizes != nullptr); ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); if (minsizes && minsizes->size() == 1U) { const Library::ArgumentChecks::MinSize &m = minsizes->front(); ASSERT_EQUALS(Library::ArgumentChecks::MinSize::STRLEN, m.type); ASSERT_EQUALS(2, m.arg); } // arg2: type=argvalue arg3 minsizes = library.argminsizes(tokenList.front(), 2); ASSERT_EQUALS(true, minsizes != nullptr); ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); if (minsizes && minsizes->size() == 1U) { const Library::ArgumentChecks::MinSize &m = minsizes->front(); ASSERT_EQUALS(Library::ArgumentChecks::MinSize::ARGVALUE, m.type); ASSERT_EQUALS(3, m.arg); } // arg4: type=value minsizes = library.argminsizes(tokenList.front(), 4); ASSERT_EQUALS(true, minsizes != nullptr); ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); if (minsizes && minsizes->size() == 1U) { const Library::ArgumentChecks::MinSize &m = minsizes->front(); ASSERT_EQUALS(Library::ArgumentChecks::MinSize::VALUE, m.type); ASSERT_EQUALS(500, m.value); } } void function_namespace() const { const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(library.functions.size(), 2U); ASSERT(library.functions.at("Foo::foo").argumentChecks.empty()); ASSERT(library.functions.at("bar").argumentChecks.empty()); { TokenList tokenList(nullptr); std::istringstream istr("Foo::foo();"); tokenList.createTokens(istr); ASSERT(library.isnotnoreturn(tokenList.front()->tokAt(2))); } { TokenList tokenList(nullptr); std::istringstream istr("bar();"); tokenList.createTokens(istr); ASSERT(library.isnotnoreturn(tokenList.front())); } } void function_method() const { const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT_EQUALS(library.functions.size(), 1U); { Tokenizer tokenizer(&settings, nullptr); std::istringstream istr("CString str; str.Format();"); tokenizer.tokenize(istr, "test.cpp"); ASSERT(library.isnotnoreturn(Token::findsimplematch(tokenizer.tokens(), "Format"))); } { Tokenizer tokenizer(&settings, nullptr); std::istringstream istr("HardDrive hd; hd.Format();"); tokenizer.tokenize(istr, "test.cpp"); ASSERT(!library.isnotnoreturn(Token::findsimplematch(tokenizer.tokens(), "Format"))); } } void function_baseClassMethod() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); { Tokenizer tokenizer(&settings, nullptr); std::istringstream istr("struct X : public Base { void dostuff() { f(0); } };"); tokenizer.tokenize(istr, "test.cpp"); ASSERT(library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1)); } { Tokenizer tokenizer(&settings, nullptr); std::istringstream istr("struct X : public Base { void dostuff() { f(1,2); } };"); tokenizer.tokenize(istr, "test.cpp"); ASSERT(!library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1)); } } void function_warn() const { const char xmldata[] = "\n" "\n" " \n" " Message\n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); TokenList tokenList(nullptr); std::istringstream istr("a(); b();"); tokenList.createTokens(istr); const Library::WarnInfo* a = library.getWarnInfo(tokenList.front()); const Library::WarnInfo* b = library.getWarnInfo(tokenList.front()->tokAt(4)); ASSERT_EQUALS(2, library.functionwarn.size()); ASSERT(a && b); if (a && b) { ASSERT_EQUALS("Message", a->message); ASSERT_EQUALS(Severity::style, a->severity); ASSERT_EQUALS(Standards::C99, a->standards.c); ASSERT_EQUALS(Standards::CPP03, a->standards.cpp); ASSERT_EQUALS("Obsolescent function 'b' called. It is recommended to use 'c', 'd' or 'e' instead.", b->message); ASSERT_EQUALS(Severity::performance, b->severity); ASSERT_EQUALS(Standards::C89, b->standards.c); ASSERT_EQUALS(Standards::CPP11, b->standards.cpp); } } void memory() const { const char xmldata[] = "\n" "\n" " \n" " CreateX\n" " DeleteX\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.functions.empty()); ASSERT(Library::ismemory(library.getAllocFuncInfo("CreateX"))); ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX"); ASSERT(af && af->arg == -1); const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX"); ASSERT(df && df->arg == 1); } void memory2() const { const char xmldata1[] = "\n" "\n" " \n" " malloc\n" " free\n" " \n" ""; const char xmldata2[] = "\n" "\n" " \n" " foo\n" " free\n" " \n" ""; Library library; library.loadxmldata(xmldata1, sizeof(xmldata1)); library.loadxmldata(xmldata2, sizeof(xmldata2)); ASSERT_EQUALS(library.deallocId("free"), library.allocId("malloc")); ASSERT_EQUALS(library.deallocId("free"), library.allocId("foo")); } void memory3() const { const char xmldata[] = "\n" "\n" " \n" " CreateX\n" " DeleteX\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.functions.empty()); const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX"); ASSERT(af && af->arg == 5); const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX"); ASSERT(df && df->arg == 2); ASSERT(library.returnuninitdata.find("CreateX") != library.returnuninitdata.cend()); } void resource() const { const char xmldata[] = "\n" "\n" " \n" " CreateX\n" " DeleteX\n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); ASSERT(library.functions.empty()); ASSERT(Library::isresource(library.allocId("CreateX"))); ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); } void podtype() const { { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); // s8 { const struct Library::PodType * const type = library.podtype("s8"); ASSERT_EQUALS(true, type != nullptr); if (type) { ASSERT_EQUALS(1U, type->size); ASSERT_EQUALS('s', type->sign); } } // u8 { const struct Library::PodType * const type = library.podtype("u8"); ASSERT_EQUALS(true, type != nullptr); if (type) { ASSERT_EQUALS(1U, type->size); ASSERT_EQUALS('u', type->sign); } } // u16 { const struct Library::PodType * const type = library.podtype("u16"); ASSERT_EQUALS(true, type != nullptr); if (type) { ASSERT_EQUALS(2U, type->size); ASSERT_EQUALS('u', type->sign); } } // s16 { const struct Library::PodType * const type = library.podtype("s16"); ASSERT_EQUALS(true, type != nullptr); if (type) { ASSERT_EQUALS(2U, type->size); ASSERT_EQUALS('s', type->sign); } } // robustness test: provide cfg without PodType { const struct Library::PodType * const type = library.podtype("nonExistingPodType"); ASSERT_EQUALS(true, type == nullptr); } } } void container() const { const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" // Inherits all but templateParameter " \n" " \n" " \n" " \n" " \n" ""; Library library; ASSERT_EQUALS(true, Library::OK == (readLibrary(library, xmldata)).errorcode); Library::Container& A = library.containers["A"]; Library::Container& B = library.containers["B"]; Library::Container& C = library.containers["C"]; ASSERT_EQUALS(A.type_templateArgNo, 1); ASSERT_EQUALS(A.size_templateArgNo, 4); ASSERT_EQUALS(A.startPattern, "std :: A <"); ASSERT_EQUALS(A.endPattern, "> !!::"); ASSERT_EQUALS(A.itEndPattern, "> :: iterator"); ASSERT_EQUALS(A.stdStringLike, false); ASSERT_EQUALS(A.arrayLike_indexOp, false); ASSERT_EQUALS(A.opLessAllowed, true); ASSERT_EQ(Library::Container::Yield::SIZE, A.getYield("size")); ASSERT_EQ(Library::Container::Yield::EMPTY, A.getYield("empty")); ASSERT_EQ(Library::Container::Yield::AT_INDEX, A.getYield("at")); ASSERT_EQ(Library::Container::Yield::START_ITERATOR, A.getYield("begin")); ASSERT_EQ(Library::Container::Yield::END_ITERATOR, A.getYield("end")); ASSERT_EQ(Library::Container::Yield::BUFFER, A.getYield("data")); ASSERT_EQ(Library::Container::Yield::BUFFER_NT, A.getYield("c_str")); ASSERT_EQ(Library::Container::Yield::ITEM, A.getYield("front")); ASSERT_EQ(Library::Container::Yield::NO_YIELD, A.getYield("foo")); ASSERT_EQ(Library::Container::Action::RESIZE, A.getAction("resize")); ASSERT_EQ(Library::Container::Action::CLEAR, A.getAction("clear")); ASSERT_EQ(Library::Container::Action::PUSH, A.getAction("push_back")); ASSERT_EQ(Library::Container::Action::POP, A.getAction("pop_back")); ASSERT_EQ(Library::Container::Action::FIND, A.getAction("find")); ASSERT_EQ(Library::Container::Action::NO_ACTION, A.getAction("foo")); ASSERT_EQUALS(B.type_templateArgNo, 1); ASSERT_EQUALS(B.size_templateArgNo, 3); ASSERT_EQUALS(B.startPattern, "std :: B <"); ASSERT_EQUALS(B.endPattern, "> !!::"); ASSERT_EQUALS(B.itEndPattern, "> :: iterator"); ASSERT_EQUALS(B.functions.size(), A.functions.size()); ASSERT_EQUALS(B.opLessAllowed, false); ASSERT(C.functions.empty()); ASSERT_EQUALS(C.type_templateArgNo, -1); ASSERT_EQUALS(C.size_templateArgNo, -1); ASSERT_EQUALS(C.stdStringLike, true); ASSERT_EQUALS(C.arrayLike_indexOp, true); } void version() const { { const char xmldata [] = "\n" "\n" ""; Library library; const Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(err.errorcode, Library::OK); } { const char xmldata [] = "\n" "\n" ""; Library library; const Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(err.errorcode, Library::OK); } { const char xmldata [] = "\n" "\n" ""; Library library; const Library::Error err = readLibrary(library, xmldata); ASSERT_EQUALS(err.errorcode, Library::UNSUPPORTED_FORMAT); } } void loadLibError(const char xmldata [], Library::ErrorCode errorcode, const char* file, unsigned line) const { Library library; assertEquals(file, line, errorcode, readLibrary(library, xmldata).errorcode); } #define LOADLIBERROR(xmldata, errorcode) loadLibError(xmldata, errorcode, __FILE__, __LINE__) #define LOADLIB_ERROR_INVALID_RANGE(valid) LOADLIBERROR("\n" \ "\n" \ "\n" \ "\n" \ "" valid "\n" \ "\n" \ "\n" \ "", \ Library::BAD_ATTRIBUTE_VALUE) void loadLibErrors() const { LOADLIBERROR("\n" "\n" " \n" "", Library::UNKNOWN_ELEMENT); // #define without attributes LOADLIBERROR("\n" "\n" " \n" // no attributes provided at all "", Library::MISSING_ATTRIBUTE); // #define with name but without value LOADLIBERROR("\n" "\n" " \n" // no value provided "", Library::MISSING_ATTRIBUTE); LOADLIBERROR("\n" "\n" " \n" // no name provided "", Library::MISSING_ATTRIBUTE); LOADLIBERROR("\n" "\n" "", Library::UNSUPPORTED_FORMAT); // empty range LOADLIB_ERROR_INVALID_RANGE(""); // letter as range LOADLIB_ERROR_INVALID_RANGE("a"); // letter and number as range LOADLIB_ERROR_INVALID_RANGE("1a"); // digit followed by dash LOADLIB_ERROR_INVALID_RANGE("0:2-1"); // single dash LOADLIB_ERROR_INVALID_RANGE("-"); // range with multiple colons LOADLIB_ERROR_INVALID_RANGE("1:2:3"); // extra dot LOADLIB_ERROR_INVALID_RANGE("1.0.0:10"); // consecutive dots LOADLIB_ERROR_INVALID_RANGE("1..0:10"); // dot followed by dash LOADLIB_ERROR_INVALID_RANGE("1.-0:10"); // dot without preceding number LOADLIB_ERROR_INVALID_RANGE(".5:10"); // dash followed by dot LOADLIB_ERROR_INVALID_RANGE("-.5:10"); // colon followed by dot without preceding number LOADLIB_ERROR_INVALID_RANGE("0:.5"); // colon followed by dash followed by dot LOADLIB_ERROR_INVALID_RANGE("-10:-.5"); // dot not followed by number LOADLIB_ERROR_INVALID_RANGE("1:5."); // dot not followed by number LOADLIB_ERROR_INVALID_RANGE("1.:5"); // dot followed by comma LOADLIB_ERROR_INVALID_RANGE("1:5.,6:10"); // comma followed by dot LOADLIB_ERROR_INVALID_RANGE("-10:0,.5:"); } }; REGISTER_TEST(TestLibrary) cppcheck-1.90/test/testmathlib.cpp000066400000000000000000001700301357737443600172520ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mathlib.h" #include "testsuite.h" struct InternalError; class TestMathLib : public TestFixture { public: TestMathLib() : TestFixture("TestMathLib") { } private: void run() OVERRIDE { TEST_CASE(isint); TEST_CASE(isbin); TEST_CASE(isdec); TEST_CASE(isoct); TEST_CASE(isFloatHex); TEST_CASE(isIntHex); TEST_CASE(isValidIntegerSuffix); TEST_CASE(isnegative); TEST_CASE(ispositive); TEST_CASE(isFloat); TEST_CASE(isDecimalFloat); TEST_CASE(isGreater) TEST_CASE(isGreaterEqual) TEST_CASE(isEqual) TEST_CASE(isNotEqual) TEST_CASE(isLess) TEST_CASE(isLessEqual) TEST_CASE(calculate); TEST_CASE(calculate1); TEST_CASE(typesuffix); TEST_CASE(toLongNumber); TEST_CASE(toDoubleNumber); TEST_CASE(naninf); TEST_CASE(isNullValue); TEST_CASE(incdec); TEST_CASE(sin); TEST_CASE(cos); TEST_CASE(tan); TEST_CASE(abs); TEST_CASE(toString); TEST_CASE(characterLiteralsNormalization); TEST_CASE(CPP14DigitSeparators); } void isGreater() const { ASSERT_EQUALS(true, MathLib::isGreater("1.0", "0.001")); ASSERT_EQUALS(false, MathLib::isGreater("-1.0", "0.001")); } void isGreaterEqual() const { ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.00", "1.0")); ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.001", "1.0")); ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.0", "0.001")); ASSERT_EQUALS(false, MathLib::isGreaterEqual("-1.0", "0.001")); } void isEqual() const { ASSERT_EQUALS(true, MathLib::isEqual("1.0", "1.0")); ASSERT_EQUALS(false, MathLib::isEqual("1.", "1.01")); ASSERT_EQUALS(true, MathLib::isEqual("0.1","1.0E-1")); } void isNotEqual() const { ASSERT_EQUALS(false, MathLib::isNotEqual("1.0", "1.0")); ASSERT_EQUALS(true, MathLib::isNotEqual("1.", "1.01")); } void isLess() const { ASSERT_EQUALS(false, MathLib::isLess("1.0", "0.001")); ASSERT_EQUALS(true, MathLib::isLess("-1.0", "0.001")); } void isLessEqual() const { ASSERT_EQUALS(true, MathLib::isLessEqual("1.00", "1.0")); ASSERT_EQUALS(false, MathLib::isLessEqual("1.001", "1.0")); ASSERT_EQUALS(false, MathLib::isLessEqual("1.0", "0.001")); ASSERT_EQUALS(true, MathLib::isLessEqual("-1.0", "0.001")); } void calculate() const { // addition ASSERT_EQUALS("256", MathLib::add("0xff", "1")); ASSERT_EQUALS("249", MathLib::add("250", "-1")); ASSERT_EQUALS("251", MathLib::add("250", "1")); ASSERT_EQUALS("-2.0", MathLib::add("-1.", "-1")); ASSERT_EQUALS("-1", MathLib::add("0", "-1")); ASSERT_EQUALS("1", MathLib::add("1", "0")); ASSERT_EQUALS("0.0", MathLib::add("0", "0.")); ASSERT_EQUALS("1.00000001", MathLib::add("1", "0.00000001")); // #4016 ASSERT_EQUALS("30666.22", MathLib::add("30666.22", "0.0")); // #4068 // subtraction ASSERT_EQUALS("254", MathLib::subtract("0xff", "1")); ASSERT_EQUALS("251", MathLib::subtract("250", "-1")); ASSERT_EQUALS("249", MathLib::subtract("250", "1")); ASSERT_EQUALS("0.0", MathLib::subtract("-1.", "-1")); ASSERT_EQUALS("1", MathLib::subtract("0", "-1")); ASSERT_EQUALS("1", MathLib::subtract("1", "0")); ASSERT_EQUALS("0.0", MathLib::subtract("0", "0.")); ASSERT_EQUALS("0.99999999", MathLib::subtract("1", "0.00000001")); // #4016 ASSERT_EQUALS("30666.22", MathLib::subtract("30666.22", "0.0")); // #4068 ASSERT_EQUALS("0.0", MathLib::subtract("0.0", "0.0")); // multiply ASSERT_EQUALS("-0.003", MathLib::multiply("-1e-3", "3")); ASSERT_EQUALS("-11.96", MathLib::multiply("-2.3", "5.2")); ASSERT_EQUALS("3000.0", MathLib::multiply("1E3", "3")); ASSERT_EQUALS("3000.0", MathLib::multiply("1E+3", "3")); ASSERT_EQUALS("3000.0", MathLib::multiply("1.0E3", "3")); ASSERT_EQUALS("-3000.0", MathLib::multiply("-1.0E3", "3")); ASSERT_EQUALS("-3000.0", MathLib::multiply("-1.0E+3", "3")); ASSERT_EQUALS("0.0", MathLib::multiply("+1.0E+3", "0")); ASSERT_EQUALS("2147483648", MathLib::multiply("2","1073741824")); ASSERT_EQUALS("536870912", MathLib::multiply("512","1048576")); // divide ASSERT_EQUALS("1", MathLib::divide("1", "1")); ASSERT_EQUALS("0", MathLib::divide("0", "1")); ASSERT_EQUALS("5", MathLib::divide("-10", "-2")); ASSERT_EQUALS("-2.5", MathLib::divide("-10.", "4")); ASSERT_EQUALS("2.5", MathLib::divide("-10.", "-4")); ASSERT_EQUALS("5.0", MathLib::divide("25.5", "5.1")); ASSERT_EQUALS("7.0", MathLib::divide("21.", "3")); ASSERT_EQUALS("1", MathLib::divide("3", "2")); ASSERT_THROW(MathLib::divide("123", "0"), InternalError); // decimal zero: throw ASSERT_THROW(MathLib::divide("123", "00"), InternalError); // octal zero: throw ASSERT_THROW(MathLib::divide("123", "0x0"), InternalError); // hex zero: throw MathLib::divide("123", "0.0f"); // float zero: don't throw MathLib::divide("123", "0.0"); // double zero: don't throw MathLib::divide("123", "0.0L"); // long double zero: don't throw ASSERT_THROW(MathLib::divide("-9223372036854775808", "-1"), InternalError); // #4520 - out of range => throw ASSERT_EQUALS("4611686018427387904", MathLib::divide("-9223372036854775808", "-2")); // #6679 // Unknown action should throw exception ASSERT_THROW(MathLib::calculate("1","2",'j'),InternalError); } void calculate1() const { ASSERT_EQUALS("0", MathLib::calculate("2", "1", '%')); ASSERT_EQUALS("2", MathLib::calculate("12", "5", '%')); ASSERT_EQUALS("1", MathLib::calculate("100", "3", '%')); #ifndef TEST_MATHLIB_VALUE // floating point modulo is not defined in C/C++ ASSERT_EQUALS("0.0", MathLib::calculate("2.0", "1.0", '%')); ASSERT_EQUALS("12.0", MathLib::calculate("12.0", "13.0", '%')); ASSERT_EQUALS("1.3", MathLib::calculate("5.3", "2.0", '%')); ASSERT_EQUALS("1.7", MathLib::calculate("18.5", "4.2", '%')); MathLib::calculate("123", "0.0", '%'); // don't throw #endif ASSERT_THROW(MathLib::calculate("123", "0", '%'), InternalError); // throw ASSERT_EQUALS("0", MathLib::calculate("1", "1", '^')); ASSERT_EQUALS("3", MathLib::calculate("2", "1", '^')); } void typesuffix() const { ASSERT_EQUALS("2", MathLib::add("1", "1")); ASSERT_EQUALS("2U", MathLib::add("1U", "1")); ASSERT_EQUALS("2L", MathLib::add("1L", "1")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1")); ASSERT_EQUALS("2LL", MathLib::add("1i64", "1")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1")); ASSERT_EQUALS("2ULL", MathLib::add("1ui64","1")); ASSERT_EQUALS("2U", MathLib::add("1", "1U")); ASSERT_EQUALS("2U", MathLib::add("1U", "1U")); ASSERT_EQUALS("2L", MathLib::add("1L", "1U")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1U")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1U")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1U")); ASSERT_EQUALS("2L", MathLib::add("1", "1L")); ASSERT_EQUALS("2L", MathLib::add("1U", "1L")); ASSERT_EQUALS("2L", MathLib::add("1L", "1L")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1L")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1L")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1L")); ASSERT_EQUALS("2UL", MathLib::add("1", "1UL")); ASSERT_EQUALS("2UL", MathLib::add("1U", "1UL")); ASSERT_EQUALS("2UL", MathLib::add("1L", "1UL")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1UL")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1UL")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1UL")); ASSERT_EQUALS("2UL", MathLib::add("1", "1LU")); ASSERT_EQUALS("2UL", MathLib::add("1U", "1LU")); ASSERT_EQUALS("2UL", MathLib::add("1L", "1LU")); ASSERT_EQUALS("2UL", MathLib::add("1UL", "1LU")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1LU")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LU")); ASSERT_EQUALS("2LL", MathLib::add("1", "1LL")); ASSERT_EQUALS("2LL", MathLib::add("1U", "1LL")); ASSERT_EQUALS("2LL", MathLib::add("1L", "1LL")); ASSERT_EQUALS("2LL", MathLib::add("1UL", "1LL")); ASSERT_EQUALS("2LL", MathLib::add("1LL", "1LL")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LL")); ASSERT_EQUALS("2ULL", MathLib::add("1", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1U", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1L", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1UL", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1LL", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1ULL")); ASSERT_EQUALS("2ULL", MathLib::add("1", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1U", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1L", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1UL", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1LL", "1LLU")); ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LLU")); } void toLongNumber() const { // from hex ASSERT_EQUALS(0, MathLib::toLongNumber("0x0")); ASSERT_EQUALS(0, MathLib::toLongNumber("-0x0")); ASSERT_EQUALS(0, MathLib::toLongNumber("+0x0")); ASSERT_EQUALS(10, MathLib::toLongNumber("0xa")); ASSERT_EQUALS(10995, MathLib::toLongNumber("0x2AF3")); ASSERT_EQUALS(-10, MathLib::toLongNumber("-0xa")); ASSERT_EQUALS(-10995, MathLib::toLongNumber("-0x2AF3")); ASSERT_EQUALS(10, MathLib::toLongNumber("+0xa")); ASSERT_EQUALS(10995, MathLib::toLongNumber("+0x2AF3")); // from octal ASSERT_EQUALS(8, MathLib::toLongNumber("010")); ASSERT_EQUALS(8, MathLib::toLongNumber("+010")); ASSERT_EQUALS(-8, MathLib::toLongNumber("-010")); ASSERT_EQUALS(125, MathLib::toLongNumber("0175")); ASSERT_EQUALS(125, MathLib::toLongNumber("+0175")); ASSERT_EQUALS(-125, MathLib::toLongNumber("-0175")); // from binary ASSERT_EQUALS(0, MathLib::toLongNumber("0b0")); ASSERT_EQUALS(1, MathLib::toLongNumber("0b1")); ASSERT_EQUALS(1, MathLib::toLongNumber("+0b1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-0b1")); ASSERT_EQUALS(215, MathLib::toLongNumber("0b11010111")); ASSERT_EQUALS(-215, MathLib::toLongNumber("-0b11010111")); ASSERT_EQUALS(215, MathLib::toLongNumber("0B11010111")); // from base 10 ASSERT_EQUALS(10, MathLib::toLongNumber("10")); ASSERT_EQUALS(10, MathLib::toLongNumber("10.")); ASSERT_EQUALS(10, MathLib::toLongNumber("10.0")); ASSERT_EQUALS(100, MathLib::toLongNumber("10E+1")); ASSERT_EQUALS(1, MathLib::toLongNumber("10E-1")); ASSERT_EQUALS(100, MathLib::toLongNumber("+10E+1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-10E-1")); ASSERT_EQUALS(100, MathLib::toLongNumber("+10.E+1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-10.E-1")); ASSERT_EQUALS(100, MathLib::toLongNumber("+10.0E+1")); ASSERT_EQUALS(-1, MathLib::toLongNumber("-10.0E-1")); // from char ASSERT_EQUALS((int)('A'), MathLib::toLongNumber("'A'")); ASSERT_EQUALS((int)('\x10'), MathLib::toLongNumber("'\\x10'")); ASSERT_EQUALS((int)('\100'), MathLib::toLongNumber("'\\100'")); ASSERT_EQUALS((int)('\200'), MathLib::toLongNumber("'\\200'")); ASSERT_EQUALS((int)(L'A'), MathLib::toLongNumber("L'A'")); #ifdef __GNUC__ // BEGIN Implementation-specific results ASSERT_EQUALS((int)('AB'), MathLib::toLongNumber("'AB'")); ASSERT_EQUALS((int)('ABC'), MathLib::toLongNumber("'ABC'")); ASSERT_EQUALS((int)('ABCD'), MathLib::toLongNumber("'ABCD'")); ASSERT_EQUALS((int)('ABCDE'), MathLib::toLongNumber("'ABCDE'")); // END Implementation-specific results #endif ASSERT_EQUALS((int)('\0'), MathLib::toLongNumber("'\\0'")); ASSERT_EQUALS(0x1B, MathLib::toLongNumber("'\\e'")); ASSERT_EQUALS((int)('\r'), MathLib::toLongNumber("'\\r'")); ASSERT_EQUALS((int)('\x12'), MathLib::toLongNumber("'\\x12'")); // may cause some compile problems: ASSERT_EQUALS((int)('\x123'), MathLib::toLongNumber("'\\x123'")); // may cause some compile problems: ASSERT_EQUALS((int)('\x1234'), MathLib::toLongNumber("'\\x1234'")); ASSERT_EQUALS((int)('\3'), MathLib::toLongNumber("'\\3'")); ASSERT_EQUALS((int)('\34'), MathLib::toLongNumber("'\\34'")); ASSERT_EQUALS((int)('\034'), MathLib::toLongNumber("'\\034'")); ASSERT_EQUALS((int)('\x34'), MathLib::toLongNumber("'\\x34'")); ASSERT_EQUALS((int)('\134'), MathLib::toLongNumber("'\\134'")); ASSERT_EQUALS((int)('\134t'), MathLib::toLongNumber("'\\134t'")); // Ticket #7452 ASSERT_THROW(MathLib::toLongNumber("'\\9'"), InternalError); ASSERT_THROW(MathLib::toLongNumber("'\\934'"), InternalError); // that is not gcc/clang encoding ASSERT_EQUALS(959657011, MathLib::toLongNumber("'\\u9343'")); ASSERT_EQUALS(1714631779, MathLib::toLongNumber("'\\U0001f34c'")); { // some unit-testing for a utility function ASSERT_EQUALS(0, MathLib::characterLiteralToLongNumber(std::string())); ASSERT_EQUALS(32, MathLib::characterLiteralToLongNumber(std::string(" "))); ASSERT_EQUALS(538976288, MathLib::characterLiteralToLongNumber(std::string(" "))); ASSERT_THROW(MathLib::characterLiteralToLongNumber(std::string("\\u")), InternalError); } ASSERT_EQUALS(-8552249625308161526, MathLib::toLongNumber("0x89504e470d0a1a0a")); ASSERT_EQUALS(-8481036456200365558, MathLib::toLongNumber("0x8a4d4e470d0a1a0a")); ASSERT_EQUALS(9894494448401390090ULL, MathLib::toULongNumber("0x89504e470d0a1a0a")); ASSERT_EQUALS(9965707617509186058ULL, MathLib::toULongNumber("0x8a4d4e470d0a1a0a")); // zero input ASSERT_EQUALS(0, MathLib::toULongNumber("0")); ASSERT_EQUALS(0, MathLib::toULongNumber("-0")); ASSERT_EQUALS(0, MathLib::toULongNumber("+0")); ASSERT_EQUALS(0U, MathLib::toULongNumber("0U")); ASSERT_EQUALS(0, MathLib::toULongNumber("-0x0")); ASSERT_EQUALS(1U, MathLib::toULongNumber("1U")); ASSERT_EQUALS(10000U, MathLib::toULongNumber("1e4")); ASSERT_EQUALS(10000U, MathLib::toULongNumber("1e4")); ASSERT_EQUALS(0xFF00000000000000UL, MathLib::toULongNumber("0xFF00000000000000UL")); ASSERT_EQUALS(0x0A00000000000000UL, MathLib::toULongNumber("0x0A00000000000000UL")); ASSERT_EQUALS(9U, MathLib::toULongNumber("011")); ASSERT_EQUALS(5U, MathLib::toULongNumber("0b101")); // from long long /* * ASSERT_EQUALS(0xFF00000000000000LL, MathLib::toLongNumber("0xFF00000000000000LL")); * This does not work in a portable way! * While it succeeds on 32bit Visual Studio it fails on Linux 64bit because it is greater than 0x7FFFFFFFFFFFFFFF (=LLONG_MAX) */ ASSERT_EQUALS(0x0A00000000000000LL, MathLib::toLongNumber("0x0A00000000000000LL")); } void toDoubleNumber() const { ASSERT_EQUALS_DOUBLE(10.0, MathLib::toDoubleNumber("10"), 0.001); ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2"), 0.001); ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(-100.0, MathLib::toDoubleNumber("-1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(-1e+10, MathLib::toDoubleNumber("-1.0E+10"), 1); ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("+1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(1e+10, MathLib::toDoubleNumber("+1.0E+10"), 1); ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("1.0E+2"), 0.001); ASSERT_EQUALS_DOUBLE(1e+10, MathLib::toDoubleNumber("1.0E+10"), 1); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E+0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E-0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E+00"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E-00"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0E+00"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0E-00"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0."), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0.0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0."), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0."), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0.0"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0.0"), 0.000001); ASSERT_EQUALS_DOUBLE('0', MathLib::toDoubleNumber("'0'"), 0.000001); ASSERT_EQUALS_DOUBLE(L'0', MathLib::toDoubleNumber("L'0'"), 0.000001); ASSERT_EQUALS_DOUBLE(192, MathLib::toDoubleNumber("0x0.3p10"), 0.000001); ASSERT_EQUALS_DOUBLE(5.42101e-20, MathLib::toDoubleNumber("0x1p-64"), 1e-20); ASSERT_EQUALS_DOUBLE(3.14159, MathLib::toDoubleNumber("0x1.921fb5p+1"), 0.000001); ASSERT_EQUALS_DOUBLE(2006, MathLib::toDoubleNumber("0x1.f58000p+10"), 0.000001); ASSERT_EQUALS_DOUBLE(1e-010, MathLib::toDoubleNumber("0x1.b7cdfep-34"), 0.000001); ASSERT_EQUALS_DOUBLE(.484375, MathLib::toDoubleNumber("0x1.fp-2"), 0.000001); ASSERT_EQUALS_DOUBLE(9.0, MathLib::toDoubleNumber("0x1.2P3"), 0.000001); ASSERT_EQUALS_DOUBLE(0.0625, MathLib::toDoubleNumber("0x.1P0"), 0.000001); // verify: string --> double --> string conversion ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0f"))); ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("0.0f"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("0.0"))); ASSERT_EQUALS("-1.0", MathLib::toString(MathLib::toDoubleNumber("-1.0f"))); ASSERT_EQUALS("-1.0", MathLib::toString(MathLib::toDoubleNumber("-1.0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0f"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0"))); ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("+1.0f"))); ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("+1.0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("+0.0f"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("+0.0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0"))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0."))); ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0"))); } void isint() const { // zero tests ASSERT_EQUALS(true, MathLib::isInt("0")); ASSERT_EQUALS(false, MathLib::isInt("0.")); ASSERT_EQUALS(false, MathLib::isInt("0.0")); ASSERT_EQUALS(false, MathLib::isInt("-0.")); ASSERT_EQUALS(false, MathLib::isInt("+0.")); ASSERT_EQUALS(false, MathLib::isInt("-0.0")); ASSERT_EQUALS(false, MathLib::isInt("+0.0")); ASSERT_EQUALS(false, MathLib::isInt("+0.0E+1")); ASSERT_EQUALS(false, MathLib::isInt("+0.0E-1")); ASSERT_EQUALS(false, MathLib::isInt("-0.0E+1")); ASSERT_EQUALS(false, MathLib::isInt("-0.0E-1")); ASSERT_EQUALS(true, MathLib::isInt("1")); ASSERT_EQUALS(true, MathLib::isInt("-1")); ASSERT_EQUALS(true, MathLib::isInt("+1")); ASSERT_EQUALS(false, MathLib::isInt("+1E+1")); ASSERT_EQUALS(false, MathLib::isInt("+1E+10000")); ASSERT_EQUALS(false, MathLib::isInt("-1E+1")); ASSERT_EQUALS(false, MathLib::isInt("-1E+10000")); ASSERT_EQUALS(false, MathLib::isInt("-1E-1")); ASSERT_EQUALS(false, MathLib::isInt("-1E-10000")); ASSERT_EQUALS(true, MathLib::isInt("0xff")); ASSERT_EQUALS(true, MathLib::isInt("0xa")); ASSERT_EQUALS(true, MathLib::isInt("0b1000")); ASSERT_EQUALS(true, MathLib::isInt("0B1000")); ASSERT_EQUALS(true, MathLib::isInt("0l")); ASSERT_EQUALS(true, MathLib::isInt("0L")); ASSERT_EQUALS(true, MathLib::isInt("0ul")); ASSERT_EQUALS(true, MathLib::isInt("0ull")); ASSERT_EQUALS(true, MathLib::isInt("0llu")); ASSERT_EQUALS(true, MathLib::isInt("333L")); ASSERT_EQUALS(true, MathLib::isInt("330L")); ASSERT_EQUALS(true, MathLib::isInt("330llu")); ASSERT_EQUALS(true, MathLib::isInt("07")); ASSERT_EQUALS(true, MathLib::isInt("0123")); ASSERT_EQUALS(false, MathLib::isInt("0.4")); ASSERT_EQUALS(false, MathLib::isInt("2352.3f")); ASSERT_EQUALS(false, MathLib::isInt("0.00004")); ASSERT_EQUALS(false, MathLib::isInt("2352.00001f")); ASSERT_EQUALS(false, MathLib::isInt(".4")); ASSERT_EQUALS(false, MathLib::isInt("1.0E+1")); ASSERT_EQUALS(false, MathLib::isInt("1.0E-1")); ASSERT_EQUALS(false, MathLib::isInt("-1.0E+1")); ASSERT_EQUALS(false, MathLib::isInt("+1.0E-1")); ASSERT_EQUALS(false, MathLib::isInt("-1.E+1")); ASSERT_EQUALS(false, MathLib::isInt("+1.E-1")); ASSERT_EQUALS(false, MathLib::isInt(" 1.0E+1")); // with whitespace in front ASSERT_EQUALS(false, MathLib::isInt(" 1.0E-1")); ASSERT_EQUALS(false, MathLib::isInt(" -1.0E+1")); ASSERT_EQUALS(false, MathLib::isInt(" +1.0E-1")); ASSERT_EQUALS(false, MathLib::isInt(" -1.E+1")); ASSERT_EQUALS(false, MathLib::isInt(" +1.E-1")); // with whitespace in front and end ASSERT_EQUALS(false, MathLib::isInt(" 1.0E-1 ")); ASSERT_EQUALS(false, MathLib::isInt(" -1.0E+1 ")); ASSERT_EQUALS(false, MathLib::isInt(" +1.0E-1 ")); ASSERT_EQUALS(false, MathLib::isInt(" -1.E+1 ")); ASSERT_EQUALS(false, MathLib::isInt(" +1.E-1 ")); // with whitespace in front and end ASSERT_EQUALS(false, MathLib::isInt("1.0E-1 ")); ASSERT_EQUALS(false, MathLib::isInt("-1.0E+1 ")); ASSERT_EQUALS(false, MathLib::isInt("+1.0E-1 ")); ASSERT_EQUALS(false, MathLib::isInt("-1.E+1 ")); ASSERT_EQUALS(false, MathLib::isInt("+1.E-1 ")); // test some garbage ASSERT_EQUALS(false, MathLib::isInt("u")); ASSERT_EQUALS(false, MathLib::isInt("l")); ASSERT_EQUALS(false, MathLib::isInt("ul")); ASSERT_EQUALS(false, MathLib::isInt("ll")); ASSERT_EQUALS(false, MathLib::isInt("U")); ASSERT_EQUALS(false, MathLib::isInt("L")); ASSERT_EQUALS(false, MathLib::isInt("uL")); ASSERT_EQUALS(false, MathLib::isInt("LL")); ASSERT_EQUALS(false, MathLib::isInt("e2")); ASSERT_EQUALS(false, MathLib::isInt("E2")); ASSERT_EQUALS(false, MathLib::isInt(".e2")); ASSERT_EQUALS(false, MathLib::isInt(".E2")); ASSERT_EQUALS(false, MathLib::isInt("0x")); ASSERT_EQUALS(false, MathLib::isInt("0xu")); ASSERT_EQUALS(false, MathLib::isInt("0xl")); ASSERT_EQUALS(false, MathLib::isInt("0xul")); // test empty string ASSERT_EQUALS(false, MathLib::isInt("")); } void isbin() const { // positive testing ASSERT_EQUALS(true, MathLib::isBin("0b0")); ASSERT_EQUALS(true, MathLib::isBin("0b1")); ASSERT_EQUALS(true, MathLib::isBin("+0b1")); ASSERT_EQUALS(true, MathLib::isBin("-0b1")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111")); ASSERT_EQUALS(true, MathLib::isBin("-0b11010111")); ASSERT_EQUALS(true, MathLib::isBin("0B11010111")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111u")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111ul")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111ull")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111l")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111ll")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111llu")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111l")); ASSERT_EQUALS(true, MathLib::isBin("0b11010111lu")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lul")); // Suffix LUL not allowed // negative testing ASSERT_EQUALS(false, MathLib::isBin("100101bx")); ASSERT_EQUALS(false, MathLib::isBin("0")); ASSERT_EQUALS(false, MathLib::isBin("0B")); ASSERT_EQUALS(false, MathLib::isBin("0C")); ASSERT_EQUALS(false, MathLib::isBin("+0B")); ASSERT_EQUALS(false, MathLib::isBin("-0B")); ASSERT_EQUALS(false, MathLib::isBin("-0Bx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111x")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111ux")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lux")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111ulx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lulx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111ullx")); ASSERT_EQUALS(false, MathLib::isBin("0b11010111lll")); // test empty string ASSERT_EQUALS(false, MathLib::isBin("")); } void isnegative() const { ASSERT_EQUALS(true, MathLib::isNegative("-1")); ASSERT_EQUALS(true, MathLib::isNegative("-1.")); ASSERT_EQUALS(true, MathLib::isNegative("-1.0")); ASSERT_EQUALS(true, MathLib::isNegative("-1.0E+2")); ASSERT_EQUALS(true, MathLib::isNegative("-1.0E-2")); ASSERT_EQUALS(false, MathLib::isNegative("+1")); ASSERT_EQUALS(false, MathLib::isNegative("+1.")); ASSERT_EQUALS(false, MathLib::isNegative("+1.0")); ASSERT_EQUALS(false, MathLib::isNegative("+1.0E+2")); ASSERT_EQUALS(false, MathLib::isNegative("+1.0E-2")); // test empty string ASSERT_EQUALS(false, MathLib::isNegative("")); } void isoct() const { // octal number format: [+|-]0[0-7][suffix] // positive testing ASSERT_EQUALS(true, MathLib::isOct("010")); ASSERT_EQUALS(true, MathLib::isOct("+010")); ASSERT_EQUALS(true, MathLib::isOct("-010")); ASSERT_EQUALS(true, MathLib::isOct("0175")); ASSERT_EQUALS(true, MathLib::isOct("+0175")); ASSERT_EQUALS(true, MathLib::isOct("-0175")); ASSERT_EQUALS(true, MathLib::isOct("00")); ASSERT_EQUALS(true, MathLib::isOct("02")); ASSERT_EQUALS(true, MathLib::isOct("+042")); ASSERT_EQUALS(true, MathLib::isOct("-042")); ASSERT_EQUALS(true, MathLib::isOct("+042U")); ASSERT_EQUALS(true, MathLib::isOct("-042U")); ASSERT_EQUALS(true, MathLib::isOct("+042L")); ASSERT_EQUALS(true, MathLib::isOct("-042L")); ASSERT_EQUALS(true, MathLib::isOct("+042LU")); ASSERT_EQUALS(true, MathLib::isOct("-042LU")); ASSERT_EQUALS(true, MathLib::isOct("+042UL")); ASSERT_EQUALS(true, MathLib::isOct("-042UL")); ASSERT_EQUALS(true, MathLib::isOct("+042ULL")); ASSERT_EQUALS(true, MathLib::isOct("-042ULL")); ASSERT_EQUALS(true, MathLib::isOct("+042LLU")); ASSERT_EQUALS(true, MathLib::isOct("-042LLU")); // test empty string ASSERT_EQUALS(false, MathLib::isOct("")); // negative testing ASSERT_EQUALS(false, MathLib::isOct("0")); ASSERT_EQUALS(false, MathLib::isOct("-0x175")); ASSERT_EQUALS(false, MathLib::isOct("-0_garbage_")); ASSERT_EQUALS(false, MathLib::isOct(" ")); ASSERT_EQUALS(false, MathLib::isOct(" ")); ASSERT_EQUALS(false, MathLib::isOct("02.")); ASSERT_EQUALS(false, MathLib::isOct("02E2")); ASSERT_EQUALS(false, MathLib::isOct("+042x")); ASSERT_EQUALS(false, MathLib::isOct("-042x")); ASSERT_EQUALS(false, MathLib::isOct("+042Ux")); ASSERT_EQUALS(false, MathLib::isOct("-042Ux")); ASSERT_EQUALS(false, MathLib::isOct("+042Lx")); ASSERT_EQUALS(false, MathLib::isOct("-042Lx")); ASSERT_EQUALS(false, MathLib::isOct("+042ULx")); ASSERT_EQUALS(false, MathLib::isOct("-042ULx")); ASSERT_EQUALS(false, MathLib::isOct("+042LLx")); ASSERT_EQUALS(false, MathLib::isOct("-042LLx")); ASSERT_EQUALS(false, MathLib::isOct("+042ULLx")); ASSERT_EQUALS(false, MathLib::isOct("-042ULLx")); ASSERT_EQUALS(false, MathLib::isOct("+042LLUx")); ASSERT_EQUALS(false, MathLib::isOct("-042LLUx")); ASSERT_EQUALS(false, MathLib::isOct("+042LUL")); ASSERT_EQUALS(false, MathLib::isOct("-042LUL")); // white space in front ASSERT_EQUALS(false, MathLib::isOct(" -042ULL")); // trailing white space ASSERT_EQUALS(false, MathLib::isOct("-042ULL ")); // front and trailing white space ASSERT_EQUALS(false, MathLib::isOct(" -042ULL ")); ASSERT_EQUALS(false, MathLib::isOct("+042LUL+0")); } void isFloatHex() const { // hex number syntax: [sign]0x[hexnumbers][suffix] ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.999999999999ap-4")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x0.3p10")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.fp3")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1P-1")); ASSERT_EQUALS(true, MathLib::isFloatHex("0xcc.ccccccccccdp-11")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x3.243F6A88p+03")); ASSERT_EQUALS(true, MathLib::isFloatHex("0xA.Fp-10")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p-10f")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10F")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10l")); ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10L")); ASSERT_EQUALS(true, MathLib::isFloatHex("0X.2p-0")); ASSERT_EQUALS(false, MathLib::isFloatHex("")); ASSERT_EQUALS(false, MathLib::isFloatHex("0")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("0xa")); ASSERT_EQUALS(false, MathLib::isFloatHex("+0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("-0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x.")); ASSERT_EQUALS(false, MathLib::isFloatHex("0XP")); ASSERT_EQUALS(false, MathLib::isFloatHex("0xx")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x1P+-1")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10e")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+1af")); ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10LL")); } void isIntHex() const { // hex number syntax: [sign]0x[hexnumbers][suffix] // positive testing ASSERT_EQUALS(true, MathLib::isIntHex("0xa")); ASSERT_EQUALS(true, MathLib::isIntHex("0x2AF3")); ASSERT_EQUALS(true, MathLib::isIntHex("-0xa")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x2AF3")); ASSERT_EQUALS(true, MathLib::isIntHex("+0xa")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x2AF3")); ASSERT_EQUALS(true, MathLib::isIntHex("0x0")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0U")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0U")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0L")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0L")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LU")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LU")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0UL")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0UL")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LL")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LL")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0ULL")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0ULL")); ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LLU")); ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LLU")); // negative testing ASSERT_EQUALS(false, MathLib::isIntHex("+0x")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x")); ASSERT_EQUALS(false, MathLib::isIntHex("0x")); ASSERT_EQUALS(false, MathLib::isIntHex("0xl")); ASSERT_EQUALS(false, MathLib::isIntHex("0xx")); ASSERT_EQUALS(false, MathLib::isIntHex("-0175")); ASSERT_EQUALS(false, MathLib::isIntHex("-0_garbage_")); ASSERT_EQUALS(false, MathLib::isIntHex(" ")); ASSERT_EQUALS(false, MathLib::isIntHex(" ")); ASSERT_EQUALS(false, MathLib::isIntHex("0")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0Z")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0Z")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0Uz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0Uz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0Lz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0Lz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LUz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LUz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0ULz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0ULz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LLz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LLz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0ULLz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0ULLz")); ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LLUz")); ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LLUz")); ASSERT_EQUALS(false, MathLib::isIntHex("0x0+0")); ASSERT_EQUALS(false, MathLib::isIntHex("e2")); ASSERT_EQUALS(false, MathLib::isIntHex("+E2")); // test empty string ASSERT_EQUALS(false, MathLib::isIntHex("")); } void isValidIntegerSuffix() const { // negative testing ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ux")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ulx")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lx")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lux")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lll")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("garbage")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("llu ")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("iX")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i6X")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64X")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64 ")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i66")); // positive testing ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("u")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ul")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ull")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("l")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("lu")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ll")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("llu")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("llU")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("LLU")); // Microsoft extensions: ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("i64")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("I64")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ui64")); ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("UI64")); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64", false)); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("I64", false)); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ui64", false)); ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("UI64", false)); } void ispositive() const { ASSERT_EQUALS(false, MathLib::isPositive("-1")); ASSERT_EQUALS(false, MathLib::isPositive("-1.")); ASSERT_EQUALS(false, MathLib::isPositive("-1.0")); ASSERT_EQUALS(false, MathLib::isPositive("-1.0E+2")); ASSERT_EQUALS(false, MathLib::isPositive("-1.0E-2")); ASSERT_EQUALS(true, MathLib::isPositive("+1")); ASSERT_EQUALS(true, MathLib::isPositive("+1.")); ASSERT_EQUALS(true, MathLib::isPositive("+1.0")); ASSERT_EQUALS(true, MathLib::isPositive("+1.0E+2")); ASSERT_EQUALS(true, MathLib::isPositive("+1.0E-2")); // test empty string ASSERT_EQUALS(false, MathLib::isPositive("")); // "" is neither positive nor negative } void isFloat() const { ASSERT_EQUALS(false, MathLib::isFloat("")); ASSERT_EQUALS(true, MathLib::isFloat("0.f")); ASSERT_EQUALS(true, MathLib::isFloat("0.f")); ASSERT_EQUALS(true, MathLib::isFloat("0xA.Fp-10")); } void isDecimalFloat() const { ASSERT_EQUALS(false, MathLib::isDecimalFloat("")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(".")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("...")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(".e")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(".e2")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(".E")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+E.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+e.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-E.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-e.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-X")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+X")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("0")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("0 ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0 ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.L")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("0. ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0. ")); ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0.")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("0..")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("..0..")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("..0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0E0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E+0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E-0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0E+0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0E-0")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0E-1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0E-1")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("1")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("-1")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1e+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+007")); // to be sure about #5485 ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+10000")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E+10000")); ASSERT_EQUALS(true, MathLib::isDecimalFloat(".1250E+04")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E-1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E-10000")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1.23e+01")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1.23E+01")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1e+x")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+X")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001lX")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001LX")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001f2")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001F2")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1e+003x")); ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+003X")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.4")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.00004")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001f")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001F")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001l")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001L")); ASSERT_EQUALS(true, MathLib::isDecimalFloat(".4")); ASSERT_EQUALS(true, MathLib::isDecimalFloat(".3e2")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("1.0E+1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("1.0E-1")); ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1.0E+1")); } void naninf() const { ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "0.0")); // nan ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("-0.0", "0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("-0.f", "0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("-0.0", "-0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("-.0", "-0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "-0.f")); // nan (#5875) ASSERT_EQUALS("nan.0", MathLib::divide("0.f", "-0.f")); // nan (#5875) ASSERT_EQUALS("inf.0", MathLib::divide("3.0", "0.0")); // inf ASSERT_EQUALS("inf.0", MathLib::divide("3.0", "0.f")); // inf (#5875) ASSERT_EQUALS("-inf.0", MathLib::divide("-3.0", "0.0")); // -inf (#5142) ASSERT_EQUALS("-inf.0", MathLib::divide("-3.0", "0.0f")); // -inf (#5142) ASSERT_EQUALS("-inf.0", MathLib::divide("-3.0", "-0.0f")); // inf (#5142) } void isdec() const { // positive testing ASSERT_EQUALS(true, MathLib::isDec("1")); ASSERT_EQUALS(true, MathLib::isDec("+1")); ASSERT_EQUALS(true, MathLib::isDec("-1")); ASSERT_EQUALS(true, MathLib::isDec("-100")); ASSERT_EQUALS(true, MathLib::isDec("-1L")); ASSERT_EQUALS(true, MathLib::isDec("1UL")); // negative testing ASSERT_EQUALS(false, MathLib::isDec("-1.")); ASSERT_EQUALS(false, MathLib::isDec("+1.")); ASSERT_EQUALS(false, MathLib::isDec("-x")); ASSERT_EQUALS(false, MathLib::isDec("+x")); ASSERT_EQUALS(false, MathLib::isDec("x")); ASSERT_EQUALS(false, MathLib::isDec("")); } void isNullValue() const { // inter zero value ASSERT_EQUALS(true, MathLib::isNullValue("0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0")); // inter zero value (octal) ASSERT_EQUALS(true, MathLib::isNullValue("00")); ASSERT_EQUALS(true, MathLib::isNullValue("+00")); ASSERT_EQUALS(true, MathLib::isNullValue("-00")); // inter zero value (hex) ASSERT_EQUALS(true, MathLib::isNullValue("0x0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0x0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0x0")); // unsigned integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0U")); ASSERT_EQUALS(true, MathLib::isNullValue("+0U")); ASSERT_EQUALS(true, MathLib::isNullValue("-0U")); // long integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0L")); ASSERT_EQUALS(true, MathLib::isNullValue("+0L")); ASSERT_EQUALS(true, MathLib::isNullValue("-0L")); // unsigned long integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0UL")); ASSERT_EQUALS(true, MathLib::isNullValue("+0UL")); ASSERT_EQUALS(true, MathLib::isNullValue("-0UL")); // unsigned long integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0LU")); ASSERT_EQUALS(true, MathLib::isNullValue("+0LU")); ASSERT_EQUALS(true, MathLib::isNullValue("-0LU")); // long long integer zero value ASSERT_EQUALS(true, MathLib::isNullValue("0LL")); ASSERT_EQUALS(true, MathLib::isNullValue("+0LL")); ASSERT_EQUALS(true, MathLib::isNullValue("-0LL")); // long long unsigned zero value ASSERT_EQUALS(true, MathLib::isNullValue("0LLU")); ASSERT_EQUALS(true, MathLib::isNullValue("+0LLU")); ASSERT_EQUALS(true, MathLib::isNullValue("-0LLU")); // unsigned long long zero value ASSERT_EQUALS(true, MathLib::isNullValue("0ULL")); ASSERT_EQUALS(true, MathLib::isNullValue("+0ULL")); ASSERT_EQUALS(true, MathLib::isNullValue("-0ULL")); // floating pointer zero value (no trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("0.")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.")); // floating pointer zero value (1 trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("0.0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.0")); // floating pointer zero value (3 trailing zeros after dot) ASSERT_EQUALS(true, MathLib::isNullValue("0.000")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.000")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.000")); // floating pointer zero value (no trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("00.")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.")); // floating pointer zero value (1 trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("00.0")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.0")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.0")); // floating pointer zero value (3 trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue("00.000")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.000")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.000")); // floating pointer zero value (3 trailing zero after dot) ASSERT_EQUALS(true, MathLib::isNullValue(".000")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E0")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E1")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E1")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E1")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E42")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E42")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E42")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E429999")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E429999")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E429999")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E+1")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E+1")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E+1")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E+42")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E+42")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E+42")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E+429999")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E+429999")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E+429999")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E-1")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E-1")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E-1")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E-42")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E-42")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E-42")); // integer scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0E-429999")); ASSERT_EQUALS(true, MathLib::isNullValue("+0E-429999")); ASSERT_EQUALS(true, MathLib::isNullValue("-0E-429999")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0.E0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.E0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.E0")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0.E-0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.E-0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.E+0")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0.E+0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.E+0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.E+0")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("0.00E-0")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.00E-0")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.00E-0")); // floating point scientific notation ASSERT_EQUALS(true, MathLib::isNullValue("00000.00E-000000000")); ASSERT_EQUALS(true, MathLib::isNullValue("+00000.00E-000000000")); ASSERT_EQUALS(true, MathLib::isNullValue("-00000.00E-000000000")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("0.f")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.f")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.f")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("0.0f")); ASSERT_EQUALS(true, MathLib::isNullValue("+0.0f")); ASSERT_EQUALS(true, MathLib::isNullValue("-0.0f")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("00.0f")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.0f")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.0f")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("00.00f")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.00f")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.00f")); // floating point scientific notation (suffix f) ASSERT_EQUALS(true, MathLib::isNullValue("00.00E+1f")); ASSERT_EQUALS(true, MathLib::isNullValue("+00.00E+1f")); ASSERT_EQUALS(true, MathLib::isNullValue("-00.00E+1f")); // binary numbers ASSERT_EQUALS(true, MathLib::isNullValue("0b00")); ASSERT_EQUALS(true, MathLib::isNullValue("+0b00")); ASSERT_EQUALS(true, MathLib::isNullValue("-0b00")); // binary numbers (long) ASSERT_EQUALS(true, MathLib::isNullValue("0b00L")); ASSERT_EQUALS(true, MathLib::isNullValue("+0b00L")); ASSERT_EQUALS(true, MathLib::isNullValue("-0b00L")); // negative testing ASSERT_EQUALS(false, MathLib::isNullValue("0.1")); ASSERT_EQUALS(false, MathLib::isNullValue("1.0")); ASSERT_EQUALS(false, MathLib::isNullValue("0.01")); ASSERT_EQUALS(false, MathLib::isNullValue("-00.01e-12")); ASSERT_EQUALS(false, MathLib::isNullValue("-00.01e+12")); ASSERT_EQUALS(false, MathLib::isNullValue("")); ASSERT_EQUALS(false, MathLib::isNullValue(" ")); ASSERT_EQUALS(false, MathLib::isNullValue("x")); ASSERT_EQUALS(false, MathLib::isNullValue("garbage")); ASSERT_EQUALS(false, MathLib::isNullValue("UL")); } void incdec() const { // increment { const MathLib::biguint num = ~10U; const std::string op = "++"; const std::string strNum = MathLib::incdec(MathLib::toString(num), op); const MathLib::biguint incrementedNum = MathLib::toULongNumber(strNum); ASSERT_EQUALS(num + 1U, incrementedNum); } // decrement { const MathLib::biguint num = ~10U; const std::string op = "--"; const std::string strNum = MathLib::incdec(MathLib::toString(num), op); const MathLib::biguint decrementedNum = MathLib::toULongNumber(strNum); ASSERT_EQUALS(num - 1U, decrementedNum); } // invalid operation ASSERT_THROW(MathLib::incdec("1", "x"), InternalError); // throw } void sin() const { ASSERT_EQUALS("0.0", MathLib::sin("0")); } void cos() const { ASSERT_EQUALS("1.0", MathLib::cos("0")); } void tan() const { ASSERT_EQUALS("0.0", MathLib::tan("0")); } void abs() const { ASSERT_EQUALS("0.0", MathLib::abs("0")); ASSERT_EQUALS("0.0", MathLib::abs("+0")); ASSERT_EQUALS("0.0", MathLib::abs("-0")); ASSERT_EQUALS("1.0", MathLib::abs("+1")); ASSERT_EQUALS("1.0", MathLib::abs("+1.0")); ASSERT_EQUALS("1.0", MathLib::abs("-1")); ASSERT_EQUALS("1.0", MathLib::abs("-1.0")); } void toString() const { ASSERT_EQUALS("0.0", MathLib::toString(0.0)); ASSERT_EQUALS("0.0", MathLib::toString(+0.0)); ASSERT_EQUALS("0.0", MathLib::toString(-0.0)); // float (trailing f or F) ASSERT_EQUALS("0", MathLib::toString(+0.0f)); ASSERT_EQUALS("-0", MathLib::toString(-0.0F)); // double (tailing l or L) ASSERT_EQUALS("0", MathLib::toString(+0.0l)); ASSERT_EQUALS("-0", MathLib::toString(-0.0L)); } void characterLiteralsNormalization() const { // `A` is 0x41 and 0101 ASSERT_EQUALS("A", MathLib::normalizeCharacterLiteral("\\x41")); ASSERT_EQUALS("A", MathLib::normalizeCharacterLiteral("\\101")); // Hexa and octal numbers should not only be interpreted in byte 1 ASSERT_EQUALS("TESTATEST", MathLib::normalizeCharacterLiteral("TEST\\x41TEST")); ASSERT_EQUALS("TESTATEST", MathLib::normalizeCharacterLiteral("TEST\\101TEST")); ASSERT_EQUALS("TESTTESTA", MathLib::normalizeCharacterLiteral("TESTTEST\\x41")); ASSERT_EQUALS("TESTTESTA", MathLib::normalizeCharacterLiteral("TESTTEST\\101")); // Single escape sequences ASSERT_EQUALS("\?", MathLib::normalizeCharacterLiteral("\\?")); ASSERT_EQUALS("\'", MathLib::normalizeCharacterLiteral("\\'")); // Incomplete hexa and octal sequences ASSERT_THROW(MathLib::normalizeCharacterLiteral("\\"), InternalError); ASSERT_THROW(MathLib::normalizeCharacterLiteral("\\x"), InternalError); // No octal digit in an octal sequence ASSERT_THROW(MathLib::normalizeCharacterLiteral("\\9"), InternalError); // Unsupported single escape sequence ASSERT_THROW(MathLib::normalizeCharacterLiteral("\\c"), InternalError); } void CPP14DigitSeparators() const { // Ticket #7137, #7565 ASSERT(MathLib::isDigitSeparator("'", 0) == false); ASSERT(MathLib::isDigitSeparator("123'0;", 3)); ASSERT(MathLib::isDigitSeparator("foo(1'2);", 5)); ASSERT(MathLib::isDigitSeparator("foo(1,1'2);", 7)); ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 7)); ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 13)); ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 16) == false); ASSERT(MathLib::isDigitSeparator("int b=1+9'8;", 9)); ASSERT(MathLib::isDigitSeparator("if (1'2) { char c = 'c'; }", 5)); ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 9)); ASSERT(MathLib::isDigitSeparator("if (120&1'2) { char c = 'c'; }", 9)); ASSERT(MathLib::isDigitSeparator("if (120|1'2) { char c = 'c'; }", 9)); ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 24) == false); ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 26) == false); ASSERT(MathLib::isDigitSeparator("0b0000001'0010'01110", 14)); } }; REGISTER_TEST(TestMathLib) cppcheck-1.90/test/testmemleak.cpp000066400000000000000000002335251357737443600172560ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkmemoryleak.h" #include "preprocessor.h" #include "settings.h" #include "simplecpp.h" #include "standards.h" #include "symboldatabase.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include "tokenlist.h" #include #include #include #include struct InternalError; class TestMemleak : private TestFixture { public: TestMemleak() : TestFixture("TestMemleak") { } private: Settings settings; void run() OVERRIDE { TEST_CASE(testFunctionReturnType); TEST_CASE(open); } CheckMemoryLeak::AllocType functionReturnType(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); const CheckMemoryLeak c(&tokenizer, this, &settings); return c.functionReturnType(&tokenizer.getSymbolDatabase()->scopeList.front().functionList.front()); } void testFunctionReturnType() { { const char code[] = "const char *foo()\n" "{ return 0; }"; ASSERT_EQUALS(CheckMemoryLeak::No, functionReturnType(code)); } { const char code[] = "Fred *newFred()\n" "{ return new Fred; }"; ASSERT_EQUALS(CheckMemoryLeak::New, functionReturnType(code)); } { const char code[] = "char *foo()\n" "{ return new char[100]; }"; ASSERT_EQUALS(CheckMemoryLeak::NewArray, functionReturnType(code)); } { const char code[] = "char *foo()\n" "{\n" " char *p = new char[100];\n" " return p;\n" "}"; ASSERT_EQUALS(CheckMemoryLeak::NewArray, functionReturnType(code)); } } void open() { const char code[] = "class A {\n" " static int open() {\n" " return 1;\n" " }\n" "\n" " A() {\n" " int ret = open();\n" " }\n" "};\n"; // Clear the error buffer.. errout.str(""); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // there is no allocation const Token *tok = Token::findsimplematch(tokenizer.tokens(), "ret ="); const CheckMemoryLeak check(&tokenizer, nullptr, &settings); ASSERT_EQUALS(CheckMemoryLeak::No, check.getAllocationType(tok->tokAt(2), 1)); } }; REGISTER_TEST(TestMemleak) class TestMemleakInFunction : public TestFixture { public: TestMemleakInFunction() : TestFixture("TestMemleakInFunction") { } private: Settings settings0; Settings settings1; Settings settings2; void check(const char code[]) { // Clear the error buffer.. errout.str(""); Settings *settings = &settings1; // Tokenize.. Tokenizer tokenizer(settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for memory leaks.. CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, settings, this); checkMemoryLeak.checkReallocUsage(); } void run() OVERRIDE { LOAD_LIB_2(settings1.library, "std.cfg"); LOAD_LIB_2(settings1.library, "posix.cfg"); LOAD_LIB_2(settings2.library, "std.cfg"); TEST_CASE(realloc1); TEST_CASE(realloc2); TEST_CASE(realloc3); TEST_CASE(realloc4); TEST_CASE(realloc5); TEST_CASE(realloc7); TEST_CASE(realloc8); TEST_CASE(realloc9); TEST_CASE(realloc10); TEST_CASE(realloc11); TEST_CASE(realloc12); TEST_CASE(realloc13); TEST_CASE(realloc14); TEST_CASE(realloc15); TEST_CASE(realloc16); TEST_CASE(realloc17); TEST_CASE(realloc18); TEST_CASE(realloc19); TEST_CASE(realloc20); TEST_CASE(realloc21); TEST_CASE(realloc22); TEST_CASE(realloc23); TEST_CASE(reallocarray1); } void realloc1() { check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = realloc(a, 100);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc2() { check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = (char *)realloc(a, 100);\n" " free(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc3() { check("void foo()\n" "{\n" " char *a = 0;\n" " if ((a = realloc(a, 100)) == NULL)\n" " return;\n" " free(a);\n" "}"); TODO_ASSERT_EQUALS("", "[test.cpp:4]: (error) Common realloc mistake: 'a' nulled but not freed upon failure\n", errout.str()); } void realloc4() { check("void foo()\n" "{\n" " static char *a = 0;\n" " if ((a = realloc(a, 100)) == NULL)\n" " return;\n" " free(a);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: a\n", "[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc5() { check("void foo()\n" "{\n" " char *buf;\n" " char *new_buf;\n" " buf = calloc( 10 );\n" " new_buf = realloc ( buf, 20);\n" " if ( !new_buf )\n" " free(buf);\n" " else\n" " free(new_buf);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc7() { check("bool foo(size_t nLen, char* pData)\n" "{\n" " pData = (char*) realloc(pData, sizeof(char) + (nLen + 1)*sizeof(char));\n" " if ( pData == NULL )\n" " {\n" " return false;\n" " }\n" " free(pData);\n" " return true;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc8() { check("void foo()\n" "{\n" " char *origBuf = m_buf;\n" " m_buf = (char *) realloc (m_buf, m_capacity + growBy);\n" " if (!m_buf) {\n" " m_buf = origBuf;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc9() { check("void foo()\n" "{\n" " x = realloc(x,100);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc10() { check("void foo() {\n" " char *pa, *pb;\n" " pa = pb = malloc(10);\n" " pa = realloc(pa, 20);" " exit();\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc11() { check("void foo() {\n" " char *p;\n" " p = realloc(p, size);\n" " if (!p)\n" " error();\n" " usep(p);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc12() { check("void foo(int x)\n" "{\n" " char *a = 0;\n" " if ((a = realloc(a, x + 100)) == NULL)\n" " return;\n" " free(a);\n" "}"); TODO_ASSERT_EQUALS("", "[test.cpp:4]: (error) Common realloc mistake: 'a' nulled but not freed upon failure\n", errout.str()); } void realloc13() { check("void foo()\n" "{\n" " char **str;\n" " *str = realloc(*str,100);\n" " free (*str);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'str\' nulled but not freed upon failure\n", errout.str()); } void realloc14() { check("void foo() {\n" " char *p;\n" " p = realloc(p, size + 1);\n" " if (!p)\n" " error();\n" " usep(p);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc15() { check("bool foo() {\n" " char ** m_options;\n" " m_options = (char**)realloc( m_options, 2 * sizeof(char*));\n" " if( m_options == NULL )\n" " return false;\n" " return true;\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Common realloc mistake: \'m_options\' nulled but not freed upon failure\n", errout.str()); } void realloc16() { check("void f(char *zLine) {\n" " zLine = realloc(zLine, 42);\n" " if (zLine) {\n" " free(zLine);\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void realloc17() { check("void foo()\n" "{\n" " void ***a = malloc(sizeof(a));\n" " ***a = realloc(***(a), sizeof(a) * 2);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc18() { check("void foo()\n" "{\n" " void *a = malloc(sizeof(a));\n" " a = realloc((void*)a, sizeof(a) * 2);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc19() { check("void foo()\n" "{\n" " void *a = malloc(sizeof(a));\n" " a = (realloc((void*)((a)), sizeof(a) * 2));\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } void realloc20() { check("void foo()\n" "{\n" " void *a = malloc(sizeof(a));\n" " a = realloc((a) + 1, sizeof(a) * 2);\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc21() { check("char *foo(char *bs0)\n" "{\n" " char *bs = bs0;\n" " bs = realloc(bs, 100);\n" " if (bs == NULL) return bs0;\n" " return bs;\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc22() { check("void foo(char **bsp)\n" "{\n" " char *bs = *bsp;\n" " bs = realloc(bs, 100);\n" " if (bs == NULL) return;\n" " *bsp = bs;\n" "}"); ASSERT_EQUALS("", errout.str()); } void realloc23() { check("void foo(struct ABC *s)\n" "{\n" " uint32_t *cigar = s->cigar;\n" " if (!(cigar = realloc(cigar, 100 * sizeof(*cigar))))\n" " return;\n" " s->cigar = cigar;\n" "}"); ASSERT_EQUALS("", errout.str()); } void reallocarray1() { check("void foo()\n" "{\n" " char *a = (char *)malloc(10);\n" " a = reallocarray(a, 100, 2);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Common reallocarray mistake: \'a\' nulled but not freed upon failure\n", errout.str()); } }; REGISTER_TEST(TestMemleakInFunction) class TestMemleakInClass : public TestFixture { public: TestMemleakInClass() : TestFixture("TestMemleakInClass") { } private: Settings settings; /** * Tokenize and execute leak check for given code * @param code Source code */ void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); tokenizer.simplifyTokenList2(); // Check for memory leaks.. CheckMemoryLeakInClass checkMemoryLeak(&tokenizer, &settings, this); checkMemoryLeak.check(); } void run() OVERRIDE { settings.addEnabled("warning"); settings.addEnabled("style"); LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(class1); TEST_CASE(class2); TEST_CASE(class3); TEST_CASE(class4); TEST_CASE(class6); TEST_CASE(class7); TEST_CASE(class8); TEST_CASE(class9); TEST_CASE(class10); TEST_CASE(class11); TEST_CASE(class12); TEST_CASE(class13); TEST_CASE(class14); TEST_CASE(class15); TEST_CASE(class16); TEST_CASE(class17); TEST_CASE(class18); TEST_CASE(class19); // ticket #2219 TEST_CASE(class20); TEST_CASE(class21); // ticket #2517 TEST_CASE(class22); // ticket #3012 TEST_CASE(class23); // ticket #3303 TEST_CASE(class24); // ticket #3806 - false positive in copy constructor TEST_CASE(class25); // ticket #4367 - false positive implementation for destructor is not seen TEST_CASE(staticvar); TEST_CASE(free_member_in_sub_func); TEST_CASE(mismatch1); TEST_CASE(mismatch2); // #5659 // allocating member variable in public function TEST_CASE(func1); TEST_CASE(func2); } void class1() { check("class Fred\n" "{\n" "private:\n" " char *str1;\n" " char *str2;\n" "public:\n" " Fred();\n" " ~Fred();\n" "};\n" "\n" "Fred::Fred()\n" "{\n" " str1 = new char[10];\n" " str2 = new char[10];\n" "}\n" "\n" "Fred::~Fred()\n" "{\n" " delete [] str2;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " char *str1;\n" " char *str2;\n" "public:\n" " Fred()\n" " {\n" " str1 = new char[10];\n" " str2 = new char[10];\n" " }\n" " ~Fred()\n" " {\n" " delete [] str2;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); } void class2() { check("class Fred\n" "{\n" "private:\n" " char *str1;\n" "public:\n" " Fred();\n" " ~Fred();\n" "};\n" "\n" "Fred::Fred()\n" "{\n" " str1 = new char[10];\n" "}\n" "\n" "Fred::~Fred()\n" "{\n" " free(str1);\n" "}"); ASSERT_EQUALS("[test.cpp:17]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout.str()); check("class Fred\n" "{\n" "private:\n" " char *str1;\n" "public:\n" " Fred()\n" " {\n" " str1 = new char[10];\n" " }\n" " ~Fred()\n" " {\n" " free(str1);\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:12]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout.str()); } void class3() { check("class Token;\n" "\n" "class Tokenizer\n" "{\n" "private:\n" " Token *_tokens;\n" "\n" "public:\n" " Tokenizer();\n" " ~Tokenizer();\n" " void deleteTokens(Token *tok);\n" "};\n" "\n" "Tokenizer::Tokenizer()\n" "{\n" " _tokens = new Token;\n" "}\n" "\n" "Tokenizer::~Tokenizer()\n" "{\n" " deleteTokens(_tokens);\n" "}\n" "\n" "void Tokenizer::deleteTokens(Token *tok)\n" "{\n" " while (tok)\n" " {\n" " Token *next = tok->next();\n" " delete tok;\n" " tok = next;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Token;\n" "\n" "class Tokenizer\n" "{\n" "private:\n" " Token *_tokens;\n" "\n" "public:\n" " Tokenizer()\n" " {\n" " _tokens = new Token;\n" " }\n" " ~Tokenizer()\n" " {\n" " deleteTokens(_tokens);\n" " }\n" " void deleteTokens(Token *tok)\n" " {\n" " while (tok)\n" " {\n" " Token *next = tok->next();\n" " delete tok;\n" " tok = next;\n" " }\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class4() { check("struct ABC;\n" "class Fred\n" "{\n" "private:\n" " void addAbc(ABC *abc);\n" "public:\n" " void click();\n" "};\n" "\n" "void Fred::addAbc(ABC* abc)\n" "{\n" " AbcPosts->Add(abc);\n" "}\n" "\n" "void Fred::click()\n" "{\n" " ABC *p = new ABC;\n" " addAbc( p );\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct ABC;\n" "class Fred\n" "{\n" "private:\n" " void addAbc(ABC* abc)\n" " {\n" " AbcPosts->Add(abc);\n" " }\n" "public:\n" " void click()\n" " {\n" " ABC *p = new ABC;\n" " addAbc( p );\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class6() { check("class Fred\n" "{\n" "public:\n" " void foo();\n" "};\n" "\n" "void Fred::foo()\n" "{\n" " char *str = new char[100];\n" " delete [] str;\n" " hello();\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " void foo()\n" " {\n" " char *str = new char[100];\n" " delete [] str;\n" " hello();\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class7() { check("class Fred\n" "{\n" "public:\n" " int *i;\n" " Fred();\n" " ~Fred();\n" "};\n" "\n" "Fred::Fred()\n" "{\n" " this->i = new int;\n" "}\n" "Fred::~Fred()\n" "{\n" " delete this->i;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class Fred\n" "{\n" "public:\n" " int *i;\n" " Fred()\n" " {\n" " this->i = new int;\n" " }\n" " ~Fred()\n" " {\n" " delete this->i;\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class8() { check("class A\n" "{\n" "public:\n" " void a();\n" " void doNothing() { }\n" "};\n" "\n" "void A::a()\n" "{\n" " int* c = new int(1);\n" " delete c;\n" " doNothing(c);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" "public:\n" " void a()\n" " {\n" " int* c = new int(1);\n" " delete c;\n" " doNothing(c);\n" " }\n" " void doNothing() { }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class9() { check("class A\n" "{\n" "public:\n" " int * p;\n" " A();\n" " ~A();\n" "};\n" "\n" "A::A()\n" "{ p = new int; }\n" "\n" "A::~A()\n" "{ delete (p); }"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" "public:\n" " int * p;\n" " A()\n" " { p = new int; }\n" " ~A()\n" " { delete (p); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class10() { check("class A\n" "{\n" "public:\n" " int * p;\n" " A();\n" "};\n" "A::A()\n" "{ p = new int; }"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" "public:\n" " int * p;\n" " A() { p = new int; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); } void class11() { check("class A\n" "{\n" "public:\n" " int * p;\n" " A() : p(new int[10])\n" " { }" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" "public:\n" " int * p;\n" " A();\n" "};\n" "A::A() : p(new int[10])\n" "{ }"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); } void class12() { check("class A\n" "{\n" "private:\n" " int *p;\n" "public:\n" " A();\n" " ~A();\n" " void cleanup();" "};\n" "\n" "A::A()\n" "{ p = new int[10]; }\n" "\n" "A::~A()\n" "{ }\n" "\n" "void A::cleanup()\n" "{ delete [] p; }"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" "private:\n" " int *p;\n" "public:\n" " A()\n" " { p = new int[10]; }\n" " ~A()\n" " { }\n" " void cleanup()\n" " { delete [] p; }\n" "};"); ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); } void class13() { check("class A\n" "{\n" "private:\n" " int *p;\n" "public:\n" " A();\n" " ~A();\n" " void foo();" "};\n" "\n" "A::A()\n" "{ }\n" "\n" "A::~A()\n" "{ }\n" "\n" "void A::foo()\n" "{ p = new int[10]; delete [] p; }"); ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n", errout.str()); check("class A\n" "{\n" "private:\n" " int *p;\n" "public:\n" " A()\n" " { }\n" " ~A()\n" " { }\n" " void foo()\n" " { p = new int[10]; delete [] p; }\n" "};"); ASSERT_EQUALS("[test.cpp:11]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n", errout.str()); } void class14() { check("class A\n" "{\n" " int *p;\n" "public:\n" " void init();\n" "};\n" "\n" "void A::init()\n" "{ p = new int[10]; }"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init()\n" " { p = new int[10]; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init();\n" "};\n" "\n" "void A::init()\n" "{ p = new int; }"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init()\n" " { p = new int; }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init();\n" "};\n" "\n" "void A::init()\n" "{ p = malloc(sizeof(int)*10); }"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " void init()\n" " { p = malloc(sizeof(int)*10); }\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout.str()); } void class15() { check("class A\n" "{\n" " int *p;\n" "public:\n" " A();\n" " ~A() { delete [] p; }\n" "};\n" "A::A()\n" "{ p = new int[10]; }"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A()\n" " { p = new int[10]; }\n" " ~A() { delete [] p; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A();\n" " ~A() { delete p; }\n" "};\n" "A::A()\n" "{ p = new int; }"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A()\n" " { p = new int; }\n" " ~A() { delete p; }\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A();\n" " ~A() { free(p); }\n" "};\n" "A::A()\n" "{ p = malloc(sizeof(int)*10); }"); ASSERT_EQUALS("", errout.str()); check("class A\n" "{\n" " int *p;\n" "public:\n" " A()\n" " { p = malloc(sizeof(int)*10); }\n" " ~A() { free(p); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class16() { // Ticket #1510 check("class A\n" "{\n" " int *a;\n" " int *b;\n" "public:\n" " A() { a = b = new int[10]; }\n" " ~A() { delete [] a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class17() { // Ticket #1557 check("class A {\n" "private:\n" " char *pd;\n" "public:\n" " void foo();\n" "};\n" "\n" "void A::foo()\n" "{\n" " A::pd = new char[12];\n" " delete [] A::pd;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout.str()); check("class A {\n" "private:\n" " char *pd;\n" "public:\n" " void foo()\n" " {\n" " pd = new char[12];\n" " delete [] pd;\n" " }\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout.str()); check("class A {\n" "private:\n" " char *pd;\n" "public:\n" " void foo();\n" "};\n" "\n" "void A::foo()\n" "{\n" " pd = new char[12];\n" " delete [] pd;\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout.str()); } void class18() { // Ticket #853 check("class A : public x\n" "{\n" "public:\n" " A()\n" " {\n" " a = new char[10];\n" " foo(a);\n" " }\n" "private:\n" " char *a;\n" "};"); ASSERT_EQUALS("", errout.str()); check("class A : public x\n" "{\n" "public:\n" " A();\n" "private:\n" " char *a;\n" "};\n" "A::A()\n" "{\n" " a = new char[10];\n" " foo(a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void class19() { // Ticket #2219 check("class Foo\n" "{\n" "private:\n" " TRadioButton* rp1;\n" " TRadioButton* rp2;\n" "public:\n" " Foo();\n" "};\n" "Foo::Foo()\n" "{\n" " rp1 = new TRadioButton(this);\n" " rp2 = new TRadioButton(this);\n" "}"); ASSERT_EQUALS("", errout.str()); check("class TRadioButton { };\n" "class Foo\n" "{\n" "private:\n" " TRadioButton* rp1;\n" " TRadioButton* rp2;\n" "public:\n" " Foo();\n" "};\n" "Foo::Foo()\n" "{\n" " rp1 = new TRadioButton;\n" " rp2 = new TRadioButton;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Foo' is unsafe, 'Foo::rp1' can leak by wrong usage.\n" "[test.cpp:6]: (style) Class 'Foo' is unsafe, 'Foo::rp2' can leak by wrong usage.\n", errout.str()); check("class TRadioButton { };\n" "class Foo\n" "{\n" "private:\n" " TRadioButton* rp1;\n" " TRadioButton* rp2;\n" "public:\n" " Foo();\n" " ~Foo();\n" "};\n" "Foo::Foo()\n" "{\n" " rp1 = new TRadioButton;\n" " rp2 = new TRadioButton;\n" "}\n" "Foo::~Foo()\n" "{\n" " delete rp1;\n" " delete rp2;\n" "}"); ASSERT_EQUALS("", errout.str()); } void class20() { check("namespace ns1 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred()\n" " {\n" " str1 = new char[10];\n" " str2 = new char[10];\n" " }\n" " ~Fred()\n" " {\n" " delete [] str2;\n" " }\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("namespace ns1 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred();\n" " ~Fred();\n" " };\n" "\n" " Fred::Fred()\n" " {\n" " str1 = new char[10];\n" " str2 = new char[10];\n" " }\n" "\n" " Fred::~Fred()\n" " {\n" " delete [] str2;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("namespace ns1 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred();\n" " ~Fred();\n" " };\n" "}\n" "ns1::Fred::Fred()\n" "{\n" " str1 = new char[10];\n" " str2 = new char[10];\n" "}\n" "\n" "ns1::Fred::~Fred()\n" "{\n" " delete [] str2;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("namespace ns1 {\n" " namespace ns2 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred();\n" " ~Fred();\n" " };\n" " }\n" "}\n" "ns1::ns2::Fred::Fred()\n" "{\n" " str1 = new char[10];\n" " str2 = new char[10];\n" "}\n" "\n" "ns1::ns2::Fred::~Fred()\n" "{\n" " delete [] str2;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); check("namespace ns1 {\n" " namespace ns2 {\n" " namespace ns3 {\n" " class Fred\n" " {\n" " private:\n" " char *str1;\n" " char *str2;\n" " public:\n" " Fred();\n" " ~Fred();\n" " };\n" " }\n" " }\n" "}\n" "ns1::ns2::ns3::Fred::Fred()\n" "{\n" " str1 = new char[10];\n" " str2 = new char[10];\n" "}\n" "\n" "ns1::ns2::ns3::Fred::~Fred()\n" "{\n" " delete [] str2;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout.str()); } void class21() { // ticket #2517 check("struct B { };\n" "struct C\n" "{\n" " B * b;\n" " C(B * x) : b(x) { }\n" "};\n" "class A\n" "{\n" " B *b;\n" " C *c;\n" "public:\n" " A() : b(new B()), c(new C(b)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (style) Class 'A' is unsafe, 'A::b' can leak by wrong usage.\n" "[test.cpp:10]: (style) Class 'A' is unsafe, 'A::c' can leak by wrong usage.\n", errout.str()); check("struct B { };\n" "struct C\n" "{\n" " B * b;\n" " C(B * x) : b(x) { }\n" "};\n" "class A\n" "{\n" " B *b;\n" " C *c;\n" "public:\n" " A()\n" " {\n" " b = new B();\n" " c = new C(b);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (style) Class 'A' is unsafe, 'A::b' can leak by wrong usage.\n" "[test.cpp:10]: (style) Class 'A' is unsafe, 'A::c' can leak by wrong usage.\n", errout.str()); } void class22() { // ticket #3012 - false positive check("class Fred {\n" "private:\n" " int * a;\n" "private:\n" " Fred() { a = new int; }\n" " ~Fred() { (delete(a), (a)=NULL); }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class23() { // ticket #3303 - false positive check("class CDataImpl {\n" "public:\n" " CDataImpl() { m_refcount = 1; }\n" " void Release() { if (--m_refcount == 0) delete this; }\n" "private:\n" " int m_refcount;\n" "};\n" "\n" "class CData {\n" "public:\n" " CData() : m_impl(new CDataImpl()) { }\n" " ~CData() { if (m_impl) m_impl->Release(); }\n" "private:\n" " CDataImpl *m_impl;\n" "};"); ASSERT_EQUALS("", errout.str()); } void class24() { // ticket #3806 - false positive in copy constructor check("class Fred {\n" "private:\n" " int * a;\n" "public:\n" " Fred(const Fred &fred) { a = new int; }\n" " ~Fred() { delete a; }\n" "};"); ASSERT_EQUALS("", errout.str()); } void class25() { // ticket #4367 - false positive when implementation for destructor is not seen check("class Fred {\n" "private:\n" " int * a;\n" "public:\n" " Fred() { a = new int; }\n" " ~Fred();\n" "};"); ASSERT_EQUALS("", errout.str()); } void staticvar() { check("class A\n" "{\n" "private:\n" " static int * p;\n" "public:" " A()\n" " {\n" " if (!p)\n" " p = new int[100];\n" " }\n" "};"); ASSERT_EQUALS("", errout.str()); } void free_member_in_sub_func() { // Member function check("class Tokenizer\n" "{\n" "public:\n" " Tokenizer();\n" " ~Tokenizer();\n" "\n" "private:\n" " int *_tokens;\n" " static void deleteTokens(int *tok);\n" "};\n" "\n" "Tokenizer::Tokenizer()\n" "{\n" " _tokens = new int;\n" "}\n" "\n" "Tokenizer::~Tokenizer()\n" "{\n" " deleteTokens(_tokens);\n" " _tokens = 0;\n" "}\n" "\n" "void Tokenizer::deleteTokens(int *tok)\n" "{\n" " delete tok;\n" "}"); ASSERT_EQUALS("", errout.str()); // Global function check("void deleteTokens(int *tok)\n" "{\n" " delete tok;\n" "}\n" "class Tokenizer\n" "{\n" "public:\n" " Tokenizer();\n" " ~Tokenizer();\n" "\n" "private:\n" " int *_tokens;\n" "};\n" "\n" "Tokenizer::Tokenizer()\n" "{\n" " _tokens = new int;\n" "}\n" "\n" "Tokenizer::~Tokenizer()\n" "{\n" " deleteTokens(_tokens);\n" " _tokens = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void mismatch1() { check("class A\n" "{\n" "public:\n" " A(int i);\n" " ~A();\n" "private:\n" " char* pkt_buffer;\n" "};\n" "\n" "A::A(int i)\n" "{\n" " pkt_buffer = new char[8192];\n" " if (i != 1) {\n" " delete pkt_buffer;\n" " pkt_buffer = 0;\n" " }\n" "}\n" "\n" "A::~A() {\n" " delete [] pkt_buffer;\n" "}"); ASSERT_EQUALS("[test.cpp:14]: (error) Mismatching allocation and deallocation: A::pkt_buffer\n", errout.str()); } void mismatch2() { // #5659 check("namespace NS\n" "{\n" "class Foo\n" "{\n" "public:\n" " void fct();\n" "\n" "private:\n" " char* data_;\n" "};\n" "}\n" "\n" "using namespace NS;\n" "\n" "void Foo::fct()\n" "{\n" " data_ = new char[42];\n" " delete data_;\n" " data_ = 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'data_' is not deallocated before it is allocated.\n" "[test.cpp:18]: (error) Mismatching allocation and deallocation: Foo::data_\n", errout.str()); check("namespace NS\n" "{\n" "class Foo\n" "{\n" "public:\n" " void fct(int i);\n" "\n" "private:\n" " char* data_;\n" "};\n" "}\n" "\n" "using namespace NS;\n" "\n" "void Foo::fct(int i)\n" "{\n" " data_ = new char[42];\n" " delete data_;\n" " data_ = 0;\n" "}\n"); ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'data_' is not deallocated before it is allocated.\n" "[test.cpp:18]: (error) Mismatching allocation and deallocation: Foo::data_\n", errout.str()); } void func1() { check("class Fred\n" "{\n" "private:\n" " char *s;\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { free(s); }\n" " void xy()\n" " { s = malloc(100); }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout.str()); check("class Fred\n" "{\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { free(s); }\n" " void xy()\n" " { s = malloc(100); }\n" "private:\n" " char *s;\n" "};"); ASSERT_EQUALS("[test.cpp:7]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout.str()); } void func2() { check("class Fred\n" "{\n" "private:\n" " char *s;\n" "public:\n" " Fred() { s = 0; }\n" " ~Fred() { free(s); }\n" " const Fred & operator = (const Fred &f)\n" " { s = malloc(100); }\n" "};"); ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout.str()); } }; REGISTER_TEST(TestMemleakInClass) class TestMemleakStructMember : public TestFixture { public: TestMemleakStructMember() : TestFixture("TestMemleakStructMember") { } private: Settings settings; void check(const char code[], bool isCPP = true) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, isCPP ? "test.cpp" : "test.c"); tokenizer.simplifyTokenList2(); // Check for memory leaks.. CheckMemoryLeakStructMember checkMemoryLeakStructMember(&tokenizer, &settings, this); checkMemoryLeakStructMember.check(); } void run() OVERRIDE { LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "posix.cfg"); // testing that errors are detected TEST_CASE(err); // handle / bail out when "goto" is found TEST_CASE(goto_); // Don't report errors if the struct is returned TEST_CASE(ret1); TEST_CASE(ret2); // assignments TEST_CASE(assign1); TEST_CASE(assign2); TEST_CASE(assign3); // Failed allocation TEST_CASE(failedAllocation); TEST_CASE(function1); // Deallocating in function TEST_CASE(function2); // #2848: Taking address in function TEST_CASE(function3); // #3024: kernel list TEST_CASE(function4); // #3038: Deallocating in function // Handle if-else TEST_CASE(ifelse); // Linked list TEST_CASE(linkedlist); // struct variable is a global variable TEST_CASE(globalvar); // local struct variable TEST_CASE(localvars); // struct variable is a reference variable TEST_CASE(refvar); // Segmentation fault in CheckMemoryLeakStructMember TEST_CASE(trac5030); TEST_CASE(varid); // #5201: Analysis confused by (variable).attribute notation TEST_CASE(varid_2); // #5315: Analysis confused by ((variable).attribute) notation TEST_CASE(customAllocation); } void err() { check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " free(abc);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: abc.a\n", errout.str()); check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: abc.a\n", errout.str()); check("static ABC * foo()\n" "{\n" " ABC *abc = malloc(sizeof(ABC));\n" " abc->a = malloc(10);\n" " abc->b = malloc(10);\n" " if (abc->b == 0)\n" " {\n" " return 0;\n" " }\n" " return abc;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: abc.a\n", errout.str()); check("static void foo(int a)\n" "{\n" " ABC *abc = malloc(sizeof(ABC));\n" " abc->a = malloc(10);\n" " if (a == 1)\n" " {\n" " free(abc->a);\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: abc.a\n", errout.str()); } void goto_() { check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " if (abc->a)\n" " { goto out; }\n" " free(abc);\n" " return;\n" "out:\n" " free(abc->a);\n" " free(abc);\n" "}"); ASSERT_EQUALS("", errout.str()); } void ret1() { check("static ABC * foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " return abc;\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void foo(struct ABC *abc)\n" "{\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7302 check("void* foo() {\n" " struct ABC abc;\n" " abc.a = malloc(10);\n" " return abc.a;\n" "}", false); ASSERT_EQUALS("", errout.str()); check("void* foo() {\n" " struct ABC abc;\n" " abc.a = malloc(10);\n" " return abc.b;\n" "}", false); ASSERT_EQUALS("[test.c:4]: (error) Memory leak: abc.a\n", errout.str()); } void ret2() { check("static ABC * foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " return &abc->self;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign1() { check("static void foo()\n" "{\n" " struct ABC *abc = abc1;\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void foo()\n" "{\n" " struct ABC *abc;\n" " abc1 = abc = malloc(sizeof(ABC));\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void foo()\n" "{\n" " struct msn_entry *ptr;\n" " ptr = malloc(sizeof(struct msn_entry));\n" " ptr->msn = malloc(100);\n" " back = ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign2() { check("static void foo() {\n" " struct ABC *abc = malloc(123);\n" " abc->a = abc->b = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); } void assign3() { check("void f(struct s *f1) {\n" " struct s f2;\n" " f2.a = malloc(100);\n" " *f1 = f2;\n" "}", false); ASSERT_EQUALS("", errout.str()); } void failedAllocation() { check("static struct ABC * foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " if (!abc->a)\n" " {\n" " free(abc);\n" " return 0;\n" " }\n" " return abc;\n" "}"); ASSERT_EQUALS("", errout.str()); } void function1() { // Not found function => assume that the function may deallocate check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " func(abc);\n" "}"); ASSERT_EQUALS("", errout.str()); check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abclist.push_back(abc);\n" " abc->a = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); } // #2848: Taking address in function 'assign' void function2() { check("void f() {\n" " A a = { 0 };\n" " a.foo = (char *) malloc(10);\n" " assign(&a);\n" "}", false); ASSERT_EQUALS("", errout.str()); } // #3024: kernel list void function3() { check("void f() {\n" " struct ABC *abc = malloc(100);\n" " abc.a = (char *) malloc(10);\n" " list_add_tail(&abc->list, head);\n" "}", false); ASSERT_EQUALS("", errout.str()); } // #3038: deallocating in function void function4() { check("void a(char *p) { char *x = p; free(x); }\n" "void b() {\n" " struct ABC abc;\n" " abc.a = (char *) malloc(10);\n" " a(abc.a);\n" "}", false); ASSERT_EQUALS("", errout.str()); } void ifelse() { check("static void foo()\n" "{\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " if (x)" " {\n" " abc->a = malloc(10);\n" " }\n" " else\n" " {\n" " free(abc);\n" " return;\n" " }\n" " free(abc->a);\n" " free(abc);\n" "}"); ASSERT_EQUALS("", errout.str()); } void linkedlist() { // #3904 - false positive when linked list is used check("static void foo() {\n" " struct ABC *abc = malloc(sizeof(struct ABC));\n" " abc->next = malloc(sizeof(struct ABC));\n" " abc->next->next = NULL;\n" "\n" " while (abc) {\n" " struct ABC *next = abc->next;\n" " free(abc);\n" " abc = next;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void globalvar() { check("struct ABC *abc;\n" "\n" "static void foo()\n" "{\n" " abc = malloc(sizeof(struct ABC));\n" " abc->a = malloc(10);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Ticket #933 Leaks with struct members not detected void localvars() { // Test error case const char code1[] = "struct A {\n" " FILE* f;\n" " char* c;\n" " void* m;\n" "};\n" "\n" "void func() {\n" " struct A a;\n" " a.f = fopen(\"test\", \"r\");\n" " a.c = new char[12];\n" " a.m = malloc(12);\n" "}"; check(code1, true); ASSERT_EQUALS("[test.cpp:12]: (error) Memory leak: a.f\n" "[test.cpp:12]: (error) Memory leak: a.c\n" "[test.cpp:12]: (error) Memory leak: a.m\n", errout.str()); check(code1, false); ASSERT_EQUALS("[test.c:12]: (error) Memory leak: a.f\n" "[test.c:12]: (error) Memory leak: a.m\n", errout.str()); // Test OK case const char code2[] = "struct A {\n" " FILE* f;\n" " char* c;\n" " void* m;\n" "};\n" "\n" "void func() {\n" " struct A a;\n" " a.f = fopen(\"test\", \"r\");\n" " a.c = new char[12];\n" " a.m = malloc(12);\n" " fclose(a.f);\n" " delete [] a.c;\n" " free(a.m);\n" "}"; check(code2, true); ASSERT_EQUALS("", errout.str()); check(code2, false); ASSERT_EQUALS("", errout.str()); // Test unknown struct. In C++, it might have a destructor const char code3[] = "void func() {\n" " struct A a;\n" " a.f = fopen(\"test\", \"r\");\n" "}"; check(code3, true); ASSERT_EQUALS("", errout.str()); check(code3, false); ASSERT_EQUALS("[test.c:4]: (error) Memory leak: a.f\n", errout.str()); // Test struct with destructor const char code4[] = "struct A {\n" " FILE* f;\n" " ~A();\n" "};\n" "void func() {\n" " struct A a;\n" " a.f = fopen(\"test\", \"r\");\n" "}"; check(code4, true); ASSERT_EQUALS("", errout.str()); } void refvar() { // #8116 check("struct Test\n" "{\n" " int* data;\n" "};\n" "\n" "void foo(Test* x)\n" "{\n" " Test& y = *x;\n" " y.data = malloc(10);\n" "}"); ASSERT_EQUALS("", errout.str()); } // don't crash void trac5030() { check("bool bob( char const **column_ptrs ) {\n" "unique_ptrotherbuffer{new char[otherbufsize+1]};\n" "char *const oldbuffer = otherbuffer.get();\n" "int const oldbufsize = otherbufsize;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varid() { // #5201 check("struct S {\n" " void *state_check_buff;\n" "};\n" "void f() {\n" " S s;\n" " (s).state_check_buff = (void* )malloc(1);\n" " if (s.state_check_buff == 0)\n" " return;\n" "}", false); ASSERT_EQUALS("[test.c:9]: (error) Memory leak: s.state_check_buff\n", errout.str()); } void varid_2() { // #5315 check("typedef struct foo { char *realm; } foo;\n" "void build_principal() {\n" " foo f;\n" " ((f)->realm) = strdup(realm);\n" " if(f->realm == NULL) {}\n" "}", false); ASSERT_EQUALS("[test.c:6]: (error) Memory leak: f.realm\n", errout.str()); } void customAllocation() { // #4770 check("char *myalloc(void) {\n" " return malloc(100);\n" "}\n" "void func() {\n" " struct ABC abc;\n" " abc.a = myalloc();\n" "}", false); ASSERT_EQUALS("[test.c:7]: (error) Memory leak: abc.a\n", errout.str()); } }; REGISTER_TEST(TestMemleakStructMember) class TestMemleakNoVar : public TestFixture { public: TestMemleakNoVar() : TestFixture("TestMemleakNoVar") { } private: Settings settings; void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for memory leaks.. CheckMemoryLeakNoVar checkMemoryLeakNoVar(&tokenizer, &settings, this); checkMemoryLeakNoVar.check(); } void run() OVERRIDE { settings.inconclusive = true; settings.libraries.emplace_back("posix"); settings.addEnabled("warning"); LOAD_LIB_2(settings.library, "std.cfg"); LOAD_LIB_2(settings.library, "posix.cfg"); // pass allocated memory to function.. TEST_CASE(functionParameter); // never use leakable resource TEST_CASE(missingAssignment); // pass allocated memory to function using a smart pointer TEST_CASE(smartPointerFunctionParam); TEST_CASE(resourceLeak); // Test getAllocationType for subfunction TEST_CASE(getAllocationType); } void functionParameter() { // standard function.. check("void x() {\n" " strcpy(a, strdup(p));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout.str()); check("char *x() {\n" " char *ret = strcpy(malloc(10), \"abc\");\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x() {\n" " free(malloc(10));\n" "}"); ASSERT_EQUALS("", errout.str()); // user function.. check("void set_error(const char *msg) {\n" "}\n" "\n" "void x() {\n" " set_error(strdup(p));\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Allocation with strdup, set_error doesn't release it.\n", "", errout.str()); check("void f()\n" "{\n" " int fd;\n" " fd = mkstemp(strdup(\"/tmp/file.XXXXXXXX\"));\n" " close(fd);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Allocation with strdup, mkstemp doesn't release it.\n", "", errout.str()); check("void f()\n" "{\n" " if(TRUE || strcmp(strdup(a), b));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout.str()); check("void f()\n" "{\n" " if(!strcmp(strdup(a), b) == 0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout.str()); check("void f()\n" "{\n" " 42, strcmp(strdup(a), b);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout.str()); check("void f() {\n" " assert(freopen(\"/dev/null\", \"r\", stdin));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x() {\n" " strcpy(a, (void*)strdup(p));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout.str()); check("void* malloc1() {\n" " return (malloc(1));\n" "}"); ASSERT_EQUALS("", errout.str()); check("char *x() {\n" " char *ret = (char*)strcpy(malloc(10), \"abc\");\n" " return ret;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " free(malloc(1));\n" " strcpy(a, strdup(p));\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout.str()); check("void f() {\n" " memcmp(calloc(10, 10), strdup(q), 100);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with calloc, memcmp doesn't release it.\n" "[test.cpp:2]: (error) Allocation with strdup, memcmp doesn't release it.\n", errout.str()); check("void* f(int size) {\n" " return (void*) malloc(size);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int* f(int size) {\n" " return static_cast(malloc(size));\n" "}"); ASSERT_EQUALS("", errout.str()); } void missingAssignment() { check("void x()\n" "{\n" " malloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); check("void x()\n" "{\n" " calloc(10, 1);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'calloc' is not stored.\n", errout.str()); check("void x()\n" "{\n" " strdup(\"Test\");\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'strdup' is not stored.\n", errout.str()); check("void x()\n" "{\n" " reallocarray(NULL, 10, 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'reallocarray' is not stored.\n", errout.str()); check("void x()\n" "{\n" " (char*) malloc(10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); check("void x()\n" "{\n" " char* ptr = malloc(10);\n" " foo(ptr);\n" " free(ptr);\n" "}"); ASSERT_EQUALS("", errout.str()); check("char** x(const char* str) {\n" " char* ptr[] = { malloc(10), malloc(5), strdup(str) };\n" " return ptr;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x()\n" "{\n" " 42,malloc(42);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); check("void *f()\n" "{\n" " return malloc(10);\n" "}\n" "void x()\n" "{\n" " f();\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Return value of allocation function 'f' is not stored.\n", errout.str()); check("void f()\n" // #8100 "{\n" " auto lambda = [](){return malloc(10);};\n" "}\n" "void x()\n" "{\n" " f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void *f() {\n" // #8848 " struct S { void *alloc() { return malloc(10); } };\n" "}\n" "void x()\n" "{\n" " f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void x()\n" "{\n" " if(!malloc(5)) fail();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str()); check("FOO* factory() {\n" " FOO* foo = new (std::nothrow) FOO;\n" " return foo;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #6536 check("struct S { S(int) {} };\n" "void foo(int i) {\n" " S socket(i);\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #6693 check("struct CTest {\n" " void Initialise();\n" " void malloc();\n" "};\n" "void CTest::Initialise() {\n" " malloc();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" // #7348 - cast " p = (::X*)malloc(42);\n" "}"); ASSERT_EQUALS("", errout.str()); // #7182 "crash: CheckMemoryLeak::functionReturnType()" check("template auto unary_right_comma (Ts... ts) { return (ts , ...); }\n" "template auto binary_left_comma (T x, Ts... ts) { return (x , ... , ts); }\n" "int main() {\n" " unary_right_comma (a);\n" "}"); ASSERT_EQUALS("", errout.str()); } void smartPointerFunctionParam() { check("void x() {\n" " f(shared_ptr(new int(42)), g());\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " h(12, f(shared_ptr(new int(42)), g()));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " f(unique_ptr(new int(42)), g());\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_unique() instead.\n", errout.str()); check("void x() {\n" " f(g(), shared_ptr(new int(42)));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " f(g(), unique_ptr(new int(42)));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_unique() instead.\n", errout.str()); check("void x() {\n" " f(shared_ptr(new char), make_unique(32));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If make_unique() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " f(g(124), h(\"test\", 234), shared_ptr(new char));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If h() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void x() {\n" " f(shared_ptr(new std::string(\"\")), g());\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout.str()); check("void g(int x) throw() { }\n" "void x() {\n" " f(g(124), shared_ptr(new char));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void __declspec(nothrow) g(int x) { }\n" "void x() {\n" " f(g(124), shared_ptr(new char));\n" "}"); ASSERT_EQUALS("", errout.str()); } void resourceLeak() { check("void foo() {\n" " fopen(\"file.txt\", \"r\");\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Return value of allocation function 'fopen' is not stored.\n", errout.str()); check("void foo() {\n" " FILE f* = fopen(\"file.txt\", \"r\");\n" " freopen(\"file.txt\", \"r\", f);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'freopen' is not stored.\n", errout.str()); check("void foo() {\n" " freopen(\"file.txt\", \"r\", stdin);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder h ( fopen(\"file.txt\", \"r\"));\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder ( fopen(\"file.txt\", \"r\"));\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder h { fopen(\"file.txt\", \"r\")};\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder h = fopen(\"file.txt\", \"r\");\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder { fopen(\"file.txt\", \"r\")};\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct Holder {\n" " Holder(int i, FILE* f) : file(f) {}\n" " ~Holder() { fclose(file); }\n" " FILE* file;\n" "};\n" "void foo() {\n" " Holder { 0, fopen(\"file.txt\", \"r\")};\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void getAllocationType() { // #7845 check("class Thing { Thing(); };\n" "Thing * makeThing() { Thing *thing = new Thing; return thing; }\n" "\n" "void f() {\n" " makeThing();\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestMemleakNoVar) cppcheck-1.90/test/testnullpointer.cpp000066400000000000000000003331041357737443600202100ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checknullpointer.h" #include "checkuninitvar.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" #include #include #include #include #include #include class TestNullPointer : public TestFixture { public: TestNullPointer() : TestFixture("TestNullPointer") { } private: Settings settings; void run() OVERRIDE { LOAD_LIB_2(settings.library, "std.cfg"); settings.addEnabled("warning"); TEST_CASE(nullpointerAfterLoop); TEST_CASE(nullpointer1); TEST_CASE(nullpointer2); TEST_CASE(structDerefAndCheck); // dereferencing struct and then checking if it's null TEST_CASE(pointerDerefAndCheck); TEST_CASE(nullpointer5); // References should not be checked TEST_CASE(nullpointerExecutionPaths); TEST_CASE(nullpointerExecutionPathsLoop); TEST_CASE(nullpointer7); TEST_CASE(nullpointer9); TEST_CASE(nullpointer10); TEST_CASE(nullpointer11); // ticket #2812 TEST_CASE(nullpointer12); // ticket #2470 TEST_CASE(nullpointer15); // #3560 (fp: return p ? f(*p) : f(0)) TEST_CASE(nullpointer16); // #3591 TEST_CASE(nullpointer17); // #3567 TEST_CASE(nullpointer18); // #1927 TEST_CASE(nullpointer19); // #3811 TEST_CASE(nullpointer20); // #3807 (fp: return p ? (p->x() || p->y()) : z) TEST_CASE(nullpointer21); // #4038 (fp: if (x) p=q; else return;) TEST_CASE(nullpointer23); // #4665 (false positive) TEST_CASE(nullpointer24); // #5082 fp: chained assignment TEST_CASE(nullpointer25); // #5061 TEST_CASE(nullpointer26); // #3589 TEST_CASE(nullpointer27); // #6568 TEST_CASE(nullpointer28); // #6491 TEST_CASE(nullpointer30); // #6392 TEST_CASE(nullpointer31); // #8482 TEST_CASE(nullpointer32); // #8460 TEST_CASE(nullpointer33); TEST_CASE(nullpointer34); TEST_CASE(nullpointer35); TEST_CASE(nullpointer36); // #9264 TEST_CASE(nullpointer37); // #9315 TEST_CASE(nullpointer38); TEST_CASE(nullpointer39); // #2153 TEST_CASE(nullpointer40); TEST_CASE(nullpointer41); TEST_CASE(nullpointer42); TEST_CASE(nullpointer43); // #9404 TEST_CASE(nullpointer44); // #9395, #9423 TEST_CASE(nullpointer45); TEST_CASE(nullpointer46); // #9441 TEST_CASE(nullpointer47); // #6850 TEST_CASE(nullpointer48); // #9196 TEST_CASE(nullpointer_addressOf); // address of TEST_CASE(nullpointerSwitch); // #2626 TEST_CASE(nullpointer_cast); // #4692 TEST_CASE(nullpointer_castToVoid); // #3771 TEST_CASE(nullpointer_subfunction); TEST_CASE(pointerCheckAndDeRef); // check if pointer is null and then dereference it TEST_CASE(nullConstantDereference); // Dereference NULL constant TEST_CASE(gcc_statement_expression); // Don't crash TEST_CASE(snprintf_with_zero_size); TEST_CASE(snprintf_with_non_zero_size); TEST_CASE(printf_with_invalid_va_argument); TEST_CASE(scanf_with_invalid_va_argument); TEST_CASE(nullpointer_in_return); TEST_CASE(nullpointer_in_typeid); TEST_CASE(nullpointer_in_for_loop); TEST_CASE(nullpointerDelete); TEST_CASE(nullpointerSubFunction); TEST_CASE(nullpointerExit); TEST_CASE(nullpointerStdString); TEST_CASE(nullpointerStdStream); TEST_CASE(nullpointerSmartPointer); TEST_CASE(functioncall); TEST_CASE(functioncalllibrary); // use Library to parse function call TEST_CASE(functioncallDefaultArguments); TEST_CASE(nullpointer_internal_error); // #5080 TEST_CASE(ticket6505); TEST_CASE(subtract); TEST_CASE(addNull); TEST_CASE(isPointerDeRefFunctionDecl); TEST_CASE(ctu); } void check(const char code[], bool inconclusive = false, const char filename[] = "test.cpp") { // Clear the error buffer.. errout.str(""); settings.inconclusive = inconclusive; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); if (!tokenizer.tokenize(istr, filename)) return; // Check for null pointer dereferences.. CheckNullPointer checkNullPointer; checkNullPointer.runChecks(&tokenizer, &settings, this); } void checkP(const char code[]) { // Clear the error buffer.. errout.str(""); settings.inconclusive = false; // Raw tokens.. std::vector files(1, "test.cpp"); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenizer.. Tokenizer tokenizer(&settings, this); tokenizer.createTokens(&tokens2); tokenizer.simplifyTokens1(""); // Check for null pointer dereferences.. CheckNullPointer checkNullPointer; checkNullPointer.runChecks(&tokenizer, &settings, this); } void nullpointerAfterLoop() { check("int foo(const Token *tok)\n" "{\n" " while (tok);\n" " tok = tok->next();\n" "}", true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning, inconclusive) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\n", errout.str()); // #2681 { const char code[] = "void foo(const Token *tok)\n" "{\n" " while (tok && tok->str() == \"=\")\n" " tok = tok->next();\n" "\n" " if (tok->str() != \";\")\n" " ;\n" "}\n"; check(code, false); // inconclusive=false => no error ASSERT_EQUALS("", errout.str()); check(code, true); // inconclusive=true => error ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning, inconclusive) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\n", errout.str()); } check("void foo()\n" "{\n" " for (const Token *tok = tokens; tok; tok = tok->next())\n" " {\n" " while (tok && tok->str() != \";\")\n" " tok = tok->next();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition 'while' is redundant or there is possible null pointer dereference: tok.\n", errout.str()); check("void foo(Token &tok)\n" "{\n" " for (int i = 0; i < tok.size(); i++ )\n" " {\n" " while (!tok)\n" " char c = tok.read();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " for (const Token *tok = tokens; tok; tok = tok->next())\n" " {\n" " while (tok && tok->str() != \";\")\n" " tok = tok->next();\n" " if( !tok ) break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " for (const Token *tok = tokens; tok; tok = tok ? tok->next() : NULL)\n" " {\n" " while (tok && tok->str() != \";\")\n" " tok = tok->next();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(A*a)\n" "{\n" " switch (a->b()) {\n" " case 1:\n" " while( a ){\n" " a = a->next;\n" " }\n" " break;\n" " case 2:\n" " a->b();\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // dereference in outer scope.. check("void foo(int x, const Token *tok) {\n" " if (x == 123) {\n" " while (tok) tok = tok->next();\n" " }\n" " tok->str();\n" "}\n"); TODO_ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning, inconclusive) Possible null pointer dereference: tok - otherwise it is redundant to check it against null.\n", "", errout.str()); check("int foo(const Token *tok)\n" "{\n" " while (tok){;}\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("int foo(const Token *tok)\n" "{\n" " while (tok){;}\n" " char a[2] = {0,0};\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer1() { // ticket #1923 - no false positive when using else if check("void f(A *a)\n" "{\n" " if (a->x == 1)\n" " {\n" " a = a->next;\n" " }\n" " else if (a->x == 2) { }\n" " if (a) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #2134 - sizeof doesn't dereference check("void f() {\n" " int c = 1;\n" " int *list = NULL;\n" " sizeof(*list);\n" " if (!list)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); // ticket #2245 - sizeof doesn't dereference check("void f(Bar *p) {\n" " if (!p) {\n" " int sz = sizeof(p->x);\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer2() { // Null pointer dereference can only happen with pointers check("void foo()\n" "{\n" " Fred fred;\n" " while (fred);\n" " fred.hello();\n" "}", true); ASSERT_EQUALS("", errout.str()); } // Dereferencing a struct and then checking if it is null // This is checked by this function: // CheckOther::nullPointerStructByDeRefAndChec void structDerefAndCheck() { // errors.. check("void foo(struct ABC *abc)\n" "{\n" " int a = abc->a;\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); check("void foo(struct ABC *abc) {\n" " bar(abc->a);\n" " bar(x, abc->a);\n" " bar(x, y, abc->a);\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n" "[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n" "[test.cpp:5] -> [test.cpp:4]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); check("void foo(ABC *abc) {\n" " if (abc->a == 3) {\n" " return;\n" " }\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition 'if(abc)' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); check("void f(ABC *abc) {\n" " if (abc->x == 0) {\n" " return;\n" " }\n" " if (!abc);\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // TODO: False negative if member of member is dereferenced check("void foo(ABC *abc) {\n" " abc->next->a = 0;\n" " if (abc->next)\n" " ;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\n", "", errout.str()); check("void foo(ABC *abc) {\n" " abc->a = 0;\n" " if (abc && abc->b == 0)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'if(abc&&abc->b==0)' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // ok dereferencing in a condition check("void foo(struct ABC *abc)\n" "{\n" " if (abc && abc->a);\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" " int x = abc && a(abc->x);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // ok to use a linked list.. check("void foo(struct ABC *abc)\n" "{\n" " abc = abc->next;\n" " if (!abc)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" " abc = (ABC *)(abc->_next);\n" " if (abc) { }" "}", true); ASSERT_EQUALS("", errout.str()); // reassign struct.. check("void foo(struct ABC *abc)\n" "{\n" " int a = abc->a;\n" " abc = abc->next;\n" " if (!abc)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(struct ABC *abc)\n" "{\n" " int a = abc->a;\n" " f(&abc);\n" " if (!abc)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); // goto.. check("void foo(struct ABC *abc)\n" "{\n" " int a;\n" " if (!abc)\n" " goto out;" " a = abc->a;\n" " return;\n" "out:\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); // loops.. check("void foo(struct ABC *abc)\n" "{\n" " int a = abc->a;" " do\n" " {\n" " if (abc)\n" " abc = abc->next;\n" " --a;\n" " }\n" " while (a > 0);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" " for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())\n" " {\n" " while (tok && tok->str() != \"{\")\n" " tok = tok->next();\n" " if (!tok)\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // dynamic_cast.. check("void foo(ABC *abc)\n" "{\n" " int a = abc->a;\n" " if (!dynamic_cast(abc))\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); // #2641 - global pointer, function call check("ABC *abc;\n" "void f() {\n" " abc->a = 0;\n" " do_stuff();\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("",errout.str()); check("Fred *fred;\n" "void f() {\n" " fred->foo();\n" " if (fred) { }\n" "}"); ASSERT_EQUALS("",errout.str()); // #2641 - local pointer, function call check("void f() {\n" " ABC *abc = abc1;\n" " abc->a = 0;\n" " do_stuff();\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition 'if(abc)' is redundant or there is possible null pointer dereference: abc.\n",errout.str()); // #2641 - local pointer, function call check("void f(ABC *abc) {\n" " abc->a = 0;\n" " do_stuff();\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition 'if(abc)' is redundant or there is possible null pointer dereference: abc.\n",errout.str()); // #2691 - switch/break check("void f(ABC *abc) {\n" " switch ( x ) {\n" " case 14:\n" " sprintf(buf, \"%d\", abc->a);\n" " break;\n" " case 15:\n" " if ( abc ) {}\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3128 check("void f(ABC *abc) {\n" " x(!abc || y(abc->a));\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(ABC *abc) {\n" " x(def || !abc || y(def, abc->a));\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(ABC *abc) {\n" " x(abc && y(def, abc->a));\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(ABC *abc) {\n" " x(def && abc && y(def, abc->a));\n" " if (abc) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #3228 - calling function with null object { const char code[] = "void f(Fred *fred) {\n" " fred->x();\n" " if (fred) { }\n" "}"; check(code); ASSERT_EQUALS("", errout.str()); check(code, true); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning, inconclusive) Either the condition 'if(fred)' is redundant or there is possible null pointer dereference: fred.\n", errout.str()); } // #3425 - false positives when there are macros checkP("#define IF if\n" "void f(struct FRED *fred) {\n" " fred->x = 0;\n" " IF(!fred){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " BUFFER *buffer = get_buffer();\n" " if (!buffer)\n" " uv_fatal_error();\n" " buffer->x = 11;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Dereferencing a pointer and then checking if it is null void pointerDerefAndCheck() { // errors.. check("void foo(int *p)\n" "{\n" " *p = 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(int *p)\n" "{\n" " *p = 0;\n" " if (p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'if(p)' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(int *p)\n" "{\n" " *p = 0;\n" " if (p || q) { }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'if(p||q)' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(int *p)\n" "{\n" " bar(*p);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p)\n" "{\n" " strcpy(p, \"abc\");\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p)\n" "{\n" " if (*p == 0) { }\n" " if (!p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // no error check("void foo()\n" "{\n" " int *p;\n" " f(&p);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int **p = f();\n" " if (!p)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(int *p)\n" "{\n" " if (x)\n" " p = 0;\n" " else\n" " *p = 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x)\n" "{\n" " int a = 2 * x;" " if (x == 0)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(int *p)\n" "{\n" " int var1 = p ? *p : 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p)\n" "{\n" " int var1 = x ? *p : 5;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // while check("void f(int *p) {\n" " *p = 0;\n" " while (p) { p = 0; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " *p = 0;\n" " while (p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'while(p)' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // Ticket #3125 check("void foo(ABC *p)\n" "{\n" " int var1 = p ? (p->a) : 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(ABC *p)\n" "{\n" " int var1 = p ? (1 + p->a) : 0;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * a=0;\n" " if (!a) {};\n" " int c = a ? 0 : 1;\n" "}\n",true); ASSERT_EQUALS("", errout.str()); // #3686 check("void f() {\n" " int * a=0;\n" " if (!a) {};\n" " int c = a ? b : b+1;\n" "}\n",true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int * a=0;\n" " if (!a) {};\n" " int c = (a) ? b : b+1;\n" "}\n",true); ASSERT_EQUALS("", errout.str()); check("void foo(P *p)\n" "{\n" " while (p)\n" " if (p->check())\n" " break;\n" " else\n" " p = p->next();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(Document *doc) {\n" " int x = doc && doc->x;\n" " if (!doc) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3128 - false positive check("void f(int *p) {\n" " assert(!p || (*p<=6));\n" " if (p) { *p = 0; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " assert(p && (*p<=6));\n" " if (p) { *p = 0; }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " *p = 12;\n" " assert(p && (*p<=6));\n" " if (p) { *p = 0; }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition 'if(p)' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(x *p)\n" "{\n" " p = p->next;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(x *p)\n" "{\n" " p = bar(p->next);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(x *p)\n" "{\n" " p = aa->bar(p->next);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(x *p)\n" "{\n" " p = *p2 = p->next;\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(struct ABC *abc)\n" "{\n" " abc = abc ? abc->next : 0;\n" " if (!abc)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" // #4523 " abc = (*abc).next;\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" // #4523 " abc = (*abc->ptr);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(Item *item) {\n" " x = item ? ab(item->x) : 0;\n" " if (item) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(Item *item) {\n" " item->x = 0;\n" " a = b ? c : d;\n" " if (item) { }\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition 'if(item)' is redundant or there is possible null pointer dereference: item.\n", errout.str()); check("BOOL GotoFlyAnchor()\n" // #2243 "{\n" " const SwFrm* pFrm = GetCurrFrm();\n" " do {\n" " pFrm = pFrm->GetUpper();\n" " } while( pFrm && !pFrm->IsFlyFrm() );\n" "\n" " if( !pFrm )\n" " return FALSE;\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #2463 check("struct A\n" "{\n" " B* W;\n" "\n" " void f() {\n" " switch (InData) {\n" " case 2:\n" " if (!W) return;\n" " W->foo();\n" " break;\n" " case 3:\n" " f();\n" " if (!W) return;\n" " break;\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #2525 - sizeof check("void f() {\n" " int *test = NULL;\n" " int c = sizeof(test[0]);\n" " if (!test)\n" " ;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(type* p) {\n" // #4983 " x(sizeof p[0]);\n" " if (!p)\n" " ;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3023 - checked deref check("void f(struct ABC *abc) {\n" " WARN_ON(!abc || abc->x == 0);\n" " if (!abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct ABC *abc) {\n" " WARN_ON(!abc || abc->x == 7);\n" " if (!abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3425 - false positives when there are macros checkP("#define IF if\n" "void f(int *p) {\n" " *p = 0;\n" " IF(!p){}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #3914 - false positive " int *p;\n" " ((p=ret()) && (x=*p));\n" " if (p);\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer5() { // errors.. check("void foo(A &a)\n" "{\n" " char c = a.c();\n" " if (!a)\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } // Execution paths.. void nullpointerExecutionPaths() { // errors.. check("static void foo()\n" "{\n" " Foo *p = 0;\n" " if (a == 1) {\n" " p = new FooBar;\n" " } else { if (a == 2) {\n" " p = new FooCar; } }\n" " p->abcd();\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Possible null pointer dereference: p\n", "", errout.str()); check("static void foo() {\n" " int &r = *0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("static void foo(int x) {\n" " int y = 5 + *0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); { const char code[] = "static void foo() {\n" " Foo *abc = 0;\n" " abc->a();\n" "}\n"; // inconclusive=false => no error check(code,false); ASSERT_EQUALS("", errout.str()); // inconclusive=true => error check(code, true); ASSERT_EQUALS("[test.cpp:3]: (error, inconclusive) Null pointer dereference: abc\n", errout.str()); } check("static void foo() {\n" " std::cout << *0;" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f()\n" "{\n" " char *c = 0;\n" " {\n" " delete c;\n" " }\n" " c[0] = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (error) Null pointer dereference: c\n", errout.str()); check("static void foo() {\n" " if (3 > *0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); // no false positive.. check("static void foo()\n" "{\n" " Foo *p = 0;\n" " p = new Foo;\n" " p->abcd();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int sz = sizeof((*(struct dummy *)0).x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void get_offset(long &offset)\n" "{\n" " mystruct * temp; temp = 0;\n" " offset = (long)(&(temp->z));\n" "}"); ASSERT_EQUALS("", errout.str()); // Ticket #1893 - try/catch inside else check("int *test(int *Z)\n" "{\n" " int *Q=NULL;\n" " if (Z) {\n" " Q = Z;\n" " }\n" " else {\n" " Z = new int;\n" " try {\n" " } catch(...) {\n" " }\n" " Q = Z;\n" " }\n" " *Q=1;\n" " return Q;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int *test(int *Z)\n" "{\n" " int *Q=NULL;\n" " if (Z) {\n" " Q = Z;\n" " }\n" " else {\n" " try {\n" " } catch(...) {\n" " }\n" " }\n" " *Q=1;\n" " return Q;\n" "}"); ASSERT_EQUALS("[test.cpp:12]: (warning) Possible null pointer dereference: Q\n", errout.str()); // Ticket #2052 (false positive for 'else continue;') check("void f() {\n" " for (int x = 0; x < 5; ++x) {" " int *p = 0;\n" " if (a(x)) p=b(x);\n" " else continue;\n" " *p = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // function pointer.. check("void foo()\n" "{\n" " void (*f)();\n" " f = 0;\n" " f();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f\n", errout.str()); // loops.. check("void f() {\n" " int *p = 0;\n" " for (int i = 0; i < 10; ++i) {\n" " int x = *p + 1;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("void f(int a) {\n" " const char *p = 0;\n" " if (a) {\n" " p = \"abcd\";\n" " }\n" " for (int i = 0; i < 3; i++) {\n" " if (a && (p[i] == '1'));\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); // ticket #2251: taking the address of member check("void f() {\n" " Fred *fred = 0;\n" " int x = &fred->x;\n" "}", true); ASSERT_EQUALS("", errout.str()); // ticket #3220: calling member function check("void f() {\n" " Fred *fred = NULL;\n" " fred->do_something();\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #3570 - parsing of conditions { check("void f() {\n" " int *p = NULL;\n" " if (x)\n" " p = q;\n" " if (p && *p) { }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *p = NULL;\n" " if (x)\n" " p = q;\n" " if (!p || *p) { }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int *p = NULL;\n" " if (x)\n" " p = q;\n" " if (p || *p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); } // ticket #8831 - FP triggered by if/return/else sequence { check("void f(int *p, int *q) {\n" " if (p == NULL)\n" " return;\n" " else if (q == NULL)\n" " return;\n" " *q = 0;\n" "}\n" "\n" "void g() {\n" " f(NULL, NULL);\n" "}", true); ASSERT_EQUALS("", errout.str()); } } // Ticket #2350 void nullpointerExecutionPathsLoop() { // No false positive: check("void foo() {\n" " int n;\n" " int *argv32 = p;\n" " if (x) {\n" " n = 0;\n" " argv32 = 0;\n" " }\n" "\n" " for (int i = 0; i < n; i++) {\n" " argv32[i] = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // No false negative: check("void foo() {\n" " int n;\n" " int *argv32;\n" " if (x) {\n" " n = 10;\n" " argv32 = 0;\n" " }\n" "\n" " for (int i = 0; i < n; i++) {\n" " argv32[i] = 0;\n" " }\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); // #2231 - error if assignment in loop is not used check("void f() {\n" " char *p = 0;\n" "\n" " for (int x = 0; x < 3; ++x) {\n" " if (y[x] == 0) {\n" " p = malloc(10);\n" " break;\n" " }\n" " }\n" "\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (warning) Possible null pointer dereference: p\n", errout.str()); } void nullpointer7() { check("void foo()\n" "{\n" " wxLongLong x = 0;\n" " int y = x.GetValue();\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer9() { //#ticket 1778 check("void foo()\n" "{\n" " std::string * x = 0;\n" " *x = \"test\";\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: x\n", errout.str()); } void nullpointer10() { check("void foo()\n" "{\n" " struct my_type* p = 0;\n" " p->x = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); } void nullpointer11() { // ticket #2812 check("int foo()\n" "{\n" " struct my_type* p;\n" " p = 0;\n" " return p->x;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: p\n", errout.str()); } void nullpointer12() { // ticket #2470, #4035 const char code[] = "int foo()\n" "{\n" " int* i = nullptr;\n" " return *i;\n" "}\n"; check(code, false, "test.cpp"); // C++ file => nullptr means NULL ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: i\n", errout.str()); check(code, false, "test.c"); // C file => nullptr does not mean NULL ASSERT_EQUALS("", errout.str()); } void nullpointer15() { // #3560 check("void f() {\n" " char *p = 0;\n" " if (x) p = \"abcd\";\n" " return p ? f(*p) : f(0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer16() { // #3591 check("void foo() {\n" " int *p = 0;\n" " bar(&p);\n" " *p = 0;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer17() { // #3567 check("int foo() {\n" " int *p = 0;\n" " if (x) { return 0; }\n" " return !p || *p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " int *p = 0;\n" " if (x) { return 0; }\n" " return p && *p;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer18() { // #1927 check("void f ()\n" "{\n" " int i=0;\n" " char *str=NULL;\n" " while (str[i])\n" " {\n" " i++;\n" " };\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: str\n", errout.str()); } void nullpointer19() { // #3811 check("int foo() {\n" " perror(0);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer20() { // #3807 check("void f(int x) {\n" " struct xy *p = 0;\n" " if (x) p = q;\n" " if (p ? p->x || p->y : 0) { }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" // false negative " struct xy *p = 0;\n" " if (x) p = q;\n" " if (y ? p->x : p->y) { }\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Possible null pointer dereference: p\n", "", errout.str()); } void nullpointer21() { // #4038 - fp: if (x) p=q; else return; check("void f(int x) {\n" " int *p = 0;\n" " if (x) p = q;\n" " else return;\n" " *p = 0;\n" // <- p is not NULL "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer23() { // #4665 check("void f(){\n" " char *c = NULL;\n" " char cBuf[10];\n" " sprintf(cBuf, \"%s\", c ? c : \"0\" );\n" "}"); ASSERT_EQUALS("",errout.str()); } void nullpointer24() { // #5083 - fp: chained assignment check("void f(){\n" " char *c = NULL;\n" " x = c = new char[10];\n" " *c = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer25() { // #5061 check("void f(int *data, int i)\n" "{\n" " int *array = NULL;\n" " if (data == 1 && array[i] == 0)\n" " std::cout << \"test\";\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: array\n", errout.str()); } void nullpointer26() { // #3589 check("double foo() {\n" " sk *t1 = foo();\n" " sk *t2 = foo();\n" " if ((!t1) && (!t2))\n" " return 0.0;\n" " if (t1 && (!t2))\n" " return t1->Inter();\n" " if (t2->GetT() == t)\n" " return t2->Inter();\n" " if (t2 && (!t1))\n" " return 0.0;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer27() { // #6568 check("template\n" "class Foo {\n" " Foo& operator = ( Type* );\n" "};\n" "template\n" "Foo& Foo::operator = ( Type* pointer_ ) {\n" " pointer_=NULL;\n" " *pointer_=0;\n" " return *this;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (error) Null pointer dereference: pointer_\n", errout.str()); } void nullpointer28() { // #6491 check("typedef struct { int value; } S;\n" "int f(const S *s) { \n" " int i = s ? s->value + 1 \n" " : s->value - 1; // <-- null ptr dereference \n" " return i;\n" "}\n"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Possible null pointer dereference: s\n", "", errout.str()); } void nullpointer30() { // #6392 check("void f(std::vector *values)\n" "{\n" " values->clear();\n" " if (values) \n" " {\n" " for (int i = 0; i < values->size(); ++i)\n" " {\n" " values->push_back(\"test\");\n" " }\n" " }\n" "}\n", true); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning, inconclusive) Either the condition 'if(values)' is redundant or there is possible null pointer dereference: values.\n", errout.str()); } void nullpointer31() { // #8482 check("struct F\n" "{\n" " int x;\n" "};\n" " \n" "static void foo(F* f)\n" "{\n" " if( f ) {}\n" " else { return; }\n" " (void)f->x;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer32() { // #8460 check("int f(int * ptr) {\n" " if(ptr)\n" " { return 0;}\n" " else{\n" " int *p1 = ptr;\n" " return *p1;\n" " }\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition 'ptr' is redundant or there is possible null pointer dereference: p1.\n", errout.str()); } void nullpointer33() { check("void f(int * x) {\n" " if (x != nullptr)\n" " *x = 2;\n" " else\n" " *x = 3;\n" "}\n", true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'x!=nullptr' is redundant or there is possible null pointer dereference: x.\n", errout.str()); } void nullpointer34() { check("void g() {\n" " throw "";\n" "}\n" "bool f(int * x) {\n" " if (x) *x += 1;\n" " if (!x) g();\n" " return *x;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer35() { check("bool f(int*);\n" "void g(int* x) {\n" " if (f(x)) {\n" " *x = 1;\n" " }\n" "}\n" "void h() {\n" " g(0);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer36() { check("char* f(char* s) {\n" " char* start = s;\n" " if (!s)\n" " return (s);\n" " while (isspace(*start))\n" " start++;\n" " return (start);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer37() { check("void f(int value, char *string) {\n" " char *ptr1 = NULL, *ptr2 = NULL;\n" " unsigned long count = 0;\n" " if(!string)\n" " return;\n" " ptr1 = string;\n" " ptr2 = strrchr(string, 'a');\n" " if(ptr2 == NULL)\n" " return;\n" " while(ptr1 < ptr2) {\n" " count++;\n" " ptr1++;\n" " }\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer38() { check("void f(int * x) {\n" " std::vector v;\n" " if (x) {\n" " v.push_back(x);\n" " *x;\n" " }\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointer39() { check("struct A { int * x; };\n" "void f(struct A *a) {\n" " if (a->x == NULL) {}\n" " *(a->x);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->x==NULL' is redundant or there is possible null pointer dereference: a->x.\n", errout.str()); } void nullpointer40() { check("struct A { std::unique_ptr x; };\n" "void f(struct A *a) {\n" " if (a->x == nullptr) {}\n" " *(a->x);\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->x==nullptr' is redundant or there is possible null pointer dereference: a->x.\n", errout.str()); } void nullpointer41() { check("struct A { int * g() const; };\n" "void f(struct A *a) {\n" " if (a->g() == nullptr) {}\n" " *(a->g());\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->g()==nullptr' is redundant or there is possible null pointer dereference: a->g().\n", errout.str()); check("struct A { int * g(); };\n" "void f(struct A *a) {\n" " if (a->g() == nullptr) {}\n" " *(a->g());\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer42() { check("struct A { std::unique_ptr g() const; };\n" "void f(struct A *a) {\n" " if (a->g() == nullptr) {}\n" " *(a->g());\n" "}\n"); ASSERT_EQUALS( "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->g()==nullptr' is redundant or there is possible null pointer dereference: a->g().\n", errout.str()); } void nullpointer43() { check("struct A { int* x; };\n" "void f(A* a) {\n" " int * x = a->x;\n" " if (x) {\n" " (void)*a->x;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer44() { // #9395 check("int foo( ) {\n" " const B* b = getB();\n" " const double w = ( nullptr != b) ? 42. : 0.0;\n" " if ( w == 0.0 )\n" " return 0;\n" " return b->get();\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9423 check("extern F* GetF();\n" "extern L* GetL();\n" "void Foo() {\n" " const F* const fPtr = GetF();\n" " const bool fPtrOk = fPtr != NULL;\n" " assert(fPtrOk);\n" " if (!fPtrOk)\n" " return;\n" " L* const lPtr = fPtr->l;\n" " const bool lPtrOk = lPtr != NULL;\n" " assert(lPtrOk);\n" " if (!lPtrOk)\n" " return;\n" " lPtr->Clear();\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer45() { check("struct a {\n" " a *b() const;\n" "};\n" "void g() { throw 0; }\n" "a h(a * c) {\n" " if (c && c->b()) {}\n" " if (!c)\n" " g();\n" " if (!c->b())\n" " g();\n" " a d = *c->b();\n" " return d;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct a {\n" " a *b() const;\n" "};\n" "void e() { throw 0; }\n" "a f() {\n" " a *c = 0;\n" " if (0 && c->b()) {}\n" " if (!c)\n" " e();\n" " if (!c->b())\n" " e();\n" " a d = *c->b();\n" " return d;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer46() { check("void f() {\n" " char* p = new(std::nothrow) char[1];\n" " if( p ) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer47() { check("void f(int *p) {\n" " if(!p[0]) {}\n" " const int *const a = p;\n" " if(!a){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition '!a' is redundant or there is possible null pointer dereference: p.\n", errout.str()); } void nullpointer48() { check("template\n" "auto f(T& x) -> decltype(x);\n" "int& g(int* x) {\n" " return f(*x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointer_addressOf() { // address of check("void f() {\n" " struct X *x = 0;\n" " if (addr == &x->y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " struct X *x = 0;\n" " if (addr == &x->y.z[0]) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointerSwitch() { // #2626 check("char *f(int x) {\n" " char *p = do_something();\n" " switch (x) {\n" " case 1:\n" " p = 0;\n" " case 2:\n" " *p = 0;\n" " break;\n" " }\n" " return p;\n" "}", true); ASSERT_EQUALS("[test.cpp:7]: (warning) Possible null pointer dereference: p\n", errout.str()); } void nullpointer_cast() { // #4692 check("char *nasm_skip_spaces(const char *p) {\n" " if (p)\n" " while (*p && nasm_isspace(*p))\n" " p++;\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointer_castToVoid() { // #3771 check("void f () {\n" " int *buf; buf = NULL;\n" " buf;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer_subfunction() { check("int f(int* x, int* y) {\n" " if (!x)\n" " return;\n" " return *x + *y;\n" "}\n" "void g() {\n" " f(nullptr, nullptr);\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } // Check if pointer is null and the dereference it void pointerCheckAndDeRef() { check("void foo(char *p) {\n" " if (!p) {\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p) {\n" " if (p && *p == 0) {\n" " }\n" " printf(\"%c\", *p);\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p) {\n" " if (p && *p == 0) {\n" " } else { *p = 0; }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p) {\n" " if (p) {\n" " }\n" " strcpy(p, \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(char *p) {\n" " if (p) {\n" " }\n" " bar();\n" " strcpy(p, \"abc\");\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void foo(abc *p) {\n" " if (!p) {\n" " }\n" " else { if (!p->x) {\n" " } }\n" "}"); ASSERT_EQUALS("", errout.str()); { static const char code[] = "void foo(char *p) {\n" " if (!p) {\n" " abort();\n" " }\n" " *p = 0;\n" "}"; check(code, false); ASSERT_EQUALS("", errout.str()); check(code, true); ASSERT_EQUALS("", errout.str()); } check("void foo(char *p) {\n" " if (!p) {\n" " (*bail)();\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " if (!p) {\n" " throw x;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " if (!p) {\n" " ab.abort();\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " if (!p) {\n" " switch (x) { }\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " if (!p) {\n" " }\n" " return *x;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int foo(int *p) {\n" " if (!p) {\n" " x = *p;\n" " return 5+*p;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // operator! check("void f() {\n" " A a;\n" " if (!a) {\n" " a.x();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // This is why this check can't be used on the simplified token list check("void f(Foo *foo) {\n" " if (!dynamic_cast(foo)) {\n" " *foo = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket: #2300 - calling unknown function that may initialize the pointer check("Fred *fred;\n" "void a() {\n" " if (!fred) {\n" " initfred();\n" " fred->x = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #1219 check("void foo(char *p) {\n" " if (p) {\n" " return;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // #2467 - unknown macro may terminate the application check("void f(Fred *fred) {\n" " if (fred == NULL) {\n" " MACRO;\n" " }\n" " fred->a();\n" "}"); ASSERT_EQUALS("", errout.str()); // #2493 - switch check("void f(Fred *fred) {\n" " if (fred == NULL) {\n" " x = 0;\n" " }\n" " switch (x) {\n" " case 1:\n" " fred->a();\n" " break;\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); // #4118 - second if check("void f(char *p) {\n" " int x = 1;\n" " if (!p) x = 0;\n" " if (x) *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #2674 - different functions check("class Fred {\n" "public:\n" " Wilma *wilma;\n" " void a();\n" " void b();\n" "};\n" "\n" "void Fred::a() {\n" " if ( wilma ) { }\n" "}\n" "\n" "void Fred::b() {\n" " wilma->Reload();\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void test(int *i) {\n" " if(i == NULL) { }\n" " else {\n" " int b = *i;\n" " }\n" "}", true); ASSERT_EQUALS("", errout.str()); // #2696 - false positives nr 1 check("void f()\n" "{\n" " struct foo *pFoo = NULL;\n" " size_t len;\n" "\n" " len = sizeof(*pFoo) - sizeof(pFoo->data);\n" "\n" " if (pFoo)\n" " bar();\n" "}", true); ASSERT_EQUALS("", errout.str()); // #2696 - false positives nr 2 check("void f()\n" "{\n" " struct foo *pFoo = NULL;\n" " size_t len;\n" "\n" " while (pFoo)\n" " pFoo = pFoo->next;\n" "\n" " len = sizeof(pFoo->data);\n" "}", true); ASSERT_EQUALS("", errout.str()); // #2696 - false positives nr 3 check("void f()\n" "{\n" " struct foo *pFoo = NULL;\n" " size_t len;\n" "\n" " while (pFoo)\n" " pFoo = pFoo->next;\n" "\n" " len = decltype(*pFoo);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int foo(struct Fred *fred) {\n" " if (fred) { }\n" " return fred->a;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'fred' is redundant or there is possible null pointer dereference: fred.\n", errout.str()); // #2789 - assign and check pointer check("void f() {\n" " char *p; p = x();\n" " if (!p) { }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // check, assign and use check("void f() {\n" " char *p;\n" " if (p == 0 && (p = malloc(10)) != 0) {\n" " *p = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // check, assign and use check("void f() {\n" " char *p;\n" " if (p == 0 && (p = malloc(10)) != a && (*p = a)) {\n" " *p = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // check, and use check("void f() {\n" " char *p;\n" " if (p == 0 && (*p = 0)) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // check, and use check("void f() {\n" " struct foo *p;\n" " if (p == 0 && p->x == 10) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // check, and use check("void f() {\n" " struct foo *p;\n" " if (p == 0 || p->x == 10) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // check, and use check("void f() {\n" " char *p; p = malloc(10);\n" " if (p == NULL && (*p = a)) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==NULL' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // check, and use check("void f(struct X *p, int x) {\n" " if (!p && x==1 || p && p->x==0) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); { const char code[] = "void f(Fred *fred) {\n" " if (fred == NULL) { }\n" " fred->x();\n" "}"; check(code, false); // non-inconclusive ASSERT_EQUALS("", errout.str()); check(code, true); // inconclusive ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning, inconclusive) Either the condition 'fred==NULL' is redundant or there is possible null pointer dereference: fred.\n", errout.str()); } check("void f(char *s) {\n" // #3358 " if (s==0);\n" " strcpy(a, s?b:c);\n" "}"); ASSERT_EQUALS("", errout.str()); // sizeof check("void f(struct fred_t *fred) {\n" " if (!fred)\n" " int sz = sizeof(fred->x);\n" "}", true); ASSERT_EQUALS("", errout.str()); // check in macro check("void f(int *x) {\n" " $if (!x) {}\n" " *x = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // return ?: check("int f(ABC *p) {\n" // FP : return ?: " if (!p) {}\n" " return p ? p->x : 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(ABC *p) {\n" // no fn " if (!p) {}\n" " return q ? p->x : 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("int f(ABC *p) {\n" // FP : return && " if (!p) {}\n" " return p && p->x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int *p) {\n" " if (x || !p) {}\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // sizeof check("void f() {\n" " int *pointer = NULL;\n" " pointer = func(sizeof pointer[0]);\n" "}"); ASSERT_EQUALS("", errout.str()); } // Test CheckNullPointer::nullConstantDereference void nullConstantDereference() { check("void f() {\n" " int* p = 0;\n" " return p[4];\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: p\n", errout.str()); check("void f() {\n" " typeof(*NULL) y;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("int * f() {\n" " return NULL;\n" "}\n" "int main() {\n" " return *f();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f()\n", errout.str()); } void gcc_statement_expression() { // Ticket #2621 check("void f(struct ABC *abc) {\n" " ({ if (abc) dbg(); })\n" "}"); ASSERT_EQUALS("", errout.str()); } void snprintf_with_zero_size() { // Ticket #2840 check("void f() {\n" " int bytes = snprintf(0, 0, \"%u\", 1);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void snprintf_with_non_zero_size() { // Ticket #2840 check("void f() {\n" " int bytes = snprintf(0, 10, \"%u\", 1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); } void printf_with_invalid_va_argument() { check("void f() {\n" " printf(\"%s\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* s) {\n" " printf(\"%s\", s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char* s = 0;\n" " printf(\"%s\", s);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: s\n", errout.str()); check("void f() {\n" " char *s = 0;\n" " printf(\"%s\", s == 0 ? a : s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " printf(\"%u%s\", 0, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* s) {\n" " printf(\"%u%s\", 0, s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char* s = 0;\n" " printf(\"%u%s\", 123, s);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: s\n", errout.str()); check("void f() {\n" " printf(\"%%%s%%\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* s) {\n" " printf(\"text: %s, %s\", s, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f() {\n" " char* s = \"blabla\";\n" " printf(\"%s\", s);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char* s) {\n" " printf(\"text: %m%s, %s\", s, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* s) {\n" " printf(\"text: %*s, %s\", s, 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); // Ticket #3364 check("void f() {\n" " printf(\"%-*.*s\", s, 0);\n" " sprintf(\"%*\", s);\n" "}"); ASSERT_EQUALS("", errout.str()); } void scanf_with_invalid_va_argument() { check("void f(char* s) {\n" " sscanf(s, \"%s\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f() {\n" " scanf(\"%d\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); check("void f(char* foo) {\n" " char location[200];\n" " int width, height;\n" " sscanf(imgInfo, \"%s %d %d\", location, &width, &height);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #3207 check("void f(char *dummy) {\n" " int iVal;\n" " sscanf(dummy, \"%d%c\", &iVal);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #3211 check("void f(char *dummy) {\n" " int* iVal = 0;\n" " sscanf(dummy, \"%d\", iVal);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: iVal\n", errout.str()); check("void f(char *dummy) {\n" " int* iVal;\n" " sscanf(dummy, \"%d\", foo(iVal));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char *dummy) {\n" " int* iVal = 0;\n" " sscanf(dummy, \"%d%d\", foo(iVal), iVal);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char* dummy) {\n" " sscanf(dummy, \"%*d%u\", 0);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); } void nullpointer_in_return() { check("int foo() {\n" " int* iVal = 0;\n" " if(g()) iVal = g();\n" " return iVal[0];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Possible null pointer dereference: iVal\n", errout.str()); check("int foo(int* iVal) {\n" " return iVal[0];\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer_in_typeid() { // Should throw std::bad_typeid check("struct PolymorphicA { virtual ~A() {} };\n" "bool foo() {\n" " PolymorphicA* a = 0;\n" " return typeid(*a) == typeid(*a);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("struct NonPolymorphicA { ~A() {} };\n" "bool foo() {\n" " NonPolymorphicA* a = 0;\n" " return typeid(*a) == typeid(*a);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("bool foo() {\n" " char* c = 0;\n" " return typeid(*c) == typeid(*c);\n" "}", true); ASSERT_EQUALS("", errout.str()); } void nullpointer_in_for_loop() { // Ticket #3278 check("void f(int* ptr, int cnt){\n" " if (!ptr)\n" " cnt = 0;\n" " for (int i = 0; i < cnt; ++i)\n" " *ptr++ = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void nullpointerDelete() { check("void f() {\n" " K *k = getK();\n" " if (k)\n" " k->doStuff();\n" " delete k;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " K *k = getK();\n" " if (k)\n" " k[0] = ptr;\n" " delete [] k;\n" " k = new K[10];\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointerSubFunction() { check("void g(int* x) { *x; }\n" "void f(int* x) {\n" " if (x)\n" " g(x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nullpointerExit() { check("void f() {\n" " K *k = getK();\n" " if (!k)\n" " exit(1);\n" " k->f();\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointerStdString() { check("void f(std::string s1) {\n" " void* p = 0;\n" " s1 = 0;\n" " s1 = '\\0';\n" " std::string s2 = 0;\n" " std::string s2 = '\\0';\n" " std::string s3(0);\n" " foo(std::string(0));\n" " s1 = p;\n" " std::string s4 = p;\n" " std::string s5(p);\n" " foo(std::string(p));\n" "}", true); ASSERT_EQUALS("[test.cpp:9]: (error) Null pointer dereference: p\n" "[test.cpp:10]: (error) Null pointer dereference: p\n" "[test.cpp:11]: (error) Null pointer dereference: p\n" "[test.cpp:12]: (warning, inconclusive) Possible null pointer dereference: p\n" "[test.cpp:3]: (error) Null pointer dereference\n" "[test.cpp:5]: (error) Null pointer dereference\n" "[test.cpp:7]: (error) Null pointer dereference\n" "[test.cpp:8]: (error) Null pointer dereference\n" , errout.str()); check("void f(std::string s1) {\n" " s1 = nullptr;\n" " std::string s2 = nullptr;\n" " std::string s3(nullptr);\n" " foo(std::string(nullptr));\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n" "[test.cpp:3]: (error) Null pointer dereference\n" "[test.cpp:4]: (error) Null pointer dereference\n" "[test.cpp:5]: (error) Null pointer dereference\n" , errout.str()); check("void f(std::string s1) {\n" " s1 = NULL;\n" " std::string s2 = NULL;\n" " std::string s3(NULL);\n" " foo(std::string(NULL));\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n" "[test.cpp:3]: (error) Null pointer dereference\n" "[test.cpp:4]: (error) Null pointer dereference\n" "[test.cpp:5]: (error) Null pointer dereference\n" , errout.str()); check("void f(std::string s1, const std::string& s2, const std::string* s3) {\n" " void* p = 0;\n" " if (x) { return; }\n" " foo(s1 == p);\n" " foo(s2 == p);\n" " foo(s3 == p);\n" " foo(p == s1);\n" " foo(p == s2);\n" " foo(p == s3);\n" "}", true); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n" "[test.cpp:5]: (error) Null pointer dereference: p\n" "[test.cpp:7]: (error) Null pointer dereference: p\n" "[test.cpp:8]: (error) Null pointer dereference: p\n", errout.str()); check("void f(std::string s1, const std::string& s2, const std::string* s3) {\n" " void* p = 0;\n" " if (x) { return; }\n" " foo(0 == s1.size());\n" " foo(0 == s2.size());\n" " foo(0 == s3->size());\n" " foo(s1.size() == 0);\n" " foo(s2.size() == 0);\n" " foo(s3->size() == 0);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(std::string s1, const std::string& s2) {\n" " if (x) { return; }\n" " foo(0 == s1[0]);\n" " foo(0 == s2[0]);\n" " foo(s1[0] == 0);\n" " foo(s2[0] == 0);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(std::string s1, const std::string& s2) {\n" " if (x) { return; }\n" " foo(s1 == '\\0');\n" " foo(s2 == '\\0');\n" " foo('\\0' == s1);\n" " foo('\\0' == s2);\n" "}", true); ASSERT_EQUALS("", errout.str()); check("class Bar {\n" " std::string s;\n" " Bar() : s(0) {}\n" "};\n" "class Foo {\n" " std::string s;\n" " Foo();\n" "};\n" "Foo::Foo() : s(0) {}"); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference\n" "[test.cpp:9]: (error) Null pointer dereference\n", errout.str()); check("void f() {\n" " std::string s = 0 == x ? \"a\" : \"b\";\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const std::string s = g();\n" " ASSERT_MESSAGE(\"Error on s\", 0 == s.compare(\"Some text\"));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int i, std::string s);\n" "void bar() {\n" " foo(0, \"\");\n" " foo(0, 0);\n" " foo(var, 0);\n" " foo(var, NULL);\n" " foo(var, nullptr);\n" " foo(0, var);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference\n" "[test.cpp:5]: (error) Null pointer dereference\n" "[test.cpp:6]: (error) Null pointer dereference\n" "[test.cpp:7]: (error) Null pointer dereference\n", errout.str()); } void nullpointerStdStream() { check("void f(std::ifstream& is) {\n" " char* p = 0;\n" " is >> p;\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: p\n", "", errout.str()); check("void f(const std::ostringstream& oss, char* q) {\n" " char const* p = 0;\n" // Simplification makes detection of bug difficult " oss << p;\n" " oss << foo << p;\n" " if(q == 0)\n" " oss << foo << q;\n" "}", false); ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: p\n" "[test.cpp:4]: (error) Null pointer dereference: p\n" "[test.cpp:5] -> [test.cpp:6]: (warning) Either the condition 'q==0' is redundant or there is possible null pointer dereference: q.\n", errout.str()); check("void f(const char* p) {\n" " if(p == 0) {\n" " std::cout << p;\n" " std::cerr << p;\n" " std::cin >> p;\n" " std::cout << abc << p;\n" " }\n" "}", false); TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", "[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("void f() {\n" " void* p1 = 0;\n" " std::cout << p1;\n" // No char* " char* p2 = 0;\n" " std::cin >> (int)p;\n" // result casted " std::cout << (int)p;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(const std::string& str) {\n" " long long ret = 0;\n" " std::istringstream istr(str);\n" " istr >> std::hex >> ret;\n" // Read integer " return ret;\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f(int* i) {\n" " if(i) return;\n" " std::cout << i;\n" // Its no char* (#4240) "}", true); ASSERT_EQUALS("", errout.str()); // #5811 false positive: (error) Null pointer dereference check("using namespace std;\n" "std::string itoip(int ip) {\n" " stringstream out;\n" " out << ((ip >> 0) & 0xFF);\n" " return out.str();\n" "}n", true); ASSERT_EQUALS("", errout.str()); // avoid regression from first fix attempt for #5811... check("void deserialize(const std::string &data) {\n" "std::istringstream iss(data);\n" "unsigned int len = 0;\n" "if (!(iss >> len))\n" " return;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void nullpointerSmartPointer() { check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " if (p) {}\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " p = nullptr;\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::unique_ptr p) {\n" " if (p) {}\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::unique_ptr p) {\n" " p = nullptr;\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f() {\n" " std::shared_ptr p;\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " p.reset();\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " Fred * pp = nullptr;\n" " p.reset(pp);\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f(Fred& f) {\n" " std::shared_ptr p;\n" " p.reset(&f);\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct Fred { int x; };\n" "void f(std::shared_ptr p) {\n" " p.release();\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct Fred { int x; };\n" "void f() {\n" " std::shared_ptr p(nullptr);\n" " dostuff(p->x);\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout.str()); check("struct A {};\n" "void f(int n) {\n" " std::unique_ptr p;\n" " p.reset(new const A*[n]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9216 check("struct A {\n" " void reset();\n" " void f();\n" "};\n" "void g(std::unique_ptr var) {\n" " var->reset();\n" " var->f();\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #9439 check("char* g();\n" "char* f() {\n" " std::unique_ptr x(g());\n" " if( x ) {}\n" " return x.release();\n" "}\n", true); ASSERT_EQUALS("", errout.str()); } void functioncall() { // #3443 - function calls // dereference pointer and then check if it's null { // function not seen check("void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // function seen (taking pointer parameter) check("void foo(int *p) { }\n" "\n" "void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'if(p)' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // function seen (taking reference parameter) check("void foo(int *&p) { }\n" "\n" "void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}", true); ASSERT_EQUALS("", errout.str()); // function implementation not seen check("void foo(int *p);\n" "\n" "void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'if(p)' is redundant or there is possible null pointer dereference: p.\n", errout.str()); // inconclusive check("void f(int *p) {\n" " *p = 0;\n" " foo(p);\n" " if (p) { }\n" "}", true); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning, inconclusive) Either the condition 'if(p)' is redundant or there is possible null pointer dereference: p.\n", errout.str()); } // dereference struct pointer and then check if it's null { // function not seen check("void f(struct ABC *abc) {\n" " abc->a = 0;\n" " foo(abc);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("", errout.str()); // function seen (taking pointer parameter) check("void foo(struct ABC *abc) { }\n" "\n" "void f(struct ABC *abc) {\n" " abc->a = 0;\n" " foo(abc);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'if(abc)' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // function implementation not seen check("void foo(struct ABC *abc);\n" "\n" "void f(struct ABC *abc) {\n" " abc->a = 0;\n" " foo(abc);\n" " if (abc) { }\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'if(abc)' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); // inconclusive check("void f(struct ABC *abc) {\n" " abc->a = 0;\n" " foo(abc);\n" " if (abc) { }\n" "}", true); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning, inconclusive) Either the condition 'if(abc)' is redundant or there is possible null pointer dereference: abc.\n", errout.str()); } } void functioncalllibrary() { Settings settings1; Tokenizer tokenizer(&settings1,this); std::istringstream code("void f() { int a,b,c; x(a,b,c); }"); tokenizer.tokenize(code,"test.c"); const Token *xtok = Token::findsimplematch(tokenizer.tokens(), "x"); // nothing bad.. { Library library; Library::ArgumentChecks arg; library.functions["x"].argumentChecks[1] = arg; library.functions["x"].argumentChecks[2] = arg; library.functions["x"].argumentChecks[3] = arg; std::list null; CheckNullPointer::parseFunctionCall(*xtok, null, &library); ASSERT_EQUALS(0U, null.size()); } // for 1st parameter null pointer is not ok.. { Library library; Library::ArgumentChecks arg; library.functions["x"].argumentChecks[1] = arg; library.functions["x"].argumentChecks[2] = arg; library.functions["x"].argumentChecks[3] = arg; library.functions["x"].argumentChecks[1].notnull = true; std::list null; CheckNullPointer::parseFunctionCall(*xtok, null, &library); ASSERT_EQUALS(1U, null.size()); ASSERT_EQUALS("a", null.front()->str()); } } void functioncallDefaultArguments() { check("void f(int *p = 0) {\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " if (!p)\n" " return;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char a, int *p = 0) {\n" " *p = 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " printf(\"p = %d\", *p);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " printf(\"p[1] = %d\", p[1]);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " buf[p] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p != 0 && bar())\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p) {\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p != 0)\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " int y;\n" " if (p == 0)\n" " p = &y;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int a, int *p = 0) {\n" " if (a != 0)\n" " *p = 0;\n" "}", true); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", "", errout.str()); check("void f(int *p = 0) {\n" " p = a;\n" " *p = 0;\n" // <- don't simplify and verify "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " p += a;\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int *p = 0) {\n" " if (p == 0) {\n" " return 0;\n" " }\n" " return *p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " std::cout << p ? *p : 0;\n" // Due to operator precedence, this is equivalent to: (std::cout << p) ? *p : 0; "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); // Check the first branch of ternary check("void f(char *p = 0) {\n" " std::cout << p ? *p : 0;\n" // Due to operator precedence, this is equivalent to: (std::cout << p) ? *p : 0; "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); check("void f(int *p = 0) {\n" " std::cout << (p ? *p : 0);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " std::cout << p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " std::cout << (p && p[0] ? *p : 42);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void isEmpty(int *p = 0) {\n" " return p && *p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void g(int *p = 0) {\n" " return !p || *p;\n" "}"); ASSERT_EQUALS("", errout.str()); // bar may initialize p but be can't know for sure without knowing // if p is passed in by reference and is modified by bar() check("void f(int *p = 0) {\n" " bar(p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " printf(\"%p\", p);\n" " *p = 0;\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); // The init() function may or may not initialize p, but since the address // of p is passed in, it's a good bet that p may be modified and // so we should not report an error. check("void f(int *p = 0) {\n" " init(&p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void init(int* &g);\n" "void f(int *p = 0) {\n" " init(p);\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p == 0) {\n" " init(&p);\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int *p = 0) {\n" " if (p == 0) {\n" " throw SomeException;\n" " }\n" " *p = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int *p = 0) {\n" " int var1 = x ? *p : 5;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout.str()); } void nullpointer_internal_error() { // ticket #5080 check("struct A { unsigned int size; };\n" "struct B { struct A *a; };\n" "void f(struct B *b) {\n" " unsigned int j;\n" " for (j = 0; j < b[0].a->size; ++j) {\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void ticket6505() { check("void foo(MythSocket *socket) {\n" " bool do_write=0;\n" " if (socket) {\n" " do_write=something();\n" " }\n" " if (do_write) {\n" " socket->func();\n" " }\n" "}\n" "void bar() {\n" " foo(0);\n" "}\n", true, "test.c"); ASSERT_EQUALS("", errout.str()); } void subtract() { check("void foo(char *s) {\n" " p = s - 20;\n" "}\n" "void bar() { foo(0); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " p = s - 20;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout.str()); check("void foo(char *s) {\n" " s -= 20;\n" "}\n" "void bar() { foo(0); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " s -= 20;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout.str()); check("int* f8() { int *x = NULL; return --x; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout.str()); check("int* f9() { int *x = NULL; return x--; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout.str()); } void addNull() { check("void foo(char *s) {\n" " char * p = s + 20;\n" "}\n" "void bar() { foo(0); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " char * p = s + 20;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " char * p = 20 + s;\n" "}\n" "void bar() { foo(0); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " char * p = 20 + s;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " s += 20;\n" "}\n" "void bar() { foo(0); }\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("void foo(char *s) {\n" " if (!s) {}\n" " s += 20;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout.str()); check("int* f7() { int *x = NULL; return ++x; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("int* f10() { int *x = NULL; return x++; } "); ASSERT_EQUALS("[test.cpp:1]: (error) Pointer addition with NULL pointer.\n", errout.str()); check("class foo {};\n" "const char* get() const { return 0; }\n" "void f(foo x) { if (get()) x += get(); }\n"); ASSERT_EQUALS("", errout.str()); } void isPointerDeRefFunctionDecl() { check("const char** get() { return 0; }"); ASSERT_EQUALS("", errout.str()); } void ctu(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); CTU::FileInfo *ctu = CTU::getFileInfo(&tokenizer); // Check code.. std::list fileInfo; CheckNullPointer check(&tokenizer, &settings, this); fileInfo.push_back(check.getFileInfo(&tokenizer, &settings)); check.analyseWholeProgram(ctu, fileInfo, settings, *this); while (!fileInfo.empty()) { delete fileInfo.back(); fileInfo.pop_back(); } delete ctu; } void ctu() { setMultiline(); ctu("void f(int *fp) {\n" " a = *fp;\n" "}\n" "int main() {\n" " int *p = 0;\n" " f(p);\n" "}"); ASSERT_EQUALS("test.cpp:2:error:Null pointer dereference: fp\n" "test.cpp:5:note:Assignment 'p=0', assigned value is 0\n" "test.cpp:6:note:Calling function f, 1st argument is null\n" "test.cpp:2:note:Dereferencing argument fp that is null\n", errout.str()); ctu("void use(int *p) { a = *p + 3; }\n" "void call(int x, int *p) { x++; use(p); }\n" "int main() {\n" " call(4,0);\n" "}"); ASSERT_EQUALS("test.cpp:1:error:Null pointer dereference: p\n" "test.cpp:4:note:Calling function call, 2nd argument is null\n" "test.cpp:2:note:Calling function use, 1st argument is null\n" "test.cpp:1:note:Dereferencing argument p that is null\n", errout.str()); ctu("void dostuff(int *x, int *y) {\n" " if (!var)\n" " return -1;\n" // <- early return " *x = *y;\n" "}\n" "\n" "void f() {\n" " dostuff(a, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); ctu("void dostuff(int *x, int *y) {\n" " if (cond)\n" " *y = -1;\n" // <- conditionally written " *x = *y;\n" "}\n" "\n" "void f() {\n" " dostuff(a, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // else ctu("void dostuff(int mask, int *p) {\n" " if (mask == 13) ;\n" " else *p = 45;\n" "}\n" "\n" "void f() {\n" " dostuff(0, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); // ?, &&, || ctu("void dostuff(int mask, int *p) {\n" " x = (mask & 1) ? *p : 0;\n" "}\n" "\n" "void f() {\n" " dostuff(0, 0);\n" "}"); ASSERT_EQUALS("", errout.str()); ctu("void g(int* x) { *x; }\n" "void f(int* x) {\n" " if (x)\n" " g(x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestNullPointer) cppcheck-1.90/test/testoptions.cpp000066400000000000000000000100571357737443600173270ustar00rootroot00000000000000// Cppcheck - A tool for static C/C++ code analysis // Copyright (C) 2007-2019 Cppcheck team. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "options.h" #include "testsuite.h" class TestOptions: public TestFixture { public: TestOptions() :TestFixture("TestOptions") { } private: void run() OVERRIDE { TEST_CASE(which_test); TEST_CASE(which_test_method); TEST_CASE(no_test_method); TEST_CASE(not_quiet); TEST_CASE(quiet); TEST_CASE(not_help); TEST_CASE(help); TEST_CASE(help_long); TEST_CASE(multiple_testcases); TEST_CASE(multiple_testcases_ignore_duplicates); TEST_CASE(invalid_switches); } void which_test() const { const char* argv[] = {"./test_runner", "TestClass"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT(std::set {"TestClass"} == args.which_test()); } void which_test_method() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT(std::set {"TestClass::TestMethod"} == args.which_test()); } void no_test_method() const { const char* argv[] = {"./test_runner"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT(std::set {""} == args.which_test()); } void not_quiet() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-v"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(false, args.quiet()); } void quiet() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-q"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(true, args.quiet()); } void not_help() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-v"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(false, args.help()); } void help() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-h"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(true, args.help()); } void help_long() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "--help"}; options args(sizeof argv / sizeof argv[0], argv); ASSERT_EQUALS(true, args.help()); } void multiple_testcases() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "TestClass::AnotherTestMethod"}; options args(sizeof argv / sizeof argv[0], argv); std::set expected {"TestClass::TestMethod", "TestClass::AnotherTestMethod"}; ASSERT(expected == args.which_test()); } void multiple_testcases_ignore_duplicates() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "TestClass"}; options args(sizeof argv / sizeof argv[0], argv); std::set expected {"TestClass"}; ASSERT(expected == args.which_test()); } void invalid_switches() const { const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-a", "-v", "-q"}; options args(sizeof argv / sizeof argv[0], argv); std::set expected {"TestClass::TestMethod"}; ASSERT(expected == args.which_test()); ASSERT_EQUALS(true, args.quiet()); } }; REGISTER_TEST(TestOptions) cppcheck-1.90/test/testother.cpp000066400000000000000000011411001357737443600167500ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkother.h" #include "library.h" #include "platform.h" #include "settings.h" #include "standards.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include class TestOther : public TestFixture { public: TestOther() : TestFixture("TestOther") { } private: Settings _settings; void run() OVERRIDE { LOAD_LIB_2(_settings.library, "std.cfg"); TEST_CASE(emptyBrackets); TEST_CASE(zeroDiv1); TEST_CASE(zeroDiv2); TEST_CASE(zeroDiv3); TEST_CASE(zeroDiv4); TEST_CASE(zeroDiv5); TEST_CASE(zeroDiv6); TEST_CASE(zeroDiv7); // #4930 TEST_CASE(zeroDiv8); TEST_CASE(zeroDiv9); TEST_CASE(zeroDiv10); TEST_CASE(zeroDiv11); TEST_CASE(zeroDiv12); TEST_CASE(zeroDivCond); // division by zero / useless condition TEST_CASE(nanInArithmeticExpression); TEST_CASE(varScope1); TEST_CASE(varScope2); TEST_CASE(varScope3); TEST_CASE(varScope4); TEST_CASE(varScope5); TEST_CASE(varScope6); TEST_CASE(varScope7); TEST_CASE(varScope8); TEST_CASE(varScope9); // classes may have extra side-effects TEST_CASE(varScope10); // Undefined macro FOR TEST_CASE(varScope11); // #2475 - struct initialization is not inner scope TEST_CASE(varScope12); TEST_CASE(varScope13); // variable usage in inner loop TEST_CASE(varScope14); TEST_CASE(varScope15); // #4573 if-else-if TEST_CASE(varScope16); TEST_CASE(varScope17); TEST_CASE(varScope18); TEST_CASE(varScope20); // Ticket #5103 TEST_CASE(varScope21); // Ticket #5382 TEST_CASE(varScope22); // Ticket #5684 TEST_CASE(varScope23); // Ticket #6154 TEST_CASE(varScope24); // pointer / reference TEST_CASE(varScope25); // time_t TEST_CASE(varScope26); // range for loop, map TEST_CASE(oldStylePointerCast); TEST_CASE(invalidPointerCast); TEST_CASE(passedByValue); TEST_CASE(passedByValue_nonConst); TEST_CASE(passedByValue_externC); TEST_CASE(constVariable); TEST_CASE(switchRedundantAssignmentTest); TEST_CASE(switchRedundantOperationTest); TEST_CASE(switchRedundantBitwiseOperationTest); TEST_CASE(unreachableCode); TEST_CASE(suspiciousCase); TEST_CASE(suspiciousEqualityComparison); TEST_CASE(selfAssignment); TEST_CASE(trac1132); TEST_CASE(testMisusedScopeObjectDoesNotPickFunction1); TEST_CASE(testMisusedScopeObjectDoesNotPickFunction2); TEST_CASE(testMisusedScopeObjectPicksClass); TEST_CASE(testMisusedScopeObjectPicksStruct); TEST_CASE(testMisusedScopeObjectDoesNotPickIf); TEST_CASE(testMisusedScopeObjectDoesNotPickConstructorDeclaration); TEST_CASE(testMisusedScopeObjectDoesNotPickFunctor); TEST_CASE(testMisusedScopeObjectDoesNotPickLocalClassConstructors); TEST_CASE(testMisusedScopeObjectDoesNotPickUsedObject); TEST_CASE(testMisusedScopeObjectDoesNotPickPureC); TEST_CASE(testMisusedScopeObjectDoesNotPickNestedClass); TEST_CASE(testMisusedScopeObjectInConstructor); TEST_CASE(testMisusedScopeObjectNoCodeAfter); TEST_CASE(trac2071); TEST_CASE(trac2084); TEST_CASE(trac3693); TEST_CASE(clarifyCalculation); TEST_CASE(clarifyStatement); TEST_CASE(duplicateBranch); TEST_CASE(duplicateBranch1); // tests extracted by http://www.viva64.com/en/b/0149/ ( Comparison between PVS-Studio and cppcheck ): Errors detected in Quake 3: Arena by PVS-Studio: Fragment 2 TEST_CASE(duplicateBranch2); // empty macro TEST_CASE(duplicateBranch3); TEST_CASE(duplicateExpression1); TEST_CASE(duplicateExpression2); // ticket #2730 TEST_CASE(duplicateExpression3); // ticket #3317 TEST_CASE(duplicateExpression4); // ticket #3354 (++) TEST_CASE(duplicateExpression5); // ticket #3749 (macros with same values) TEST_CASE(duplicateExpression6); // ticket #4639 TEST_CASE(duplicateExpression7); TEST_CASE(duplicateExpression8); TEST_CASE(duplicateExpression9); // #9320 TEST_CASE(duplicateExpression10); // #9485 TEST_CASE(duplicateExpressionLoop); TEST_CASE(duplicateValueTernary); TEST_CASE(duplicateExpressionTernary); // #6391 TEST_CASE(duplicateExpressionTemplate); // #6930 TEST_CASE(oppositeExpression); TEST_CASE(duplicateVarExpression); TEST_CASE(duplicateVarExpressionUnique); TEST_CASE(duplicateVarExpressionAssign); TEST_CASE(duplicateVarExpressionCrash); TEST_CASE(multiConditionSameExpression); TEST_CASE(checkSignOfUnsignedVariable); TEST_CASE(checkSignOfPointer); TEST_CASE(checkForSuspiciousSemicolon1); TEST_CASE(checkForSuspiciousSemicolon2); TEST_CASE(checkInvalidFree); TEST_CASE(checkRedundantCopy); TEST_CASE(checkNegativeShift); TEST_CASE(incompleteArrayFill); TEST_CASE(redundantVarAssignment); TEST_CASE(redundantVarAssignment_struct); TEST_CASE(redundantVarAssignment_7133); TEST_CASE(redundantVarAssignment_stackoverflow); TEST_CASE(redundantVarAssignment_lambda); TEST_CASE(redundantVarAssignment_loop); TEST_CASE(redundantVarAssignment_after_switch); TEST_CASE(redundantVarAssignment_pointer); TEST_CASE(redundantVarAssignment_pointer_parameter); TEST_CASE(redundantVarAssignment_array); TEST_CASE(redundantInitialization); TEST_CASE(redundantMemWrite); TEST_CASE(varFuncNullUB); TEST_CASE(checkPipeParameterSize); // ticket #3521 TEST_CASE(checkCastIntToCharAndBack); // ticket #160 TEST_CASE(checkCommaSeparatedReturn); TEST_CASE(checkPassByReference); TEST_CASE(checkComparisonFunctionIsAlwaysTrueOrFalse); TEST_CASE(integerOverflow); // #5895 TEST_CASE(redundantPointerOp); TEST_CASE(test_isSameExpression); TEST_CASE(raceAfterInterlockedDecrement); TEST_CASE(testUnusedLabel); TEST_CASE(testEvaluationOrder); TEST_CASE(testEvaluationOrderSelfAssignment); TEST_CASE(testEvaluationOrderMacro); TEST_CASE(testEvaluationOrderSequencePointsFunctionCall); TEST_CASE(testEvaluationOrderSequencePointsComma); TEST_CASE(testEvaluationOrderSizeof); TEST_CASE(testUnsignedLessThanZero); TEST_CASE(doubleMove1); TEST_CASE(doubleMoveMemberInitialization1); TEST_CASE(doubleMoveMemberInitialization2); TEST_CASE(moveAndAssign1); TEST_CASE(moveAndAssign2); TEST_CASE(moveAssignMoveAssign); TEST_CASE(moveAndReset1); TEST_CASE(moveAndReset2); TEST_CASE(moveResetMoveReset); TEST_CASE(moveAndFunctionParameter); TEST_CASE(moveAndFunctionParameterReference); TEST_CASE(moveAndFunctionParameterConstReference); TEST_CASE(moveAndFunctionParameterUnknown); TEST_CASE(moveAndReturn); TEST_CASE(moveAndClear); TEST_CASE(movedPointer); TEST_CASE(moveAndAddressOf); TEST_CASE(partiallyMoved); TEST_CASE(moveAndLambda); TEST_CASE(forwardAndUsed); TEST_CASE(funcArgNamesDifferent); TEST_CASE(funcArgOrderDifferent); TEST_CASE(cpp11FunctionArgInit); // #7846 - "void foo(int declaration = {}) {" TEST_CASE(shadowVariables); TEST_CASE(constArgument); TEST_CASE(checkComparePointers); TEST_CASE(unusedVariableValueTemplate); // #8994 } void check(const char code[], const char *filename = nullptr, bool experimental = false, bool inconclusive = true, bool runSimpleChecks=true, bool verbose=false, Settings* settings = nullptr) { // Clear the error buffer.. errout.str(""); if (!settings) { settings = &_settings; } settings->addEnabled("style"); settings->addEnabled("warning"); settings->addEnabled("portability"); settings->addEnabled("performance"); settings->standards.c = Standards::CLatest; settings->standards.cpp = Standards::CPPLatest; settings->inconclusive = inconclusive; settings->experimental = experimental; settings->verbose = verbose; // Tokenize.. Tokenizer tokenizer(settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, filename ? filename : "test.cpp"); // Check.. CheckOther checkOther(&tokenizer, settings, this); checkOther.runChecks(&tokenizer, settings, this); (void)runSimpleChecks; // TODO Remove this } void check(const char code[], Settings *s) { check(code,"test.cpp",false,true,true,false,s); } void checkP(const char code[], const char *filename = "test.cpp") { // Clear the error buffer.. errout.str(""); Settings* settings = &_settings; settings->addEnabled("style"); settings->addEnabled("warning"); settings->addEnabled("portability"); settings->addEnabled("performance"); settings->standards.c = Standards::CLatest; settings->standards.cpp = Standards::CPPLatest; settings->inconclusive = true; settings->experimental = false; // Raw tokens.. std::vector files(1, filename); std::istringstream istr(code); const simplecpp::TokenList tokens1(istr, files, files[0]); // Preprocess.. simplecpp::TokenList tokens2(files); std::map filedata; simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); // Tokenizer.. Tokenizer tokenizer(settings, this); tokenizer.createTokens(&tokens2); tokenizer.simplifyTokens1(""); // Check.. CheckOther checkOther(&tokenizer, settings, this); checkOther.runChecks(&tokenizer, settings, this); } void checkposix(const char code[]) { static Settings settings; settings.addEnabled("warning"); settings.libraries.emplace_back("posix"); check(code, nullptr, // filename false, // experimental false, // inconclusive true, // runSimpleChecks false, // verbose &settings); } void checkInterlockedDecrement(const char code[]) { static Settings settings; settings.platformType = Settings::Win32A; check(code, nullptr, false, false, true, false, &settings); } void emptyBrackets() { check("{\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv1() { // floating point division by zero => no error check("void foo() {\n" " cout << 1. / 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " cout << 42 / (double)0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " cout << 42 / (float)0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " cout << 42 / (int)0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n", errout.str()); } void zeroDiv2() { check("void foo()\n" "{\n" " int sum = 0;\n" " for(int i = 0; i < n; i ++)\n" " {\n" " sum += i;\n" " }\n" " cout< do not warn check("void f() {\n" " int a = x/2*3/0;\n" " int b = y/2*3%0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " int a = x/2*3/0;\n" " int b = y/2*3%0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n" "[test.cpp:3]: (error) Division by zero.\n", errout.str()); } void zeroDiv8() { // #5584 - FP when function is unknown check("void f() {\n" " int a = 0;\n" " do_something(a);\n" " return 4 / a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Division by zero.\n", errout.str()); } void zeroDiv9() { // #6403 FP zerodiv - inside protecting if-clause check("void foo() {\n" " double fStepHelp = 0;\n" " if( (rOuterValue >>= fStepHelp) ) {\n" " if( fStepHelp != 0.0) {\n" " double fStepMain = 1;\n" " sal_Int32 nIntervalCount = static_cast< sal_Int32 >(fStepMain / fStepHelp);\n" " }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv10() { // #5402 false positive: (error) Division by zero -- with boost::format check("int main() {\n" " std::cout\n" " << boost::format(\" %d :: %s <> %s\") % 0 % \"a\" % \"b\"\n" " << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv11() { check("void f(int a) {\n" " int res = (a+2)/0;\n" " int res = (a*2)/0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n" "[test.cpp:3]: (error) Division by zero.\n", errout.str()); check("void f() {\n" " int res = (a+2)/0;\n" " int res = (a*2)/0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void zeroDiv12() { // #8141 check("intmax_t f() {\n" " return 1 / imaxabs(0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n", errout.str()); } void zeroDivCond() { check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x > 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>0' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x >= 1) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>=1' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(int x) {\n" " int y = 17 / x;\n" " if (x == 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x==0' is redundant or there is division by zero at line 2.\n", errout.str()); check("void f(unsigned int x) {\n" " int y = 17 / x;\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x!=0' is redundant or there is division by zero at line 2.\n", errout.str()); // function call check("void f1(int x, int y) { c=x/y; }\n" "void f2(unsigned int y) {\n" " f1(123,y);\n" " if (y>0){}\n" "}"); TODO_ASSERT_EQUALS( "[test.cpp:4] -> [test.cpp:1]: (warning) Either the condition 'y>0' is redundant or there is division by zero at line 1.\n", "", errout.str()); // avoid false positives when variable is changed after division check("void f() {\n" " unsigned int x = do_something();\n" " int y = 17 / x;\n" " x = some+calculation;\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); { // function is called that might modify global variable check("void do_something();\n" "int x;\n" "void f() {\n" " int y = 17 / x;\n" " do_something();\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // function is called. but don't care, variable is local check("void do_something();\n" "void f() {\n" " int x = some + calculation;\n" " int y = 17 / x;\n" " do_something();\n" " if (x != 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'x!=0' is redundant or there is division by zero at line 4.\n", errout.str()); } check("void do_something(int value);\n" "void f(int x) {\n" " int y = 17 / x;\n" " do_something(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int x;\n" "void f() {\n" " int y = 17 / x;\n" " while (y || x == 0) { x--; }\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket 5033 segmentation fault (valid code) in CheckOther::checkZeroDivisionOrUselessCondition check("void f() {\n" "double* p1= new double[1];\n" "double* p2= new double[1];\n" "double* p3= new double[1];\n" "double* pp[3] = {p1,p2,p3};\n" "}"); ASSERT_EQUALS("", errout.str()); // #5105 - FP check("int f(int a, int b) {\n" " int r = a / b;\n" " if (func(b)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // Unknown types for b and c --> do not warn check("int f(int d) {\n" " int r = (a?b:c) / d;\n" " if (d == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a) {\n" " int r = a ? 1 / a : 0;\n" " if (a == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int a) {\n" " int r = (a == 0) ? 0 : 1 / a;\n" " if (a == 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int g();\n" "void f(int b) {\n" " int x = g();\n" " if (x == 0) {}\n" " else if (x > 0) {}\n" " else\n" " a = b / -x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void nanInArithmeticExpression() { check("void f()\n" "{\n" " double x = 3.0 / 0.0 + 1.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout.str()); check("void f()\n" "{\n" " double x = 3.0 / 0.0 - 1.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout.str()); check("void f()\n" "{\n" " double x = 1.0 + 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout.str()); check("void f()\n" "{\n" " double x = 1.0 - 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout.str()); check("void f()\n" "{\n" " double x = 3.0 / 0.0;\n" " printf(\"%f\", x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope1() { check("unsigned short foo()\n" "{\n" " test_client CClient;\n" " try\n" " {\n" " if (CClient.Open())\n" " {\n" " return 0;\n" " }\n" " }\n" " catch (...)\n" " {\n" " return 2;\n" " }\n" "\n" " try\n" " {\n" " CClient.Close();\n" " }\n" " catch (...)\n" " {\n" " return 2;\n" " }\n" "\n" " return 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope2() { check("int foo()\n" "{\n" " Error e;\n" " e.SetValue(12);\n" " throw e;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope3() { check("void foo()\n" "{\n" " int i;\n" " int *p = 0;\n" " if (abc)\n" " {\n" " p = &i;\n" " }\n" " *p = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope4() { check("void foo()\n" "{\n" " int i;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope5() { check("void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) The scope of the variable 'i' can be reduced.\n", errout.str()); check("void f(int x) {\n" " const unsigned char i = 0;\n" " if (x) {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x)\n" "{\n" " int i = 0;\n" " if (x) {b()}\n" " else {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) The scope of the variable 'i' can be reduced.\n", errout.str()); } void varScope6() { check("void f(int x)\n" "{\n" " int i = x;\n" " if (a) {\n" " x++;\n" " }\n" " if (b) {\n" " c(i);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #5398 " bool success = false;\n" " int notReducable(someClass.getX(&success));\n" " if (success) {\n" " foo(notReducable);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Test &test) {\n" " int& x = test.getData();\n" " if (test.process())\n" " x = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f()\n" "{\n" "int foo = 0;\n" "std::vector vec(10);\n" "BOOST_FOREACH(int& i, vec)\n" "{\n" " foo += 1;\n" " if(foo == 10)\n" " {\n" " return 0;\n" " }\n" "}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int &x)\n" "{\n" " int n = 1;\n" " do\n" " {\n" " ++n;\n" " ++x;\n" " } while (x);\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope7() { check("void f(int x)\n" "{\n" " int y = 0;\n" " b(y);\n" " if (x) {\n" " y++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope8() { check("void test() {\n" " float edgeResistance=1;\n" " std::vector edges;\n" " BOOST_FOREACH(int edge, edges) {\n" " edgeResistance = (edge+1) / 2.0;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'edgeResistance' can be reduced.\n", errout.str()); } void varScope9() { // classes may have extra side effects check("class fred {\n" "public:\n" " void x();\n" "};\n" "void test(int a) {\n" " fred f;\n" " if (a == 2) {\n" " f.x();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope10() { check("int f()\n" "{\n" " int x = 0;\n" " FOR {\n" " foo(x++);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope11() { check("int f() {\n" " int x = 0;\n" " AB ab = { x, 0 };\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int x = 0;\n" " if (a == 0) { ++x; }\n" " AB ab = { x, 0 };\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int x = 0;\n" " if (a == 0) { ++x; }\n" " if (a == 1) { AB ab = { x, 0 }; }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope12() { check("void f(int x) {\n" " int i[5];\n" " int* j = y;\n" " if (x)\n" " foo(i);\n" " foo(j);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'i' can be reduced.\n", errout.str()); check("void f(int x) {\n" " int i[5];\n" " int* j;\n" " if (x)\n" " j = i;\n" " foo(j);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " const bool b = true;\n" " x++;\n" " if (x == 5)\n" " foo(b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " const bool b = x;\n" " x++;\n" " if (x == 5)\n" " foo(b);\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope13() { // #2770 check("void f() {\n" " int i = 0;\n" " forever {\n" " if (i++ == 42) { break; }\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope14() { // #3941 check("void f() {\n" " const int i( foo());\n" " if(a) {\n" " for ( ; i < 10; ++i) ;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope15() { // #4573 check("void f() {\n" " int a,b,c;\n" " if (a);\n" " else if(b);\n" " else if(c);\n" " else;\n" "}", nullptr, false, false); ASSERT_EQUALS("", errout.str()); } void varScope16() { check("void f() {\n" " int a = 0;\n" " while((++a) < 56) {\n" " foo();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 0;\n" " do {\n" " foo();\n" " } while((++a) < 56);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 0;\n" " do {\n" " a = 64;\n" " foo(a);\n" " } while((++a) < 56);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 0;\n" " do {\n" " a = 64;\n" " foo(a);\n" " } while(z());\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'a' can be reduced.\n", errout.str()); } void varScope17() { check("void f() {\n" " int x;\n" " if (a) {\n" " x = stuff(x);\n" " morestuff(x);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); check("void f() {\n" " int x;\n" " if (a) {\n" " x = stuff(x);\n" " morestuff(x);\n" " }\n" " if (b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); } void varScope18() { check("void f() {\n" " short x;\n" "\n" " switch (ab) {\n" " case A:\n" " break;\n" " case B:\n" " default:\n" " break;\n" " }\n" "\n" " if (c) {\n" " x = foo();\n" " do_something(x);\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); check("void f() {\n" " short x;\n" "\n" " switch (ab) {\n" " case A:\n" " x = 10;\n" " break;\n" " case B:\n" " default:\n" " break;\n" " }\n" "\n" " if (c) {\n" " x = foo();\n" " do_something(x);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " short x;\n" "\n" " switch (ab) {\n" " case A:\n" " if(c)\n" " do_something(x);\n" " break;\n" " case B:\n" " default:\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout.str()); check("void f() {\n" " short x;\n" "\n" " switch (ab) {\n" " case A:\n" " if(c)\n" " do_something(x);\n" " break;\n" " case B:\n" " default:\n" " if(d)\n" " do_something(x);\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope20() { // Ticket #5103 - constant variable only used in inner scope check("int f(int a) {\n" " const int x = 234;\n" " int b = a;\n" " if (b > 32) b = x;\n" " return b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope21() { // Ticket #5382 - initializing two-dimensional array check("int test() {\n" " int test_value = 3;\n" " int test_array[1][1] = { { test_value } };\n" " return sizeof(test_array);\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope22() { // Ticket #5684 - "The scope of the variable 'p' can be reduced" - But it can not. check("void foo() {\n" " int* p( 42 );\n" " int i = 0;\n" " while ( i != 100 ) {\n" " *p = i;\n" " ++p;\n" " ++i;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // try to avoid an obvious false negative after applying the fix for the example above: check("void foo() {\n" " int* p( 42 );\n" " int i = 0;\n" " int dummy = 0;\n" " while ( i != 100 ) {\n" " p = & dummy;\n" " *p = i;\n" " ++p;\n" " ++i;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'p' can be reduced.\n", errout.str()); } void varScope23() { // #6154: Don't suggest to reduce scope if inner scope is a lambda check("int main() {\n" " size_t myCounter = 0;\n" " Test myTest([&](size_t aX){\n" " std::cout << myCounter += aX << std::endl;\n" " });\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope24() { check("void f(Foo x) {\n" " Foo &r = x;\n" " if (cond) {\n" " r.dostuff();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'r' can be reduced.\n", errout.str()); check("void f(Foo x) {\n" " Foo foo = x;\n" " if (cond) {\n" " foo.dostuff();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void varScope25() { check("void f() {\n" " time_t currtime;\n" " if (a) {\n" " currtime = time(&dummy);\n" " if (currtime > t) {}\n" " }\n" "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (style) The scope of the variable 'currtime' can be reduced.\n", errout.str()); } void varScope26() { check("void f(const std::map &m) {\n" " for (auto it : m) {\n" " if (cond1) {\n" " int& key = it.first;\n" " if (cond2) { dostuff(key); }\n" " }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void checkOldStylePointerCast(const char code[]) { // Clear the error buffer.. errout.str(""); static Settings settings; settings.addEnabled("style"); settings.standards.cpp = Standards::CPP03; // #5560 // Tokenize.. Tokenizer tokenizerCpp(&settings, this); std::istringstream istr(code); tokenizerCpp.tokenize(istr, "test.cpp"); CheckOther checkOtherCpp(&tokenizerCpp, &settings, this); checkOtherCpp.warningOldStylePointerCast(); } void oldStylePointerCast() { checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (Base *) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base *) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base * const) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (volatile Base *) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (volatile Base * const) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const volatile Base *) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const volatile Base * const) derived;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base *) ( new Derived() );\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base *) new Derived();\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class Base;\n" "void foo()\n" "{\n" " Base * b = (const Base *) new short[10];\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class B;\n" "class A\n" "{\n" " virtual void abc(B *) const = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); checkOldStylePointerCast("class B;\n" "class A\n" "{\n" " virtual void abc(const B *) const = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3630 checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" " X() : Base((SomeType*)7) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" " X() : Base((SomeType*)var) {}\n" "};"); ASSERT_EQUALS("[test.cpp:3]: (style) C-style pointer casting\n", errout.str()); checkOldStylePointerCast("class SomeType;\n" "class X : public Base {\n" " X() : Base((SomeType*)0) {}\n" "};"); ASSERT_EQUALS("", errout.str()); // #5560 checkOldStylePointerCast("class C;\n" "\n" "class B\n" "{ virtual G* createGui(S*, C*) const = 0; };\n" "\n" "class MS : public M\n" "{ virtual void addController(C*) override {} };"); ASSERT_EQUALS("", errout.str()); // #6164 checkOldStylePointerCast("class Base {};\n" "class Derived: public Base {};\n" "void testCC() {\n" " std::vector v;\n" " v.push_back((Base*)new Derived);\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) C-style pointer casting\n", errout.str()); } void checkInvalidPointerCast(const char code[], bool portability = true, bool inconclusive = false) { // Clear the error buffer.. errout.str(""); Settings settings; settings.addEnabled("warning"); if (portability) settings.addEnabled("portability"); settings.inconclusive = inconclusive; settings.defaultSign = 's'; // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); CheckOther checkOtherCpp(&tokenizer, &settings, this); checkOtherCpp.invalidPointerCast(); } void invalidPointerCast() { checkInvalidPointerCast("void test() {\n" " float *f = new float[10];\n" " delete [] (double*)f;\n" " delete [] (long double const*)(new float[10]);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability) Casting between float * and double * which have an incompatible binary data representation.\n" "[test.cpp:4]: (portability) Casting between float * and const long double * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("void test(const float* f) {\n" " double *d = (double*)f;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between const float * and double * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("void test(double* d1) {\n" " long double *ld = (long double*)d1;\n" " double *d2 = (double*)ld;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between double * and long double * which have an incompatible binary data representation.\n" "[test.cpp:3]: (portability) Casting between long double * and double * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("char* test(int* i) {\n" " long double *d = (long double*)(i);\n" " double *d = (double*)(i);\n" " float *f = reinterpret_cast(i);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between signed int * and long double * which have an incompatible binary data representation.\n" "[test.cpp:3]: (portability) Casting between signed int * and double * which have an incompatible binary data representation.\n" "[test.cpp:4]: (portability) Casting between signed int * and float * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("float* test(unsigned int* i) {\n" " return (float*)i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between unsigned int * and float * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("float* test(unsigned int* i) {\n" " return (float*)i[0];\n" "}"); ASSERT_EQUALS("", errout.str()); checkInvalidPointerCast("float* test(double& d) {\n" " return (float*)&d;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between double * and float * which have an incompatible binary data representation.\n", errout.str()); checkInvalidPointerCast("void test(float* data) {\n" " f.write((char*)data,sizeof(float));\n" "}", true, false); ASSERT_EQUALS("", errout.str()); checkInvalidPointerCast("void test(float* data) {\n" " f.write((char*)data,sizeof(float));\n" "}", true, true); // #3639 ASSERT_EQUALS("[test.cpp:2]: (portability, inconclusive) Casting from float * to signed char * is not portable due to different binary data representations on different platforms.\n", errout.str()); checkInvalidPointerCast("long long* test(float* f) {\n" " return (long long*)f;\n" "}", false); ASSERT_EQUALS("", errout.str()); checkInvalidPointerCast("long long* test(float* f, char* c) {\n" " foo((long long*)f);\n" " return reinterpret_cast(c);\n" "}", true); ASSERT_EQUALS("[test.cpp:2]: (portability) Casting from float * to signed long long * is not portable due to different binary data representations on different platforms.\n", errout.str()); checkInvalidPointerCast("Q_DECLARE_METATYPE(int*)"); // #4135 - don't crash } void passedByValue() { check("void f(const std::string str) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::unique_ptr ptr) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::shared_ptr ptr) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::function ptr) {}"); ASSERT_EQUALS("", errout.str()); { check("void f(const std::pair x) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::pair x) {}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } check("void f(const std::string::size_type x) {}"); ASSERT_EQUALS("", errout.str()); check("class Foo;\nvoid f(const Foo foo) {}"); // Unknown class ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("class Foo { std::vector v; };\nvoid f(const Foo foo) {}"); // Large class (STL member) ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("class Foo { int i; };\nvoid f(const Foo foo) {}"); // Small class ASSERT_EQUALS("", errout.str()); check("class Foo { int i[6]; };\nvoid f(const Foo foo) {}"); // Large class (array) ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("class Foo { std::string* s; };\nvoid f(const Foo foo) {}"); // Small class (pointer) ASSERT_EQUALS("", errout.str()); check("class Foo { static std::string s; };\nvoid f(const Foo foo) {}"); // Small class (static member) ASSERT_EQUALS("", errout.str()); check("class X { std::string s; }; class Foo : X { };\nvoid f(const Foo foo) {}"); // Large class (inherited) ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("class X { std::string s; }; class Foo { X x; };\nvoid f(const Foo foo) {}"); // Large class (inherited) ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout.str()); check("void f(const std::string &str) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::vector v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::vector v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::vector::size_type s) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::vector &v) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::map &v) {}"); ASSERT_EQUALS("", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::map v) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("void f(const std::streamoff pos) {}"); ASSERT_EQUALS("", errout.str()); check("void f(std::initializer_list i) {}"); ASSERT_EQUALS("", errout.str()); // #5824 check("void log(const std::string& file, int line, const std::string& function, const std::string str, ...) {}"); ASSERT_EQUALS("", errout.str()); // #5534 check("struct float3 { };\n" "typedef float3 vec;\n" "class Plane {\n" " vec Refract(vec &vec) const;\n" " bool IntersectLinePlane(const vec &planeNormal);\n" "}; "); ASSERT_EQUALS("", errout.str()); check("class X {\n" " virtual void func(const std::string str) {}\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); } void passedByValue_nonConst() { check("void f(std::string str) {}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " return str + x;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " std::cout << str;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " std::cin >> str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::string str) {\n" " std::string s2 = str;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " std::string& s2 = str;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 's2' can be declared with const\n", errout.str()); check("void f(std::string str) {\n" " const std::string& s2 = str;\n" "}"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void f(std::string str) {\n" " str = \"\";\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(std::string str) {\n" " foo(str);\n" // It could be that foo takes str as non-const-reference "}"); ASSERT_EQUALS("", errout.str()); check("void foo(const std::string& str);\n" "void f(std::string str) {\n" " foo(str);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void foo(std::string str);\n" "void f(std::string str) {\n" " foo(str);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("void foo(std::string& str);\n" "void f(std::string str) {\n" " foo(str);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(std::string* str);\n" "void f(std::string str) {\n" " foo(&str);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int& i1, const std::string& str, int& i2);\n" "void f(std::string str) {\n" " foo((a+b)*c, str, x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("std::string f(std::string str) {\n" " str += x;\n" " return str;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class X {\n" " std::string s;\n" " void func() const;\n" "};\n" "Y f(X x) {\n" " x.func();\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance) Function parameter 'x' should be passed by const reference.\n", errout.str()); check("class X {\n" " void func();\n" "};\n" "Y f(X x) {\n" " x.func();\n" "}"); ASSERT_EQUALS("", errout.str()); check("class X {\n" " void func(std::string str) {}\n" "};"); ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout.str()); check("class X {\n" " virtual void func(std::string str) {}\n" // Do not warn about virtual functions, if 'str' is not declared as const "};"); ASSERT_EQUALS("", errout.str()); check("class X {\n" " char a[1024];\n" "};\n" "class Y : X {\n" " char b;\n" "};\n" "void f(Y y) {\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (performance) Function parameter 'y' should be passed by const reference.\n", errout.str()); check("class X {\n" " void* a;\n" " void* b;\n" "};\n" "class Y {\n" " void* a;\n" " void* b;\n" " char c;\n" "};\n" "void f(X x, Y y) {\n" "}"); ASSERT_EQUALS("[test.cpp:10]: (performance) Function parameter 'y' should be passed by const reference.\n", errout.str()); { // 8-byte data should be passed by const reference on 32-bit platform but not on 64-bit platform const char code[] = "class X {\n" " uint64_t a;\n" " uint64_t b;\n" "};\n" "void f(X x) {}"; Settings s32(_settings); s32.platform(cppcheck::Platform::Unix32); check(code, &s32); ASSERT_EQUALS("[test.cpp:5]: (performance) Function parameter 'x' should be passed by const reference.\n", errout.str()); Settings s64(_settings); s64.platform(cppcheck::Platform::Unix64); check(code, &s64); ASSERT_EQUALS("", errout.str()); } } void passedByValue_externC() { check("struct X { int a[5]; }; void f(X v) { }"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("extern \"C\" { struct X { int a[5]; }; void f(X v) { } }"); ASSERT_EQUALS("", errout.str()); check("struct X { int a[5]; }; extern \"C\" void f(X v) { }"); ASSERT_EQUALS("", errout.str()); check("struct X { int a[5]; }; void f(const X v);"); ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout.str()); check("extern \"C\" { struct X { int a[5]; }; void f(const X v); }"); ASSERT_EQUALS("", errout.str()); check("struct X { int a[5]; }; extern \"C\" void f(const X v) { }"); ASSERT_EQUALS("", errout.str()); } void constVariable() { check("int f(std::vector x) {\n" " int& i = x[0];\n" " return i;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared with const\n", errout.str()); check("int f(std::vector& x) {\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared with const\n", errout.str()); check("int f(std::vector x) {\n" " const int& i = x[0];\n" " return i;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector x) {\n" " static int& i = x[0];\n" " return i;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector x) {\n" " int& i = x[0];\n" " i++;\n" " return i;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int& f(std::vector& x) {\n" " x.push_back(1);\n" " int& i = x[0];\n" " return i;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(const std::vector& x) {\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int& f(std::vector& x) {\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(std::vector& x) {\n" " x[0]++;\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int a; };\n" "A f(std::vector& x) {\n" " x[0].a = 1;\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int a(); };\n" "A f(std::vector& x) {\n" " x[0].a();\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int g(int& x);\n" "int f(std::vector& x) {\n" " g(x[0]);\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("template\n" "T f(T& x) {\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("template\n" "T f(T&& x) {\n" " return x[0];\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("template\n" "T f(T& x) {\n" " return x[0];\n" "}\n" "void h() { std::vector v; h(v); }\n"); ASSERT_EQUALS("", errout.str()); check("int f(int& x) {\n" " return std::move(x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(std::ostream& os) {\n" " os << \"Hello\";\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(int*);\n" "void f(int& x) {\n" " g(&x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { A(int*); };\n" "A f(int& x) {\n" " return A(&x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { A(int*); };\n" "A f(int& x) {\n" " return A{&x};\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Perhaps unused variable should be checked as well. check("void f(int& x, int& y) {\n" " y++;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " explicit A(int& y) : x(&y) {}\n" " int * x = nullptr;\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " std::vector v;\n" " void swap(A& a) {\n" " v.swap(a.v);\n" " }\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " template\n" " void f();\n" " template\n" " void f() const;\n" "};\n" "void g(A& a) {\n" " a.f();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector& v) {\n" " for(auto&& x:v)\n" " x = 1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector& v) {\n" " for(auto x:v)\n" " x = 1;\n" "}\n"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); check("void f(std::vector& v) {\n" " for(auto& x:v) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); check("void f(std::vector& v) {\n" " for(const auto& x:v) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared with const\n", errout.str()); check("void f(int& i) {\n" " int& j = i;\n" " j++;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(std::vector& v) {\n" " int& i = v[0];\n" " i++;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(std::map >& m, unsigned int i) {\n" " std::map& members = m[i];\n" " members.clear();\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " int& x;\n" " A(int& y) : x(y)\n" " {}\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " A(int& x);\n" "};\n" "struct B : A {\n" " B(int& x) : A(x)\n" " {}\n" "};\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool b, int& x, int& y) {\n" " auto& z = x;\n" " auto& w = b ? y : z;\n" " w = 1;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int i;\n" "};\n" "int& f(S& s) {\n" " return s.i;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void e();\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void ai(void);\n" "void j(void);\n" "void e(void);\n" "void k(void);\n" "void l(void);\n" "void m(void);\n" "void n(void);\n" "void o(void);\n" "void q(void);\n" "void r(void);\n" "void t(void);\n" "void u(void);\n" "void v(void);\n" "void w(void);\n" "void z(void);\n" "void aj(void);\n" "void am(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void k(void);\n" "void ao(wchar_t *d);\n" "void ah(void);\n" "void e(void);\n" "void an(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void e(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void k(void);\n" "void e(void);\n" "void g(void);\n" "void ah(void);\n" "void k(void);\n" "void an(void);\n" "void e(void);\n" "void e(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void k(void);\n" "void an(void);\n" "void k(void);\n" "void e(void);\n" "void g(void);\n" "void ah(void);\n" "void e(void);\n" "void k(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void an(void);\n" "void k(void);\n" "void e(void);\n" "void e(void);\n" "void e(void);\n" "void g(void);\n" "void k(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void k(void);\n" "void k(void);\n" "void e(void);\n" "void g(void);\n" "void g(void);\n" "void ah(void);\n" "void an(void);\n" "void e(void);\n" "void k(void);\n" "void e(void);\n" "void ap(wchar_t *c, int d);\n" "void ah(void);\n" "void an(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void aq(char *b, size_t d, char *c, int a);\n" "void ar(char *b, size_t d, char *c, va_list a);\n" "void k(void);\n" "void g(void);\n" "void g(void);\n" "void h(void);\n" "void ah(void);\n" "void an(void);\n" "void k(void);\n" "void k(void);\n" "void e(void);\n" "void g(void);\n" "void g(void);\n" "void as(std::string s);\n" "void at(std::ifstream &f);\n" "void au(std::istream &f);\n" "void av(std::string &aa, std::wstring &ab);\n" "void aw(bool b, double x, double y);\n" "void ax(int i);\n" "void ay(std::string c, std::wstring a);\n" "void az(const std::locale &ac);\n" "void an();\n" "void ba(std::ifstream &f);\n" "void bb(std::istream &f) {\n" "f.read(NULL, 0);\n" "}\n" "void h(void) {\n" "struct tm *tm = 0;\n" "(void)std::asctime(tm);\n" "(void)std::asctime(0);\n" "}\n" "void bc(size_t ae) {\n" "wchar_t *ad = 0, *af = 0;\n" "struct tm *ag = 0; \n" "(void)std::wcsftime(ad, ae, af, ag);\n" "(void)std::wcsftime(0, ae, 0, 0);\n" "}\n" "void k(void) {}\n" "void bd(void);\n" "void be(void);\n" "void bf(int b);\n" "void e(void);\n" "void e(void);\n" "void bg(wchar_t *p);\n" "void bh(const std::list &ak, const std::list &al);\n" "void ah();\n" "void an();\n" "void h();\n"); ASSERT_EQUALS("", errout.str()); } void switchRedundantAssignmentTest() { check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = 2;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y = 2;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = 2;\n" " case 3:\n" " if (x)\n" " {\n" " y = 3;\n" " }\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y = 2;\n" " if (z)\n" " printf(\"%d\", y);\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int x = a;\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " x = 2;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " break;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " while(xyz()) {\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " continue;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " while(xyz()) {\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " throw e;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " printf(\"%d\", y);\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " bar();\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void bar() {}\n" // bar isn't noreturn "void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y = 2;\n" " bar();\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " case 3:\n" " strcpy(str, \"b'\");\n" " }\n" "}", nullptr, false, false, false); // TODO ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " switch (a)\n" " {\n" " case 2:\n" " strncpy(str, \"a'\");\n" " case 3:\n" " strncpy(str, \"b'\");\n" " }\n" "}"); // TODO ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " int z = 0;\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " z++;\n" " case 3:\n" " strcpy(str, \"b'\");\n" " z++;\n" " }\n" "}", nullptr, false, false, false); // TODO ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (warning) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " break;\n" " case 3:\n" " strcpy(str, \"b'\");\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a) {\n" " char str[10];\n" " switch (a)\n" " {\n" " case 2:\n" " strcpy(str, \"a'\");\n" " printf(str);\n" " case 3:\n" " strcpy(str, \"b'\");\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // Ticket #5158 "segmentation fault (valid code)" check("typedef struct ct_data_s {\n" " union {\n" " char freq;\n" " } fc;\n" "} ct_data;\n" "typedef struct internal_state {\n" " struct ct_data_s dyn_ltree[10];\n" "} deflate_state;\n" "void f(deflate_state *s) {\n" " s->dyn_ltree[0].fc.freq++;\n" "}\n", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // Ticket #6132 "crash: daca: kvirc CheckOther::checkRedundantAssignment()" check("void HttpFileTransfer :: transferTerminated ( bool bSuccess@1 ) {\n" "if ( m_szCompletionCallback . isNull ( ) ) {\n" "KVS_TRIGGER_EVENT ( KviEvent_OnHTTPGetTerminated , out ? out : ( g_pApp . activeConsole ( ) ) , & vParams )\n" "} else {\n" "KviKvsScript :: run ( m_szCompletionCallback , out ? out : ( g_pApp . activeConsole ( ) ) , & vParams ) ;\n" "}\n" "}\n", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x;\n" " switch (state) {\n" " case 1: x = 3; goto a;\n" " case 1: x = 6; goto a;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void switchRedundantOperationTest() { check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " ++y;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " ++y;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " (void)y;\n" " case 3:\n" " ++y;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " ++y;\n" " case 3:\n" " ++y;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " --y;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " --y;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " (void)y;\n" " case 3:\n" " --y;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " --y;\n" " case 3:\n" " --y;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y++;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y++;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = 2;\n" " case 3:\n" " y++;\n" " }\n" " bar(y);\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y++;\n" " case 3:\n" " y++;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y--;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y--;\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = 2;\n" " case 3:\n" " y--;\n" " }\n" " bar(y);\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y--;\n" " case 3:\n" " y--;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y++;\n" " case 3:\n" " if (x)\n" " {\n" " y = 3;\n" " }\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " {\n" " y++;\n" " if (y)\n" " printf(\"%d\", y);\n" " }\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int x = a;\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " x++;\n" " case 3:\n" " y++;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " break;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " while(xyz()) {\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " continue;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " while(xyz()) {\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " throw e;\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " printf(\"%d\", y);\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int y = 1;\n" " switch (x)\n" " {\n" " case 2:\n" " y++;\n" " bar();\n" " case 3:\n" " y = 3;\n" " }\n" " bar(y);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (warning) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout.str()); check("bool f() {\n" " bool ret = false;\n" " switch (switchCond) {\n" " case 1:\n" " ret = true;\n" " break;\n" " case 31:\n" " ret = true;\n" " break;\n" " case 54:\n" " ret = true;\n" " break;\n" " };\n" " ret = true;\n" " return ret;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n" "[test.cpp:8] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n" "[test.cpp:11] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", errout.str()); } void switchRedundantBitwiseOperationTest() { check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y = y | 3;\n" " case 3:\n" " y = y | 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " default:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " default:\n" " if (z)\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= z;\n" " z++\n" " default:\n" " y |= z;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " bar(y);\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " y = 4;\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:8]: (style) Variable 'y' is reassigned a value before the old one has been used.\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y &= 3;\n" " case 3:\n" " y &= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 3;\n" " break;\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y ^= 3;\n" " case 3:\n" " y ^= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 2;\n" " case 3:\n" " y |= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y &= 2;\n" " case 3:\n" " y &= 3;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " int y = 1;\n" " switch (a)\n" " {\n" " case 2:\n" " y |= 2;\n" " case 3:\n" " y &= 2;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void unreachableCode() { check("void foo(int a) {\n" " while(1) {\n" " if (a++ >= 100) {\n" " break;\n" " continue;\n" " }\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:5]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("int foo(int a) {\n" " return 0;\n" " return(a-1);\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("int foo(int a) {\n" " A:" " return(0);\n" " goto A;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); Settings settings; settings.library.setnoreturn("exit", true); settings.library.functions["exit"].argumentChecks[1] = Library::ArgumentChecks(); check("void foo() {\n" " exit(0);\n" " break;\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("class NeonSession {\n" " void exit();\n" "};\n" "void NeonSession::exit()\n" "{\n" " SAL_INFO(\"ucb.ucp.webdav\", \"neon commands cannot be aborted\");\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void NeonSession::exit()\n" "{\n" " SAL_INFO(\"ucb.ucp.webdav\", \"neon commands cannot be aborted\");\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void foo() { xResAccess->exit(); }", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " switch(a) {\n" " case 0:\n" " printf(\"case 0\");\n" " break;\n" " break;\n" " case 1:\n" " c++;\n" " break;\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:7]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo(int a)\n" "{\n" " switch(a) {\n" " case 0:\n" " printf(\"case 0\");\n" " break;\n" " case 1:\n" " c++;\n" " break;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a)\n" "{\n" " while(true) {\n" " if (a++ >= 100) {\n" " break;\n" " break;\n" " }\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:6]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo(int a)\n" "{\n" " while(true) {\n" " if (a++ >= 100) {\n" " continue;\n" " continue;\n" " }\n" " a+=2;\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:6]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo(int a)\n" "{\n" " while(true) {\n" " if (a++ >= 100) {\n" " continue;\n" " }\n" " a+=2;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " throw 0;\n" " return 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " throw 0;\n" " return;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("int foo() {\n" " return 0;\n" " return 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("int foo() {\n" " return 0;\n" " foo();\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Statements following return, break, continue, goto or throw will never be executed.\n", errout.str()); check("int foo(int unused) {\n" " return 0;\n" " (void)unused;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("int foo(int unused1, int unused2) {\n" " return 0;\n" " (void)unused1;\n" " (void)unused2;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("int foo(int unused1, int unused2) {\n" " return 0;\n" " (void)unused1;\n" " (void)unused2;\n" " foo();\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:5]: (style) Statements following return, break, continue, goto or throw will never be executed.\n", errout.str()); check("int foo() {\n" " if(bar)\n" " return 0;\n" " return 124;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " while(bar) {\n" " return 0;\n" " return 0;\n" " return 0;\n" " return 0;\n" " }\n" " return 124;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:4]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); check("void foo() {\n" " while(bar) {\n" " return;\n" " break;\n" " }\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:4]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); // #5707 check("extern int i,j;\n" "int foo() {\n" " switch(i) {\n" " default: j=1; break;\n" " }\n" " return 0;\n" " j=2;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:7]: (style) Statements following return, break, continue, goto or throw will never be executed.\n", errout.str()); check("int foo() {\n" " return 0;\n" " label:\n" " throw 0;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Label 'label' is not used.\n", errout.str()); check("struct A {\n" " virtual void foo (P & Val) throw ();\n" " virtual void foo1 (P & Val) throw ();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" " goto label;\n" " while (true) {\n" " bar();\n" " label:\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3457 check("int foo() {\n" " goto label;\n" " do {\n" " bar();\n" " label:\n" " } while (true);\n" "}"); ASSERT_EQUALS("", errout.str()); // #3457 check("int foo() {\n" " goto label;\n" " for (;;) {\n" " bar();\n" " label:\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // #3457 // #3383. TODO: Use preprocessor check("int foo() {\n" "\n" // #ifdef A " return 0;\n" "\n" // #endif " return 1;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" "\n" // #ifdef A " return 0;\n" "\n" // #endif " return 1;\n" "}", nullptr, false, true, false); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout.str()); // #4711 lambda functions check("int f() {\n" " return g([](int x){(void)x+1; return x;});\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // #4756 check("template <>\n" "inline uint16_t htobe(uint16_t value) {\n" " return ( __extension__ ({\n" " register unsigned short int __v, __x = (unsigned short int) (value);\n" " if (__builtin_constant_p (__x))\n" " __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | (((__x) & 0xff) << 8)));\n" " else\n" " __asm__ (\"rorw $8, %w0\" : \"=r\" (__v) : \"0\" (__x) : \"cc\");\n" " (void)__v;\n" " }));\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // #6008 check("static std::function< int ( int, int ) > GetFunctor() {\n" " return [](int a_, int b_) -> int {\n" " int sum = a_ + b_;\n" " return sum;\n" " };\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // #5789 check("struct per_state_info {\n" " uint64_t enter, exit;\n" " uint64_t events;\n" " per_state_info() : enter(0), exit(0), events(0) {}\n" "};", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); // #6664 check("void foo() {\n" " (beat < 100) ? (void)0 : exit(0);\n" " bar();\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " (beat < 100) ? exit(0) : (void)0;\n" " bar();\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); // #8261 check("void foo() {\n" " (beat < 100) ? (void)0 : throw(0);\n" " bar();\n" "}", nullptr, false, false, false, false, &settings); ASSERT_EQUALS("", errout.str()); } void suspiciousCase() { check("void foo() {\n" " switch(a) {\n" " case A&&B:\n" " foo();\n" " case (A||B):\n" " foo();\n" " case A||B:\n" " foo();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Found suspicious case label in switch(). Operator '&&' probably doesn't work as intended.\n" "[test.cpp:5]: (warning, inconclusive) Found suspicious case label in switch(). Operator '||' probably doesn't work as intended.\n" "[test.cpp:7]: (warning, inconclusive) Found suspicious case label in switch(). Operator '||' probably doesn't work as intended.\n", errout.str()); check("void foo() {\n" " switch(a) {\n" " case 1:\n" " a=A&&B;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " switch(a) {\n" " case A&&B?B:A:\n" " foo();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void suspiciousEqualityComparison() { check("void foo(int c) {\n" " if (x) c == 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int* c) {\n" " if (x) *c == 0;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " if (c == 1) {\n" " c = 0;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int c) {\n" " c == 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (int i = 0; i == 10; i ++) {\n" " a ++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int c) {\n" " for (i == 0; i < 10; i ++) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (i == 1; i < 10; i ++) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (i == 2; i < 10; i ++) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (int i = 0; i < 10; i == c) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int c) {\n" " for (; running == 1;) {\n" " c ++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int c) {\n" " printf(\"%i\", ({x==0;}));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int arg) {\n" " printf(\"%i\", ({int x = do_something(); x == 0;}));\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " printf(\"%i\", ({x == 0; x > 0 ? 10 : 20}));\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout.str()); check("void foo(int x) {\n" " for (const Token* end = tok->link(); tok != end; tok = (tok == end) ? end : tok->next()) {\n" " x++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " for (int i = (x == 0) ? 0 : 5; i < 10; i ++) {\n" " x++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int x) {\n" " for (int i = 0; i < 10; i += (x == 5) ? 1 : 2) {\n" " x++;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void selfAssignment() { check("void foo()\n" "{\n" " int x = 1;\n" " x = x;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); check("void foo()\n" "{\n" " int x = x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); check("struct A { int b; };\n" "void foo(A* a1, A* a2) {\n" " a1->b = a1->b;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'a1->b' to itself.\n", errout.str()); check("int x;\n" "void f()\n" "{\n" " x = x = 3;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); // #4073 (segmentation fault) check("void Foo::myFunc( int a )\n" "{\n" " if (a == 42)\n" " a = a;\n" "}"); check("void foo()\n" "{\n" " int x = 1;\n" " x = x + 1;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " int *x = getx();\n" " *x = x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " BAR *x = getx();\n" " x = x;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); // #2502 - non-primitive type -> there might be some side effects check("void foo()\n" "{\n" " Fred fred; fred = fred;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " x = (x == 0);" " func(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) {\n" " x = (x != 0);" " func(x);\n" "}"); ASSERT_EQUALS("", errout.str()); // ticket #3001 - false positive check("void foo(int x) {\n" " x = x ? x : 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #3800 - false negative when variable is extern check("extern int i;\n" "void f() {\n" " i = i;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of 'i' to itself.\n", errout.str()); // #4291 - id for variables accessed through 'this' check("class Foo {\n" " int var;\n" " void func();\n" "};\n" "void Foo::func() {\n" " this->var = var;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (warning) Redundant assignment of 'this->var' to itself.\n", errout.str()); check("class Foo {\n" " int var;\n" " void func(int var);\n" "};\n" "void Foo::func(int var) {\n" " this->var = var;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6406 - designated initializer doing bogus self assignment check("struct callbacks {\n" " void (*s)(void);\n" "};\n" "void something(void) {}\n" "void f() {\n" " struct callbacks ops = { .s = ops.s };\n" "}\n"); TODO_ASSERT_EQUALS("[test.cpp:6]: (warning) Redundant assignment of 'something' to itself.\n", "", errout.str()); check("class V\n" "{\n" "public:\n" " V()\n" " {\n" " x = y = z = 0.0;\n" " }\n" " V( double x, const double y, const double &z )\n" " {\n" " x = x; y = y; z = z;\n" " }\n" " double x, y, z;\n" "};"); ASSERT_EQUALS("[test.cpp:10]: (warning) Redundant assignment of 'x' to itself.\n" "[test.cpp:10]: (warning) Redundant assignment of 'y' to itself.\n" "[test.cpp:10]: (warning) Redundant assignment of 'z' to itself.\n", errout.str()); check("void f(int i) { i = !!i; }"); ASSERT_EQUALS("", errout.str()); } void trac1132() { check("class Lock\n" "{\n" "public:\n" " Lock(int i)\n" " {\n" " std::cout << \"Lock \" << i << std::endl;\n" " }\n" " ~Lock()\n" " {\n" " std::cout << \"~Lock\" << std::endl;\n" " }\n" "};\n" "int main()\n" "{\n" " Lock(123);\n" " std::cout << \"hello\" << std::endl;\n" " return 0;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:15]: (style) Instance of 'Lock' object is destroyed immediately.\n", errout.str()); } void trac3693() { check("struct A{\n" " enum {\n" " b = 300\n" " };\n" "};\n" "const int DFLT_TIMEOUT = A::b % 1000000 ;\n", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickFunction1() { check("int main ( )\n" "{\n" " CouldBeFunction ( 123 ) ;\n" " return 0 ;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickFunction2() { check("struct error {\n" " error() {}\n" "};\n" "\n" "class parser {\n" "public:\n" " void error() const {}\n" "\n" " void foo() const {\n" " error();\n" " do_something();\n" " }\n" "};\n" ); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectPicksClass() { check("class NotAFunction ;\n" "int function ( )\n" "{\n" " NotAFunction ( 123 );\n" " return 0 ;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'NotAFunction' object is destroyed immediately.\n", errout.str()); } void testMisusedScopeObjectPicksStruct() { check("struct NotAClass;\n" "bool func ( )\n" "{\n" " NotAClass ( 123 ) ;\n" " return true ;\n" "}\n" ); ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'NotAClass' object is destroyed immediately.\n", errout.str()); } void testMisusedScopeObjectDoesNotPickIf() { check("bool func( int a , int b , int c )\n" "{\n" " if ( a > b ) return c == a ;\n" " return b == a ;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickConstructorDeclaration() { check("class Something : public SomethingElse\n" "{\n" "public:\n" "~Something ( ) ;\n" "Something ( ) ;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickFunctor() { check("class IncrementFunctor\n" "{\n" "public:\n" " void operator()(int &i)\n" " {\n" " ++i;\n" " }\n" "};\n" "\n" "int main()\n" "{\n" " int a = 1;\n" " IncrementFunctor()(a);\n" " return a;\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickLocalClassConstructors() { check("void f() {\n" " class Foo {\n" " Foo() { }\n" " Foo(int a) { }\n" " Foo(int a, int b) { }\n" " };\n" " Foo();\n" " do_something();\n" "}\n" ); ASSERT_EQUALS("[test.cpp:7]: (style) Instance of 'Foo' object is destroyed immediately.\n", errout.str()); } void testMisusedScopeObjectDoesNotPickUsedObject() { check("struct Foo {\n" " void bar() {\n" " }\n" "};\n" "\n" "void fn() {\n" " Foo().bar();\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectDoesNotPickPureC() { // Ticket #2352 const char code[] = "struct cb_watch_bool {\n" " int a;\n" "};\n" "\n" "void f()\n" "{\n" " cb_watch_bool();\n" " do_something();\n" "}\n"; check(code, "test.cpp"); ASSERT_EQUALS("[test.cpp:7]: (style) Instance of 'cb_watch_bool' object is destroyed immediately.\n", errout.str()); check(code, "test.c"); ASSERT_EQUALS("", errout.str()); // Ticket #2639 check("struct stat { int a; int b; };\n" "void stat(const char *fn, struct stat *);\n" "\n" "void foo() {\n" " stat(\"file.txt\", &st);\n" " do_something();\n" "}"); ASSERT_EQUALS("",errout.str()); } void testMisusedScopeObjectDoesNotPickNestedClass() { const char code[] = "class ios_base {\n" "public:\n" " class Init {\n" " public:\n" " };\n" "};\n" "class foo {\n" "public:\n" " foo();\n" " void Init(int);\n" "};\n" "foo::foo() {\n" " Init(0);\n" " do_something();\n" "}\n"; check(code, "test.cpp"); ASSERT_EQUALS("", errout.str()); } void testMisusedScopeObjectInConstructor() { const char code[] = "class Foo {\n" "public:\n" " Foo(char x) {\n" " Foo(x, 0);\n" " do_something();\n" " }\n" " Foo(char x, int y) { }\n" "};\n"; check(code, "test.cpp"); ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'Foo' object is destroyed immediately.\n", errout.str()); } void testMisusedScopeObjectNoCodeAfter() { check("class Foo {};\n" "void f() {\n" " Foo();\n" // No code after class => don't warn "}", "test.cpp"); ASSERT_EQUALS("", errout.str()); } void trac2084() { check("void f()\n" "{\n" " struct sigaction sa;\n" "\n" " { sigaction(SIGHUP, &sa, 0); };\n" " { sigaction(SIGINT, &sa, 0); };\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void trac2071() { check("void f() {\n" " struct AB {\n" " AB(int a) { }\n" " };\n" "\n" " const AB ab[3] = { AB(0), AB(1), AB(2) };\n" "}\n" ); ASSERT_EQUALS("", errout.str()); } void clarifyCalculation() { check("int f(char c) {\n" " return 10 * (c == 0) ? 1 : 2;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '*' and '?'.\n", errout.str()); check("void f(char c) {\n" " printf(\"%i\", 10 * (c == 0) ? 1 : 2);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '*' and '?'.\n", errout.str()); check("void f() {\n" " return (2*a)?b:c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char c) {\n" " printf(\"%i\", 1 + 1 ? 1 : 2);\n" // "1+1" is simplified away "}",nullptr,false,false,false); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '+' and '?'.\n", errout.str()); check("void f() {\n" " std::cout << x << 1 ? 2 : 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '<<' and '?'.\n", errout.str()); check("void f() {\n" " int ab = a - b ? 2 : 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '-' and '?'.\n", errout.str()); check("void f() {\n" " int ab = a | b ? 2 : 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '|' and '?'.\n", errout.str()); // ticket #195 check("int f(int x, int y) {\n" " return x >> ! y ? 8 : 2;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '>>' and '?'.\n", errout.str()); check("int f() {\n" " return shift < sizeof(int64_t)*8 ? 1 : 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() { a = *p ? 1 : 2; }"); ASSERT_EQUALS("", errout.str()); } void clarifyStatement() { check("char* f(char* c) {\n" " *c++;\n" " return c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("char* f(char** c) {\n" " *c[5]--;\n" " return *c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("void f(Foo f) {\n" " *f.a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("void f(Foo f) {\n" " *f.a[5].v[3]++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("void f(Foo f) {\n" " *f.a(1, 5).v[x + y]++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("char* f(char* c) {\n" " (*c)++;\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(char* c) {\n" " bar(*c++);\n" "}"); ASSERT_EQUALS("", errout.str()); check("char*** f(char*** c) {\n" " ***c++;\n" " return c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("char** f(char*** c) {\n" " **c[5]--;\n" " return **c;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", errout.str()); check("char*** f(char*** c) {\n" " (***c)++;\n" " return c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void *f(char** c) {\n" " bar(**c++);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void *f(char* p) {\n" " for (p = path; *p++;) ;\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateBranch() { check("void f(int a, int &b) {\n" " if (a)\n" " b = 1;\n" " else\n" " b = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a) {\n" " if (a == 1)\n" " b = 2;\n" " else\n" " b = 2;\n" " } else\n" " b = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("void f(int a, int &b) {\n" " if (a == 1)\n" " b = 1;\n" " else {\n" " if (a)\n" " b = 2;\n" " else\n" " b = 2;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("int f(int signed, unsigned char value) {\n" " int ret;\n" " if (signed)\n" " ret = (signed char)value;\n" // cast must be kept so the simplifications and verification is skipped " else\n" " ret = (unsigned char)value;\n" " return ret;\n" "}", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (b)\n" " __asm__(\"mov ax, bx\");\n" " else\n" " __asm__(\"mov bx, bx\");\n" "}"); ASSERT_EQUALS("", errout.str()); // #3407 check("void f() {\n" " if (b)\n" " __asm__(\"mov ax, bx\");\n" " else\n" " __asm__(\"mov ax, bx\");\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); } void duplicateBranch1() { // tests inspired by http://www.viva64.com/en/b/0149/ ( Comparison between PVS-Studio and cppcheck ) // Errors detected in Quake 3: Arena by PVS-Studio: Fragment 2 check("void f()\n" "{\n" " if (front < 0)\n" " frac = front/(front-back);\n" " else\n" " frac = front/(front-back);\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("void f()\n" "{\n" " if (front < 0)\n" " { frac = front/(front-back);}\n" " else\n" " frac = front/((front-back));\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); // No message about empty branches (#5354) check("void f()\n" "{\n" " if (front < 0)\n" " {}\n" " else\n" " {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateBranch2() { checkP("#define DOSTUFF1 ;\n" "#define DOSTUFF2 ;\n" "void f(int x) {\n" // #4329 " if (x)\n" " DOSTUFF1\n" " else\n" " DOSTUFF2\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateBranch3() { check("void f(bool b, int i) {\n" " int j = i;\n" " if (b) {\n" " x = i;\n" " } else {\n" " x = j;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout.str()); check("void f(bool b, int i) {\n" " int j = i;\n" " i++;\n" " if (b) {\n" " x = i;\n" " } else {\n" " x = j;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression1() { check("void foo(int a) {\n" " if (a == a) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("void fun(int b) {\n" " return a && a ||\n" " b == b &&\n" " d > d &&\n" " e < e &&\n" " f ;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n" "[test.cpp:3]: (style) Same expression on both sides of '=='.\n" "[test.cpp:4]: (style) Same expression on both sides of '>'.\n" "[test.cpp:5]: (style) Same expression on both sides of '<'.\n", errout.str()); check("void foo() {\n" " return a && a;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo() {\n" " a = b && b;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo(int b) {\n" " f(a,b == b);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("void foo(int b) {\n" " f(b == b, a);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("void foo() {\n" " if (x!=2 || x!=2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo(int a, int b) {\n" " if ((a < b) && (b > a)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because 'aa' represent the same value.\n", errout.str()); check("void foo(int a, int b) {\n" " if ((a <= b) && (b >= a)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because 'a<=b' and 'b>=a' represent the same value.\n", errout.str()); check("void foo() {\n" " if (x!=2 || y!=3 || x!=2) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (x!=2 && (x=y) && x!=2) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (a && b || a && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (a && b || b && c) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (a && b | b && c) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|'.\n", errout.str()); check("void foo() {\n" " if ((a + b) | (a + b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|'.\n", errout.str()); check("void foo() {\n" " if ((a | b) & (a | b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&'.\n", errout.str()); check("void foo(int a, int b) {\n" " if ((a | b) == (a | b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("void foo() {\n" " if (a1[a2[c & 0xff] & 0xff]) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void d(const char f, int o, int v)\n" "{\n" " if (((f=='R') && (o == 1) && ((v < 2) || (v > 99))) ||\n" " ((f=='R') && (o == 2) && ((v < 2) || (v > 99))) ||\n" " ((f=='T') && (o == 2) && ((v < 200) || (v > 9999)))) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int x) { return x+x; }"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { while (x+=x) ; }"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (a && b && b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo() {\n" " if (a || b || b) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (a / 1000 / 1000) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo(int i) {\n" " return i/i;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '/'.\n", errout.str()); check("void foo() {\n" " if (a << 1 << 1) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() { return !!y; }"); // No FP ASSERT_EQUALS("", errout.str()); // make sure there are not "same expression" fp when there are different casts check("void f(long x) { if ((int32_t)x == (int64_t)x) {} }", nullptr, // filename false, // experimental false, // inconclusive false, // runSimpleChecks false, // verbose nullptr // settings ); ASSERT_EQUALS("", errout.str()); // make sure there are not "same expression" fp when there are different ({}) expressions check("void f(long x) { if (({ 1+2; }) == ({3+4;})) {} }"); ASSERT_EQUALS("", errout.str()); // #5535: Reference named like its type check("void foo() { UMSConfig& UMSConfig = GetUMSConfiguration(); }"); ASSERT_EQUALS("[test.cpp:1]: (style) Variable 'UMSConfig' can be declared with const\n", errout.str()); // #3868 - false positive (same expression on both sides of |) check("void f(int x) {\n" " a = x ? A | B | C\n" " : A | B;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const Bar &bar) {\n" " bool a = bar.isSet() && bar->isSet();\n" " bool b = bar.isSet() && bar.isSet();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void foo(int a, int b) {\n" " if ((b + a) | (a + b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|' because 'b+a' and 'a+b' represent the same value.\n", errout.str()); check("void foo(const std::string& a, const std::string& b) {\n" " return a.find(b+\"&\") || a.find(\"&\"+b);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a, int b) {\n" " if ((b > a) | (a > b)) {}\n" // > is not commutative "}"); ASSERT_EQUALS("", errout.str()); check("void foo(double a, double b) {\n" " if ((b + a) > (a + b)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) The expression 'b+a > a+b' is always false because 'b+a' and 'a+b' represent the same value.\n", errout.str()); check("void f(int x) {\n" " if ((x == 1) && (x == 0x00000001))\n" " a++;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("void f() {\n" " enum { Four = 4 };\n" " if (Four == 4) {}" "}", nullptr, false, true, false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum { Four = 4 };\n" " static_assert(Four == 4, \"\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum { Four = 4 };\n" " static_assert(4 == Four, \"\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum { FourInEnumOne = 4 };\n" " enum { FourInEnumTwo = 4 };\n" " if (FourInEnumOne == FourInEnumTwo) {}\n" "}", nullptr, false, true, false); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " enum { FourInEnumOne = 4 };\n" " enum { FourInEnumTwo = 4 };\n" " static_assert(FourInEnumOne == FourInEnumTwo, \"\");\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int a, int b) {\n" " if (sizeof(a) == sizeof(a)) { }\n" " if (sizeof(a) == sizeof(b)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); check("float bar(int) __attribute__((pure));\n" "char foo(int) __attribute__((pure));\n" "int test(int a, int b) {\n" " if (bar(a) == bar(a)) { }\n" " if (unknown(a) == unknown(a)) { }\n" " if (foo(a) == foo(a)) { }\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Same expression on both sides of '=='.\n", errout.str()); } void duplicateExpression2() { // check if float is NaN or Inf check("int f(long double ldbl, double dbl, float flt) {\n" // ticket #2730 " if (ldbl != ldbl) have_nan = 1;\n" " if (!(dbl == dbl)) have_nan = 1;\n" " if (flt != flt) have_nan = 1;\n" " return have_nan;\n" "}"); ASSERT_EQUALS("", errout.str()); check("float f(float x) { return x-x; }"); // ticket #4485 (Inf) ASSERT_EQUALS("", errout.str()); check("float f(float x) { return (X double)x == (X double)x; }", nullptr, false, false, false); ASSERT_EQUALS("", errout.str()); check("struct X { float f; };\n" "float f(struct X x) { return x.f == x.f; }"); ASSERT_EQUALS("", errout.str()); check("struct X { int i; };\n" "int f(struct X x) { return x.i == x.i; }"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout.str()); // #5284 - when type is unknown, assume it's float check("int f() { return x==x; }"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression3() { Settings settings; const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" ""; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settings.library.load(doc); check("void foo() {\n" " if (x() || x()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct A {\n" " void foo() const;\n" " bool bar() const;\n" "};\n" "void A::foo() const {\n" " if (bar() && bar()) {}\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("struct A {\n" " void foo();\n" " bool bar();\n" " bool bar() const;\n" "};\n" "void A::foo() {\n" " if (bar() && bar()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("class B {\n" " void bar(int i);\n" "};\n" "class A {\n" " void bar(int i) const;\n" "};\n" "void foo() {\n" " B b;\n" " A a;\n" " if (b.bar(1) && b.bar(1)) {}\n" " if (a.bar(1) && a.bar(1)) {}\n" "}"); ASSERT_EQUALS("[test.cpp:11]: (style) Same expression on both sides of '&&'.\n", errout.str()); check("class D { void strcmp(); };\n" "void foo() {\n" " D d;\n" " if (d.strcmp() && d.strcmp()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if ((mystrcmp(a, b) == 0) || (mystrcmp(a, b) == 0)) {}\n" "}", "test.cpp", false, false, true, false, &settings); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void GetValue() { return rand(); }\n" "void foo() {\n" " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void __attribute__((const)) GetValue() { return X; }\n" "void foo() {\n" " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void GetValue() __attribute__((const));\n" "void GetValue() { return X; }\n" "void foo() {\n" " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (str == \"(\" || str == \"(\") {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); check("void foo() {\n" " if (bar(a) && !strcmp(a, b) && bar(a) && !strcmp(a, b)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #5334 check("void f(C *src) {\n" " if (x(src) || x(src))\n" " a++;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(A *src) {\n" " if (dynamic_cast(src) || dynamic_cast(src)) {}\n" "}\n", "test.cpp", false, false, false); // don't run simplifications ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout.str()); // #5819 check("Vector func(Vector vec1) {\n" " return fabs(vec1 & vec1 & vec1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("Vector func(int vec1) {\n" " return fabs(vec1 & vec1 & vec1);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&'.\n", errout.str()); } void duplicateExpression4() { check("void foo() {\n" " if (*a++ != b || *a++ != b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (*a-- != b || *a-- != b) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // assignment check("void f() {\n" " while (*(a+=2)==*(b+=2) && *(a+=2)==*(b+=2)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression5() { // #3749 - macros with same values check("void f() {\n" " if ($a == $a) { }\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression6() { // #4639 check("float IsNan(float value) { return !(value == value); }\n" "double IsNan(double value) { return !(value == value); }\n" "long double IsNan(long double value) { return !(value == value); }"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression7() { check("void f() {\n" " const int i = sizeof(int);\n" " if ( i != sizeof (int)){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'i != sizeof(int)' is always false because 'i' and 'sizeof(int)' represent the same value.\n", errout.str()); check("void f() {\n" " const int i = sizeof(int);\n" " if ( sizeof (int) != i){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'sizeof(int) != i' is always false because 'sizeof(int)' and 'i' represent the same value.\n", errout.str()); check("void f(int a = 1) { if ( a != 1){}}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 1;\n" " if ( a != 1){} \n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'a != 1' is always false.\n", errout.str()); check("void f() {\n" " int a = 1;\n" " int b = 1;\n" " if ( a != b){} \n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:4]: (style) The expression 'a != b' is always false because 'a' and 'b' represent the same value.\n", errout.str()); check("void f() {\n" " int a = 1;\n" " int b = a;\n" " if ( a != b){} \n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) The expression 'a != b' is always false because 'a' and 'b' represent the same value.\n", errout.str()); check("void use(int);\n" "void f() {\n" " int a = 1;\n" " int b = 1;\n" " use(b);\n" " if ( a != 1){} \n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) The expression 'a != 1' is always false.\n", errout.str()); check("void use(int);\n" "void f() {\n" " int a = 1;\n" " use(a);\n" " a = 2;\n" " if ( a != 1){} \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void use(int);\n" "void f() {\n" " int a = 2;\n" " use(a);\n" " a = 1;\n" " if ( a != 1){} \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("const int a = 1;\n" " void f() {\n" " if ( a != 1){} \n" "}\n"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The expression 'a != 1' is always false.\n", errout.str()); check("int a = 1;\n" " void f() {\n" " if ( a != 1){} \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static const int a = 1;\n" " if ( a != 1){} \n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'a != 1' is always false.\n", errout.str()); check("void f() {\n" " static int a = 1;\n" " if ( a != 1){} \n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 1;\n" " if ( a != 1){\n" " a++;\n" " }}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'a != 1' is always false.\n", errout.str()); check("void f(int b) {\n" " int a = 1;\n" " while (b) {\n" " if ( a != 1){}\n" " a++;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool f(bool a, bool b) {\n" " const bool c = a;\n" " return a && b && c;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '&&' because 'a' and 'c' represent the same value.\n", errout.str()); // 6906 check("void f(const bool b) {\n" " const bool b1 = !b;\n" " if(!b && b1){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '&&' because '!b' and 'b1' represent the same value.\n", errout.str()); // 7284 check("void f(void) {\n" " if (a || !!a) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'a' and '!!a' represent the same value.\n", errout.str()); // 8205 check("void f(int x) {\n" " int Diag = 0;\n" " switch (x) {\n" " case 12:\n" " if (Diag==0) {}\n" " break;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The expression 'Diag == 0' is always true.\n", errout.str()); } void duplicateExpression8() { check("void f() {\n" " int a = 1;\n" " int b = a;\n" " a = 2;\n" " if ( b != a){}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int * a, int i) { int b = a[i]; a[i] = 2; if ( b != a[i]){}}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int * a, int i) { int b = *a; *a = 2; if ( b != *a){}}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int f() const; };\n" "A g();\n" "void foo() {\n" " for (const A x = A();;) {\n" " const int a = x.f();\n" " x = g();\n" " if (x.f() == a) break;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int f(int i);\n" "struct A {\n" " enum E { B, C };\n" " bool f(E);\n" "};\n" "void foo() {\n" " A a;\n" " const bool x = a.f(A::B);\n" " const bool y = a.f(A::C);\n" " if(!x && !y) return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void foo() { \n" " const bool x = a.f(A::B);\n" " const bool y = a.f(A::C);\n" " if (!x && !y) return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool * const b);\n" "void foo() { \n" " bool x = true;\n" " bool y = true;\n" " f(&x);\n" " if (!x && !y) return;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const int a = {};\n" " if(a == 1) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("volatile const int var = 42;\n" "void f() { if(var == 42) {} }\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a = 0;\n" " struct b c;\n" " c.a = &a;\n" " g(&c);\n" " if (a == 0) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression9() { // #9320 check("void f() {\n" " uint16_t x = 1000;\n" " uint8_t y = x;\n" " if (x != y) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateExpression10() { // #9485 check("int f() {\n" " const int a = 1;\n" " const int b = a-1;\n" " const int c = a+1;\n" " return c;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateExpressionLoop() { check("void f() {\n" " int a = 1;\n" " while ( a != 1){}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'a != 1' is always false.\n", errout.str()); check("void f() { int a = 1; while ( a != 1){ a++; }}\n"); ASSERT_EQUALS("", errout.str()); check("void f() { int a = 1; for ( int i=0; i < 3 && a != 1; i++){ a++; }}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int b) { int a = 1; while (b) { if ( a != 1){} b++; } a++; } \n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " for(int i = 0; i < 10;) {\n" " if( i != 0 ) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'i != 0' is always false.\n", errout.str()); check("void f() {\n" " for(int i = 0; i < 10;) {\n" " if( i != 0 ) {}\n" " i++;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " for(int i = 0; i < 10;) {\n" " if( i != 0 ) { i++; }\n" " i++;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " for(int i = 0; i < 10;) {\n" " if( i != 0 ) { i++; }\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int i = 0;\n" " while(i < 10) {\n" " if( i != 0 ) {}\n" " i++;\n" " }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int b) {\n" " while (b) {\n" " int a = 1;\n" " if ( a != 1){}\n" " b++;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) The expression 'a != 1' is always false.\n", errout.str()); } void duplicateExpressionTernary() { // #6391 check("void f() {\n" " return A ? x : x;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same expression in both branches of ternary operator.\n", errout.str()); check("int f(bool b, int a) {\n" " const int c = a;\n" " return b ? a : c;\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression in both branches of ternary operator.\n", errout.str()); check("void f() {\n" " return A ? x : z;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(unsigned char c) {\n" " x = y ? (signed char)c : (unsigned char)c;\n" "}"); ASSERT_EQUALS("", errout.str()); check("std::string stringMerge(std::string const& x, std::string const& y) {\n" // #7938 " return ((x > y) ? (y + x) : (x + y));\n" "}"); ASSERT_EQUALS("", errout.str()); // #6426 check("void foo(bool flag) {\n" " bar( (flag) ? ~0u : ~0ul);\n" "}"); ASSERT_EQUALS((_settings.sizeof_int==_settings.sizeof_long)?"[test.cpp:2]: (style) Same value in both branches of ternary operator.\n":"", errout.str()); } void duplicateValueTernary() { check("void f() {\n" " if( a ? (b ? false:false): false ) ;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f1(int a) {return (a == 1) ? (int)1 : 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f2(int a) {return (a == 1) ? (int)1 : (int)1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f3(int a) {return (a == 1) ? 1 : (int)1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f4(int a) {return (a == 1) ? 1 : 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f5(int a) {return (a == (int)1) ? (int)1 : 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f6(int a) {return (a == (int)1) ? (int)1 : (int)1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f7(int a) {return (a == (int)1) ? 1 : (int)1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); check("int f8(int a) {return (a == (int)1) ? 1 : 1; }"); ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout.str()); } void duplicateExpressionTemplate() { // #6930 check("template void f() {\n" " if (I >= 0 && I < 3) {}\n" "}\n" "\n" "static auto a = f<0>();"); ASSERT_EQUALS("", errout.str()); } void oppositeExpression() { check("void f(bool a) { if(a && !a) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '&&'.\n", errout.str()); check("void f(bool a) { if(a != !a) {} }"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout.str()); check("void f(bool a) { if( a == !(a) ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(bool a) { if( a != !(a) ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout.str()); check("void f(bool a) { if( !(a) == a ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(bool a) { if( !(a) != a ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout.str()); check("void f(bool a) { if( !(!a) == !(a) ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(bool a) { if( !(!a) != !(a) ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout.str()); check("void f1(bool a) {\n" " const bool b = a;\n" " if( a == !(b) ) {}\n" " if( b == !(a) ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n" "[test.cpp:2] -> [test.cpp:4]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f2(bool *a) {\n" " const bool b = *a;\n" " if( *a == !(b) ) {}\n" " if( b == !(*a) ) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n" "[test.cpp:2] -> [test.cpp:4]: (style) Opposite expression on both sides of '=='.\n", errout.str()); check("void f(bool a) { a = !a; }"); ASSERT_EQUALS("", errout.str()); check("void f(int a) { if( a < -a ) {}}"); ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '<'.\n", errout.str()); check("void f(int a) { a -= -a; }"); ASSERT_EQUALS("", errout.str()); check("void f(int a) { a = a / (-a); }"); ASSERT_EQUALS("", errout.str()); check("bool f(int i){ return !((i - 1) & i); }"); ASSERT_EQUALS("", errout.str()); check("bool f(unsigned i){ return (x > 0) && (x & (x-1)) == 0; }"); ASSERT_EQUALS("", errout.str()); check("void A::f(bool a, bool c)\n" "{\n" " const bool b = a;\n" " if(c) { a = false; } \n" " if(b && !a) { }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(bool c) {\n" " const bool b = a;\n" " if(c) { a = false; } \n" " if(b && !a) { }\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " bool x = a;\n" " dostuff();\n" " if (x && a) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " const bool b = g();\n" " if (!b && g()) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void duplicateVarExpression() { check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int i = f();\n" " int j = f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; int g() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; int g() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " Foo f2 = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int i = 1 + f();\n" " int j = 1 + f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int i = f() + 1;\n" " int j = 1 + f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int x = f();\n" " int i = x + 1;\n" " int j = f() + 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() __attribute__((pure));\n" "int g() __attribute__((pure));\n" "void test() {\n" " int i = f() + f();\n" " int j = f() + f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("int f(int) __attribute__((pure));\n" "int g(int) __attribute__((pure));\n" "void test() {\n" " int i = f(0);\n" " int j = f(0);\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("int f(int) __attribute__((pure));\n" "int g(int) __attribute__((pure));\n" "void test() {\n" " const int x = 0;\n" " int i = f(0);\n" " int j = f(x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int * p, int * q) {\n" " int i = *p;\n" " int j = *p;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct A { int x; int y; };" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("void test() {\n" " int i = 0;\n" " int j = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " int i = -1;\n" " int j = -1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(int);\n" "void test() {\n" " int i = f(0);\n" " int j = f(1);\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f();\n" "int g();\n" "void test() {\n" " int i = f() || f();\n" " int j = f() && f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo {};\n" "void test() {\n" " Foo i = Foo();\n" " Foo j = Foo();\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo {};\n" "void test() {\n" " Foo i = Foo{};\n" " Foo j = Foo{};\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct Foo { int f() const; float g() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f(); int g(); };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test() {\n" " int i = f();\n" " int j = f();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = ++x;\n" " int j = ++x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = x++;\n" " int j = x++;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = --x;\n" " int j = --x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = x--;\n" " int j = x--;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void test(int x) {\n" " int i = x + 1;\n" " int j = 1 + x;\n" "}"); ASSERT_EQUALS("", errout.str()); } void duplicateVarExpressionUnique() { check("struct SW { int first; };\n" "void foo(SW* x) {\n" " int start = x->first;\n" " int end = x->first;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'start' and 'end'.\n", errout.str()); check("struct SW { int first; };\n" "void foo(SW* x, int i, int j) {\n" " int start = x->first;\n" " int end = x->first;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'start' and 'end'.\n", errout.str()); check("struct Foo { int f() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("void test(int * p) {\n" " int i = *p;\n" " int j = *p;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; int g(int) const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct Foo { int f() const; };\n" "void test() {\n" " Foo f = Foo{};\n" " int i = f.f();\n" " int j = f.f();\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); } void duplicateVarExpressionAssign() { check("struct A { int x; int y; };" "void use(int);\n" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" " use(i);\n" " i = j;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); check("struct A { int x; int y; };" "void use(int);\n" "void test(A a) {\n" " int i = a.x;\n" " int j = a.x;\n" " use(j);\n" " j = i;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout.str()); // Issue #8612 check("struct P\n" "{\n" " void func();\n" " bool operator==(const P&) const;\n" "};\n" "struct X\n" "{\n" " P first;\n" " P second;\n" "};\n" "bool bar();\n" "void baz(const P&);\n" "void foo(const X& x)\n" "{\n" " P current = x.first;\n" " P previous = x.first;\n" " while (true)\n" " {\n" " baz(current);\n" " if (bar() && previous == current)\n" " {\n" " current.func();\n" " }\n" " previous = current;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:16] -> [test.cpp:15]: (style, inconclusive) Same expression used in consecutive assignments of 'current' and 'previous'.\n", errout.str()); } void duplicateVarExpressionCrash() { // Issue #8624 check("struct X {\n" " X();\n" " int f() const;\n" "};\n" "void run() {\n" " X x;\n" " int a = x.f();\n" " int b = x.f();\n" " (void)a;\n" " (void)b;\n" "}\n"); ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:7]: (style, inconclusive) Same expression used in consecutive assignments of 'a' and 'b'.\n", errout.str()); // Issue #8712 check("void f() {\n" " unsigned char d;\n" " d = d % 5;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("template \n" "T f() {\n" " T x = T();\n" "}\n" "int &a = f();\n"); ASSERT_EQUALS("", errout.str()); // Issue #8713 check("class A {\n" " int64_t B = 32768;\n" " P m = MakeP(B);\n" "};\n" "void f() {\n" " uint32_t a = 42;\n" " uint32_t b = uint32_t(A ::B / 1024);\n" " int32_t c = int32_t(a / b);\n" "}\n"); ASSERT_EQUALS("", errout.str()); // Issue #8709 check("a b;\n" "void c() {\n" " switch (d) { case b:; }\n" " double e(b);\n" " if(e <= 0) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void multiConditionSameExpression() { check("void f() {\n" " int val = 0;\n" " if (val < 0) continue;\n" " if ((val > 0)) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'val < 0' is always false.\n" "[test.cpp:2] -> [test.cpp:4]: (style) The expression 'val > 0' is always false.\n", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if ((val > 0)) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'val < 0' is always false.\n" "[test.cpp:2] -> [test.cpp:4]: (style) The expression 'val > 0' is always false.\n", errout.str()); check("void f() {\n" " int val = 0;\n" " if (val < 0) {\n" " if ((val < 0)) {}\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The expression 'val < 0' is always false.\n" "[test.cpp:2] -> [test.cpp:4]: (style) The expression 'val < 0' is always false.\n", errout.str()); check("void f() {\n" " int activate = 0;\n" " int foo = 0;\n" " if (activate) {}\n" " else if (foo) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void checkSignOfUnsignedVariable() { check("void foo() {\n" " for(unsigned char i = 10; i >= 0; i--) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'i' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(bool b) {\n" " for(unsigned int i = 10; b || i >= 0; i--) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'i' can't be negative so it is unnecessary to test it.\n", errout.str()); { const char code[] = "void foo(unsigned int x) {\n" " if (x < 0) {}\n" "}"; check(code, nullptr, false, false, true, false); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check(code, nullptr, false, false, true, true); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); } check("void foo(unsigned int x) {\n" " if (x < 0u) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x) {\n" " if (x < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); { const char code[] = "void foo(unsigned x) {\n" " int y = 0;\n" " if (x < y) {}\n" "}"; check(code, nullptr, false, false, true, false); ASSERT_EQUALS("[test.cpp:3]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check(code, nullptr, false, false, true, true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); } check("void foo(unsigned x) {\n" " int y = 0;\n" " if (b)\n" " y = 1;\n" " if (x < y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x) {\n" " if (0 > x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(unsigned int x) {\n" " if (0UL > x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x) {\n" " if (0 > x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x) {\n" " if (x >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(unsigned int x, unsigned y) {\n" " if (x - y >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x-y' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(unsigned int x) {\n" " if (x >= 0ull) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x) {\n" " if (x >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x) {\n" " if (0 <= x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(unsigned int x) {\n" " if (0ll <= x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x) {\n" " if (0 <= x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (x < 0 && y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (x < 0 && y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (0 > x && y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (0 > x && y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (x >= 0 && y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (x >= 0 && y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (y && x < 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (y && x < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (y && 0 > x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (y && 0 > x) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (y && x >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (y && x >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (x < 0 || y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (x < 0 || y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (0 > x || y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (0 > x || y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(unsigned int x, bool y) {\n" " if (x >= 0 || y) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout.str()); check("void foo(int x, bool y) {\n" " if (x >= 0 || y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); // #3233 - FP when template is used (template parameter is numeric constant) { const char code[] = "template void foo(unsigned int x) {\n" " if (x <= n);\n" "}\n" "foo<0>();"; check(code, nullptr, false, false); ASSERT_EQUALS("", errout.str()); check(code, nullptr, false, true); ASSERT_EQUALS("", errout.str()); } check("template void foo(unsigned int x) {\n" "if (x <= 0);\n" "}\n"); ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout.str()); // #8836 check("uint32_t value = 0xFUL;\n" "void f() {\n" " if (value < 0u)\n" " {\n" " value = 0u;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Checking if unsigned expression 'value' is less than zero.\n", errout.str()); // #9040 Settings settings1; settings1.platform(Settings::Win64); check("using BOOL = unsigned;\n" "int i;\n" "bool f() {\n" " return i >= 0;\n" "}\n", &settings1); ASSERT_EQUALS("", errout.str()); } void checkSignOfPointer() { check("void foo(int* x) {\n" " if (x >= 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); { const char code[] = "void foo(int* x) {\n" " int y = 0;\n" " if (x >= y) {}\n" "}"; check(code, nullptr, false, false, true, false); ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); check(code, nullptr, false, false, true, true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); } check("void foo(int* x) {\n" " if (*x >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int* x) {\n" " if (x < 0) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); { const char code[] = "void foo(int* x) {\n" " unsigned y = 0u;\n" " if (x < y) {}\n" "}"; check(code, nullptr, false, false, true, false); ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); check(code, nullptr, false, false, true, true); ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); } check("void foo(int* x) {\n" " if (*x < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int* x, int* y) {\n" " if (x - y < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int* x, int* y) {\n" " if (x - y <= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int* x, int* y) {\n" " if (x - y > 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int* x, int* y) {\n" " if (x - y >= 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 <= x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first) {\n" " if (first.ptr >= 0) {} \n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if((first.ptr - second.ptr) >= 0) {} \n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first) {\n" " if((first.ptr) >= 0) {} \n" "}"); ASSERT_EQUALS("[test.cpp:5]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(0 <= first.ptr - second.ptr) {} \n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(0 <= (first.ptr - second.ptr)) {} \n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(first.ptr - second.ptr < 0) {} \n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if((first.ptr - second.ptr) < 0) {} \n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(0 > first.ptr - second.ptr) {} \n" "}"); ASSERT_EQUALS("", errout.str()); check("struct S {\n" " int* ptr;\n" "};\n" "void foo(S* first, S* second) {\n" " if(0 > (first.ptr - second.ptr)) {} \n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int* x) {\n" " if (0 <= x[0]) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 <= x.y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 <= x->y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x, Bar* y) {\n" " if (0 <= x->y - y->y ) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 > x) {}\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); check("void foo(int* x) {\n" " if (0 > x[0]) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 > x.y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(Bar* x) {\n" " if (0 > x->y) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " int (*t)(void *a, void *b);\n" " if (t(a, b) < 0) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " int (*t)(void *a, void *b);\n" " if (0 > t(a, b)) {}\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct object_info { int *typep; };\n" "void packed_object_info(struct object_info *oi) {\n" " if (oi->typep < 0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); check("struct object_info { int typep[10]; };\n" "void packed_object_info(struct object_info *oi) {\n" " if (oi->typep < 0);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout.str()); check("struct object_info { int *typep; };\n" "void packed_object_info(struct object_info *oi) {\n" " if (*oi->typep < 0);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkForSuspiciousSemicolon1() { check("void foo() {\n" " for(int i = 0; i < 10; ++i);\n" "}"); ASSERT_EQUALS("", errout.str()); // Empty block check("void foo() {\n" " for(int i = 0; i < 10; ++i); {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Suspicious use of ; at the end of 'for' statement.\n", errout.str()); check("void foo() {\n" " while (!quit); {\n" " do_something();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Suspicious use of ; at the end of 'while' statement.\n", errout.str()); } void checkForSuspiciousSemicolon2() { check("void foo() {\n" " if (i == 1); {\n" " do_something();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Suspicious use of ; at the end of 'if' statement.\n", errout.str()); // Seen this in the wild check("void foo() {\n" " if (Match());\n" " do_something();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (Match());\n" " else\n" " do_something();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (i == 1)\n" " ;\n" " {\n" " do_something();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " if (i == 1);\n" "\n" " {\n" " do_something();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkInvalidFree() { check("void foo(char *p) {\n" " char *a; a = malloc(1024);\n" " free(a + 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = malloc(1024);\n" " free(a - 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = malloc(1024);\n" " free(10 + a);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char[1024];\n" " delete[] (a + 10);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " delete a + 10;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " bar(a);\n" " delete a + 10;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " char *b; b = new char;\n" " bar(a);\n" " delete a + 10;\n" " delete b + 10;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " char *b; b = new char;\n" " bar(a, b);\n" " delete a + 10;\n" " delete b + 10;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(char *p) {\n" " char *a; a = new char;\n" " bar()\n" " delete a + 10;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout.str()); check("void foo(size_t xx) {\n" " char *ptr; ptr = malloc(42);\n" " ptr += xx;\n" " free(ptr + 1 - xx);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout.str()); check("void foo(size_t xx) {\n" " char *ptr; ptr = malloc(42);\n" " std::cout << ptr;\n" " ptr = otherPtr;\n" " free(otherPtr - xx - 1);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkRedundantCopy() { check("const std::string& getA(){static std::string a;return a;}\n" "void foo() {\n" " const std::string a = getA();\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout.str()); check("class A{public:A(){}};\n" "const A& getA(){static A a;return a;}\n" "int main()\n" "{\n" " const A a = getA();\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout.str()); check("const int& getA(){static int a;return a;}\n" "int main()\n" "{\n" " const int a = getA();\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("const int& getA(){static int a;return a;}\n" "int main()\n" "{\n" " int getA = 0;\n" " const int a = getA + 3;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4]: (style) Local variable \'getA\' shadows outer function\n", errout.str()); check("class A{public:A(){}};\n" "const A& getA(){static A a;return a;}\n" "int main()\n" "{\n" " const A a(getA());\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout.str()); check("const int& getA(){static int a;return a;}\n" "int main()\n" "{\n" " const int a(getA());\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A{\n" "public:A(int a=0){_a = a;}\n" "A operator+(const A & a){return A(_a+a._a);}\n" "private:int _a;};\n" "const A& getA(){static A a;return a;}\n" "int main()\n" "{\n" " const A a = getA() + 1;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class A{\n" "public:A(int a=0){_a = a;}\n" "A operator+(const A & a){return A(_a+a._a);}\n" "private:int _a;};\n" "const A& getA(){static A a;return a;}\n" "int main()\n" "{\n" " const A a(getA()+1);\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #5190 - FP when creating object with constructor that takes a reference check("class A {};\n" "class B { B(const A &a); };\n" "const A &getA();\n" "void f() {\n" " const B b(getA());\n" "}"); ASSERT_EQUALS("", errout.str()); // #5618 const char* code5618 = "class Token {\n" "public:\n" " const std::string& str();\n" "};\n" "void simplifyArrayAccessSyntax() {\n" " for (Token *tok = list.front(); tok; tok = tok->next()) {\n" " const std::string temp = tok->str();\n" " tok->str(tok->strAt(2));\n" " }\n" "}"; check(code5618, nullptr, false, true); TODO_ASSERT_EQUALS("", "[test.cpp:7]: (performance, inconclusive) Use const reference for 'temp' to avoid unnecessary data copying.\n", errout.str()); check(code5618, nullptr, false, false); ASSERT_EQUALS("", errout.str()); // #5890 - crash: wesnoth desktop_util.cpp / unicode.hpp check("typedef std::vector X;\n" "X f(const X &in) {\n" " const X s = f(in);\n" " return f(s);\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkNegativeShift() { check("void foo()\n" "{\n" " int a; a = 123;\n" " (void)(a << -1);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str()); check("void foo()\n" "{\n" " int a; a = 123;\n" " (void)(a >> -1);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str()); check("void foo()\n" "{\n" " int a; a = 123;\n" " a <<= -1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str()); check("void foo()\n" "{\n" " int a; a = 123;\n" " a >>= -1;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str()); check("void foo()\n" "{\n" " std::cout << -1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << a << -1 ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo()\n" "{\n" " std::cout << 3 << -1 ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " x = (-10+2) << 3;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) Shifting a negative value is technically undefined behaviour\n", errout.str()); check("x = y ? z << $-1 : 0;\n"); ASSERT_EQUALS("", errout.str()); // Negative LHS check("const int x = -1 >> 2;"); ASSERT_EQUALS("[test.cpp:1]: (portability) Shifting a negative value is technically undefined behaviour\n", errout.str()); // #6383 - unsigned type check("const int x = (unsigned int)(-1) >> 2;"); ASSERT_EQUALS("", errout.str()); // #7814 - UB happening in valueflowcode when it tried to compute shifts. check("int shift1() { return 1 >> -1 ;}\n" "int shift2() { return 1 << -1 ;}\n" "int shift3() { return -1 >> 1 ;}\n" "int shift4() { return -1 << 1 ;}\n"); ASSERT_EQUALS("[test.cpp:1]: (error) Shifting by a negative value is undefined behaviour\n" "[test.cpp:2]: (error) Shifting by a negative value is undefined behaviour\n" "[test.cpp:3]: (portability) Shifting a negative value is technically undefined behaviour\n" "[test.cpp:4]: (portability) Shifting a negative value is technically undefined behaviour\n", errout.str()); } void incompleteArrayFill() { check("void f() {\n" " int a[5];\n" " memset(a, 123, 5);\n" " memcpy(a, b, 5);\n" " memmove(a, b, 5);\n" "}"); ASSERT_EQUALS(// TODO "[test.cpp:4] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n" "[test.cpp:3]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n" "[test.cpp:4]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memcpy()' with 'sizeof(*a)'?\n" "[test.cpp:5]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memmove()' with 'sizeof(*a)'?\n", errout.str()); check("void f() {\n" " Foo* a[5];\n" " memset(a, 'a', 5);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n", errout.str()); check("class Foo {int a; int b;};\n" "void f() {\n" " Foo a[5];\n" " memset(a, 'a', 5);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n", "", errout.str()); check("void f() {\n" " Foo a[5];\n" // Size of foo is unknown " memset(a, 'a', 5);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char a[5];\n" " memset(a, 'a', 5);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int a[5];\n" " memset(a+15, 'a', 5);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " bool a[5];\n" " memset(a, false, 5);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (portability, inconclusive) Array 'a' might be filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n", errout.str()); } void redundantVarAssignment() { setMultiline(); // Simple tests check("void f(int i) {\n" " i = 1;\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:2:note:i is assigned\n" "test.cpp:3:note:i is overwritten\n", errout.str()); // non-local variable => only show warning when inconclusive is used check("int i;\n" "void f() {\n" " i = 1;\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i is assigned\n" "test.cpp:4:note:i is overwritten\n", errout.str()); check("void f() {\n" " int i;\n" " i = 1;\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i is assigned\n" "test.cpp:4:note:i is overwritten\n", errout.str()); check("void f() {\n" " static int i;\n" " i = 1;\n" " i = 1;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("void f() {\n" " int i[10];\n" " i[2] = 1;\n" " i[2] = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i[2]' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i[2] is assigned\n" "test.cpp:4:note:i[2] is overwritten\n", errout.str()); check("void f(int x) {\n" " int i[10];\n" " i[x] = 1;\n" " x=1;\n" " i[x] = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(const int x) {\n" " int i[10];\n" " i[x] = 1;\n" " i[x] = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i[x]' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i[x] is assigned\n" "test.cpp:4:note:i[x] is overwritten\n", errout.str()); // Testing different types check("void f() {\n" " Foo& bar = foo();\n" " bar = x;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " Foo& bar = foo();\n" " bar = x;\n" " bar = y;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("void f() {\n" " Foo& bar = foo();\n" // #4425. bar might refer to something global, etc. " bar = y();\n" " foo();\n" " bar = y();\n" "}"); ASSERT_EQUALS("", errout.str()); // Tests with function call between assignment check("void f(int i) {\n" " i = 1;\n" " bar();\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:2:note:i is assigned\n" "test.cpp:4:note:i is overwritten\n", errout.str()); check("int i;\n" "void f() {\n" " i = 1;\n" " bar();\n" // Global variable might be accessed in bar() " i = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " static int i;\n" " i = 1;\n" " bar();\n" // bar() might call f() recursively. This could be a false positive in more complex examples (when value of i is used somewhere. See #4229) " i = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int i;\n" " i = 1;\n" " bar();\n" " i = 1;\n" "}"); ASSERT_EQUALS("test.cpp:5:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:i is assigned\n" "test.cpp:5:note:i is overwritten\n", errout.str()); check("void bar(int i) {}\n" "void f(int i) {\n" " i = 1;\n" " bar(i);\n" // Passed as argument " i = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " Foo bar = foo();\n" " bar();\n" // #5568. operator() called " bar = y();\n" "}"); ASSERT_EQUALS("", errout.str()); // Branch tests check("void f(int i) {\n" " i = 1;\n" " if(x)\n" " i = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int i) {\n" " if(x)\n" " i = 0;\n" " i = 1;\n" " i = 2;\n" "}"); ASSERT_EQUALS("test.cpp:5:style:Variable 'i' is reassigned a value before the old one has been used.\n" "test.cpp:4:note:i is assigned\n" "test.cpp:5:note:i is overwritten\n", errout.str()); // #4513 check("int x;\n" "int g() {\n" " return x*x;\n" "}\n" "void f() {\n" " x = 2;\n" " x = g();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int g() {\n" " return x*x;\n" "}\n" "void f(int x) {\n" " x = 2;\n" " x = g();\n" "}"); ASSERT_EQUALS("test.cpp:6:style:Variable 'x' is reassigned a value before the old one has been used.\n" "test.cpp:5:note:x is assigned\n" "test.cpp:6:note:x is overwritten\n", errout.str()); check("void f() {\n" " Foo& bar = foo();\n" " bar = x;\n" " bar = y();\n" "}"); ASSERT_EQUALS("", errout.str()); check("class C {\n" " int x;\n" " void g() { return x * x; }\n" " void f();\n" "};\n" "\n" "void C::f() {\n" " x = 2;\n" " x = g();\n" "}"); ASSERT_EQUALS("", errout.str()); check("class C {\n" " int x;\n" " void g() { return x*x; }\n" " void f(Foo z);\n" "};\n" "\n" "void C::f(Foo z) {\n" " x = 2;\n" " x = z.g();\n" "}"); ASSERT_EQUALS("", errout.str()); // ({ }) check("void f() {\n" " int x;\n" " x = 321;\n" " x = ({ asm(123); })\n" "}"); ASSERT_EQUALS("", errout.str()); // from #3103 (avoid a false negative) check("int foo(){\n" " int x;\n" " x = 1;\n" " x = 1;\n" " return x + 1;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'x' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:x is assigned\n" "test.cpp:4:note:x is overwritten\n", errout.str()); // from #3103 (avoid a false positive) check("int foo(){\n" " int x;\n" " x = 1;\n" " if (y)\n" // <-- cppcheck does not know anything about 'y' " x = 2;\n" " return x + 1;\n" "}"); ASSERT_EQUALS("", errout.str()); // initialization, assignment with 0 check("void f() {\n" // Ticket #4356 " int x = 0;\n" // <- ignore initialization with 0 " x = 3;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " state_t *x = NULL;\n" " x = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " state_t *x;\n" " x = NULL;\n" " x = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); check("int foo() {\n" // #4420 " int x;\n" " bar(++x);\n" " x = 5;\n" " return bar(x);\n" "}"); ASSERT_EQUALS("", errout.str()); // struct member.. check("struct AB { int a; int b; };\n" "\n" "int f() {\n" " struct AB ab;\n" " ab.a = 1;\n" " ab.a = 2;\n" " return ab.a;\n" "}"); ASSERT_EQUALS("test.cpp:6:style:Variable 'ab.a' is reassigned a value before the old one has been used.\n" "test.cpp:5:note:ab.a is assigned\n" "test.cpp:6:note:ab.a is overwritten\n", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f() {\n" " struct AB ab;\n" " ab.a = 1;\n" " ab = do_something();\n" " return ab.a;\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f() {\n" " struct AB ab;\n" " ab.a = 1;\n" " do_something(&ab);\n" " ab.a = 2;\n" " return ab.a;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f(DO_SOMETHING do_something) {\n" " struct AB ab;\n" " ab.a = 1;\n" " do_something(&ab);\n" " ab.a = 2;\n" " return ab.a;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f(struct AB *ab) {\n" " ab->a = 1;\n" " ab->b = 2;\n" " ab++;\n" " ab->a = 1;\n" " ab->b = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("struct AB { int a; int b; };\n" "\n" "int f(struct AB *ab) {\n" " ab->a = 1;\n" " ab->b = 2;\n" " ab = x;\n" " ab->a = 1;\n" " ab->b = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(struct AB *ab) {\n" // # " ab->data->x = 1;\n" " ab = &ab1;\n" " ab->data->x = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); // #5964 check("void func(char *buffer, const char *format, int precision, unsigned value) {\n" " (precision < 0) ? sprintf(buffer, format, value) : sprintf(buffer, format, precision, value);\n" "}"); ASSERT_EQUALS("", errout.str()); // don't crash check("struct data {\n" " struct { int i; } fc;\n" "};\n" "struct state {\n" " struct data d[123];\n" "};\n" "void func(struct state *s) {\n" " s->foo[s->x++] = 2;\n" " s->d[1].fc.i++;\n" "}"); // #6525 - inline assembly check("void f(int i) {\n" " i = 1;\n" " asm(\"foo\");\n" " i = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); // #6555 check("void foo() {\n" " char *p = 0;\n" " try {\n" " p = fred();\n" " p = wilma();\n" " }\n" " catch (...) {\n" " barney(p);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo() {\n" " char *p = 0;\n" " try {\n" " p = fred();\n" " p = wilma();\n" " }\n" " catch (...) {\n" " barney(x);\n" " }\n" "}"); ASSERT_EQUALS("test.cpp:2:style:The scope of the variable 'p' can be reduced.\n", errout.str()); check("void foo() {\n" " char *p = 0;\n" " try {\n" " if(z) {\n" " p = fred();\n" " p = wilma();\n" " }\n" " }\n" " catch (...) {\n" " barney(p);\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // Member variable pointers check("void podMemPtrs() {\n" " int POD::*memptr;\n" " memptr = &POD::a;\n" " memptr = &POD::b;\n" " if (memptr)\n" " memptr = 0;\n" "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'memptr' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:memptr is assigned\n" "test.cpp:4:note:memptr is overwritten\n", errout.str()); // Pointer function argument (#3857) check("void f(float * var)\n" "{\n" " var[0] = 0.2f;\n" " var[0] = 0.2f;\n" // <-- is initialized twice "}"); ASSERT_EQUALS("test.cpp:4:style:Variable 'var[0]' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:var[0] is assigned\n" "test.cpp:4:note:var[0] is overwritten\n", errout.str()); check("void f(float * var)\n" "{\n" " *var = 0.2f;\n" " *var = 0.2f;\n" // <-- is initialized twice "}"); ASSERT_EQUALS("test.cpp:4:style:Variable '*var' is reassigned a value before the old one has been used.\n" "test.cpp:3:note:*var is assigned\n" "test.cpp:4:note:*var is overwritten\n", errout.str()); } void redundantVarAssignment_struct() { check("struct foo {\n" " int a,b;\n" "};\n" "\n" "int main() {\n" " struct foo x;\n" " x.a = _mm_set1_ps(1.0);\n" " x.a = _mm_set1_ps(2.0);\n" "}"); ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:8]: (style) Variable 'x.a' is reassigned a value before the old one has been used.\n", errout.str()); check("void f() {\n" " struct AB ab;\n" " ab.x = 23;\n" " ab.y = 41;\n" " ab.x = 1;\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'ab.x' is reassigned a value before the old one has been used.\n", errout.str()); check("void f() {\n" " struct AB ab = {0};\n" " ab = foo();\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_7133() { // #7133 check("sal_Int32 impl_Export() {\n" " try {\n" " try {\n" " uno::Sequence< uno::Any > aArgs(2);\n" " beans::NamedValue aValue;\n" " aValue.Name = \"DocumentHandler\";\n" " aValue.Value <<= xDocHandler;\n" " aArgs[0] <<= aValue;\n" " aValue.Name = \"Model\";\n" " aValue.Value <<= xDocumentComp;\n" " aArgs[1] <<= aValue;\n" " }\n" " catch (const uno::Exception&) {\n" " }\n" " }\n" " catch (const uno::Exception&) {\n" " }\n" "}", "test.cpp", false, true); ASSERT_EQUALS("", errout.str()); check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n" " BitmapBuffer aSrcBuf;\n" " aSrcBuf.mnBitCount = nSrcBits;\n" " BitmapBuffer aDstBuf;\n" " aSrcBuf.mnBitCount = nDestBits;\n" " bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3] -> [test.c:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n", errout.str()); check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n" " BitmapBuffer aSrcBuf;\n" " aSrcBuf.mnBitCount = nSrcBits;\n" " BitmapBuffer aDstBuf;\n" " aSrcBuf.mnBitCount = nDestBits;\n" " bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n", errout.str()); check("class C { void operator=(int x); };\n" // #8368 - assignment operator might have side effects => inconclusive "void f() {\n" " C c;\n" " c = x;\n" " c = x;\n" "}"); ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Variable 'c' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str()); } void redundantVarAssignment_stackoverflow() { check("typedef struct message_node {\n" " char code;\n" " size_t size;\n" " struct message_node *next, *prev;\n" "} *message_list;\n" "static message_list remove_message_from_list(message_list m) {\n" " m->prev->next = m->next;\n" " m->next->prev = m->prev;\n" " return m->next;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_lambda() { // #7152 check("int foo() {\n" " int x = 0, y = 0;\n" " auto f = [&]() { if (x < 5) ++y; };\n" " x = 2;\n" " f();\n" " x = 6;\n" " f();\n" " return y;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_loop() { check("void f() {\n" " char buf[10];\n" " int i;\n" " for (i = 0; i < 4; i++)\n" " buf[i] = 131;\n" " buf[i] = 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void bar() {\n" // #9262 do-while with break " int x = 0;\n" " x = 432;\n" " do {\n" " if (foo()) break;\n" " x = 1;\n" " } while (false);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int num) {\n" // #9420 FP " int a = num;\n" " for (int b = 0; b < num; a = b++)\n" " dostuff(a);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void foo(int num) {\n" // #9420 FN " int a = num;\n" " for (int b = 0; b < num; a = b++);\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void redundantVarAssignment_after_switch() { check("void f(int x) {\n" // #7907 " int ret;\n" " switch (x) {\n" " case 123:\n" " ret = 1;\n" // redundant assignment " break;\n" " }\n" " ret = 3;\n" "}"); ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:8]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", errout.str()); } void redundantVarAssignment_pointer() { check("void f(int *ptr) {\n" " int *x = ptr + 1;\n" " *x = 23;\n" " foo(ptr);\n" " *x = 32;\n" "}"); ASSERT_EQUALS("", errout.str()); // #8997 check("void f() {\n" " char x[2];\n" " char* p = x;\n" " *p = 1;\n" " p += 1;\n" " *p = 1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_pointer_parameter() { check("void f(int *p) {\n" " *p = 1;\n" " if (condition) return;\n" " *p = 2;\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantVarAssignment_array() { check("void f() {\n" " int arr[10];\n" " int i = 0;\n" " arr[i] = 1;\n" " i += 2;\n" " arr[i] = 3;\n" " dostuff(arr);\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantInitialization() { setMultiline(); check("void f() {\n" " int err = -ENOMEM;\n" " err = dostuff();\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Redundant initialization for 'err'. The initialized value is overwritten before it is read.\n" "test.cpp:2:note:err is initialized\n" "test.cpp:3:note:err is overwritten\n", errout.str()); check("void f() {\n" " struct S s = {1,2,3};\n" " s = dostuff();\n" "}"); ASSERT_EQUALS("test.cpp:3:style:Redundant initialization for 's'. The initialized value is overwritten before it is read.\n" "test.cpp:2:note:s is initialized\n" "test.cpp:3:note:s is overwritten\n", errout.str()); check("void f() {\n" " int *p = NULL;\n" " p = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); // "trivial" initialization => do not warn check("void f() {\n" " struct S s = {0};\n" " s = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); check("namespace N { enum E {e0,e1}; }\n" "void f() {\n" " N::E e = N::e0;\n" // #9261 " e = dostuff();\n" "}"); ASSERT_EQUALS("", errout.str()); } void redundantMemWrite() { return; // FIXME: temporary hack // Simple tests check("void f() {\n" " char a[10];\n" " memcpy(a, foo, bar);\n" " memset(a, 0, bar);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void f() {\n" " char a[10];\n" " strcpy(a, foo);\n" " strncpy(a, 0, bar);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void f() {\n" " char a[10];\n" " sprintf(a, \"foo\");\n" " memmove(a, 0, bar);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void f(char *filename) {\n" " char *p = strrchr(filename,'.');\n" " strcpy(p, \"foo\");\n" " dostuff(filename);\n" " strcpy(p, \"foo\");\n" "}"); ASSERT_EQUALS("", errout.str()); // Writing to different parts of a buffer check("void f(void* a) {\n" " memcpy(a, foo, bar);\n" " memset(a+5, 0, bar);\n" "}"); ASSERT_EQUALS("", errout.str()); // Use variable as second argument check("void f(void* a, void* b) {\n" " memset(a, 0, 5);\n" " memcpy(b, a, 5);\n" " memset(a, 1, 5);\n" "}"); ASSERT_EQUALS("", errout.str()); // strcat is special check("void f() {\n" " char a[10];\n" " strcpy(a, foo);\n" " strcat(a, bar);\n" // Not redundant " strcpy(a, x);\n" // Redundant "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); // Tests with function call between copy check("void f() {\n" " char a[10];\n" " snprintf(a, foo, bar);\n" " bar();\n" " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout.str()); check("void* a;\n" "void f() {\n" " memset(a, 0, size);\n" " bar();\n" // Global variable might be accessed in bar() " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char a[10];\n" " memset(a, 0, size);\n" " bar();\n" " memset(a, 0, size);\n" "}"); TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", "", errout.str()); check("void bar(void* a) {}\n" "void f(void* a) {\n" " memset(a, 0, size);\n" " bar(a);\n" // Passed as argument " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("", errout.str()); // Branch tests check("void f(void* a) {\n" " memset(a, 0, size);\n" " if(x)\n" " memset(a, 0, size);\n" "}"); ASSERT_EQUALS("", errout.str()); // #4455 - initialization of local buffer check("void f(void) {" " char buf[10];\n" " memset(buf, 0, 10);\n" " strcpy(buf, string);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(void) {\n" " char buf[10] = {0};\n" " memset(buf, 0, 10);\n" " strcpy(buf, string);\n" "}"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'buf' is being written before its old content has been used.\n", errout.str()); // #5689 - use return value of strcpy check("int f(void* a) {\n" " int i = atoi(strcpy(a, foo));\n" " strncpy(a, 0, bar);\n" " return i;\n" "}"); ASSERT_EQUALS("", errout.str()); // #7175 - read+write check("void f() {\n" " char buf[100];\n" " strcpy(buf, x);\n" " strcpy(buf, dostuff(buf));\n" // <- read + write " strcpy(buf, x);\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char buf[100];\n" " strcpy(buf, x);\n" " strcpy(buf, dostuff(buf));\n" " strcpy(buf, x);\n" "}"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void varFuncNullUB() { // #4482 check("void a(...);\n" "void b() { a(NULL); }"); ASSERT_EQUALS("[test.cpp:2]: (portability) Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n", errout.str()); check("void a(char *p, ...);\n" "void b() { a(NULL, 2); }"); ASSERT_EQUALS("", errout.str()); } void checkPipeParameterSize() { // #3521 checkposix("void f(){\n" "int pipefd[1];\n" // <-- array of two integers is needed "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer 'pipefd' must have size of 2 integers if used as parameter of pipe().\n", errout.str()); checkposix("void f(){\n" "int pipefd[2];\n" "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f(){\n" "int pipefd[20];\n" "if (pipe(pipefd) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f(){\n" "int pipefd[1];\n" // <-- array of two integers is needed "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer 'pipefd' must have size of 2 integers if used as parameter of pipe().\n", errout.str()); checkposix("void f(){\n" "int pipefd[2];\n" "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); checkposix("void f(){\n" "int pipefd[20];\n" "if (pipe2(pipefd,0) == -1) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid crash with pointer variable check("void foo (int* arrayPtr)\n" "{\n" " if (pipe (arrayPtr) < 0)\n" " {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); // avoid crash with pointer variable - for local variable on stack as well - see #4801 check("void foo() {\n" " int *cp;\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // test with unknown variable check("void foo() {\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // avoid crash with pointer variable - for local variable on stack as well - see #4801 check("void foo() {\n" " int *cp;\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); // test with unknown variable check("void foo() {\n" " if ( pipe (cp) == -1 ) {\n" " return;\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkCastIntToCharAndBack() { // #160 // check getchar check("void f() {\n" "unsigned char c; c = getchar();\n" " while( c != EOF)\n" " {\n" " bar(c);\n" " c = getchar();\n" " } ;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f() {\n" "unsigned char c = getchar();\n" " while( EOF != c)\n" " {\n" " bar(c);\n" " } ;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f() {\n" " unsigned char c; c = getchar();\n" " while( EOF != c )\n" " {\n" " bar(c);\n" " c = getchar();\n" " } ;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f() {\n" " unsigned char c;\n" " while( EOF != ( c = getchar() ) )\n" " {\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f() {\n" " int i; i = getchar();\n" " while( i != EOF)\n" " {\n" " bar(i);\n" " i = getchar();\n" " } ;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int i; i = getchar();\n" " while( EOF != i )\n" " {\n" " bar(i);\n" " i = getchar();\n" " } ;\n" "}"); ASSERT_EQUALS("", errout.str()); // check getc check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = getc (pFile);\n" "} while (c != EOF)" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing getc() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = getc (pFile);\n" "} while (EOF != c)" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing getc() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = getc (pFile);\n" "} while (i != EOF)" "}"); ASSERT_EQUALS("", errout.str()); check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = getc (pFile);\n" "} while (EOF != i)" "}"); ASSERT_EQUALS("", errout.str()); // check fgetc check("void f (FILE * pFile){\n" "unsigned char c;\n" "do {\n" " c = fgetc (pFile);\n" "} while (c != EOF)" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing fgetc() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f (FILE * pFile){\n" "char c;\n" "do {\n" " c = fgetc (pFile);\n" "} while (EOF != c)" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Storing fgetc() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f (FILE * pFile){\n" "signed char c;\n" "do {\n" " c = fgetc (pFile);\n" "} while (EOF != c)" "}"); ASSERT_EQUALS("", errout.str()); check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = fgetc (pFile);\n" "} while (i != EOF)" "}"); ASSERT_EQUALS("", errout.str()); check("void f (FILE * pFile){\n" "int i;\n" "do {\n" " i = fgetc (pFile);\n" "} while (EOF != i)" "}"); ASSERT_EQUALS("", errout.str()); // cin.get() check("void f(){\n" " char ch; ch = std::cin.get();\n" " while (EOF != ch) {\n" " std::cout << ch;\n" " ch = std::cin.get();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing cin.get() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f(){\n" " char ch; ch = std::cin.get();\n" " while (ch != EOF) {\n" " std::cout << ch;\n" " ch = std::cin.get();\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Storing cin.get() return value in char variable and then comparing with EOF.\n", errout.str()); check("void f(){\n" " int i; i = std::cin.get();\n" " while ( EOF != i ) {\n" " std::cout << i;\n" " i = std::cin.get();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(){\n" " int i; i = std::cin.get();\n" " while ( i != EOF ) {\n" " std::cout << i;\n" " i = std::cin.get();\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); } void checkCommaSeparatedReturn() { check("int fun(int a) {\n" " if (a < 0)\n" " return a++,\n" " do_something();\n" "}", nullptr, true, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Comma is used in return statement. The comma can easily be misread as a ';'.\n", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return a++, do_something();\n" "}", nullptr, true, false, false); ASSERT_EQUALS("", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return a+5,\n" " do_something();\n" "}", nullptr, true, false, false); ASSERT_EQUALS("[test.cpp:3]: (style) Comma is used in return statement. The comma can easily be misread as a ';'.\n", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return a+5, do_something();\n" "}", nullptr, true, false, false); ASSERT_EQUALS("", errout.str()); check("int fun(int a) {\n" " if (a < 0)\n" " return c::b;\n" "}", nullptr, true, false, false); ASSERT_EQUALS("", errout.str()); // #4943 take care of C++11 initializer lists check("std::vector Bar() {\n" " return\n" " {\n" " { \"1\" },\n" " { \"2\" },\n" " { \"3\" }\n" " };\n" "}", nullptr, true, false, false); ASSERT_EQUALS("", errout.str()); } void checkPassByReference() { // #8570 passByValue when std::move is used check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " explicit B(A a) : a(std::move(a)) {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" "};", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " explicit B(A a) : a{std::move(a)} {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" "};", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " B(A a, A a2) : a{std::move(a)}, a2{std::move(a2)} {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" " A a2;" "};", nullptr, false, false, true); ASSERT_EQUALS("", errout.str()); check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " B(A a, A a2) : a{std::move(a)}, a2{a2} {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" " A a2;" "};", nullptr, false, false, true); ASSERT_EQUALS("[test.cpp:8]: (performance) Function parameter 'a2' should be passed by const reference.\n", errout.str()); check("struct A\n" "{\n" " std::vector x;\n" "};\n" "\n" "struct B\n" "{\n" " B(A a, A a2) : a{std::move(a)}, a2(a2) {}\n" " void Init(A _a) { a = std::move(_a); }\n" " A a;" " A a2;" "};", nullptr, false, false, true); ASSERT_EQUALS("[test.cpp:8]: (performance) Function parameter 'a2' should be passed by const reference.\n", errout.str()); } void checkComparisonFunctionIsAlwaysTrueOrFalse() { // positive test check("bool f(int x){\n" " return isless(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isless(x,x) always evaluates to false.\n", errout.str()); check("bool f(int x){\n" " return isgreater(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isgreater(x,x) always evaluates to false.\n", errout.str()); check("bool f(int x){\n" " return islessgreater(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with islessgreater(x,x) always evaluates to false.\n", errout.str()); check("bool f(int x){\n" " return islessequal(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with islessequal(x,x) always evaluates to true.\n", errout.str()); check("bool f(int x){\n" " return isgreaterequal(x,x);\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isgreaterequal(x,x) always evaluates to true.\n", errout.str()); // no warning should be reported for check("bool f(int x, int y){\n" " return isgreaterequal(x,y) && islessequal(x,y) && islessgreater(x,y) && isgreater(x,y) && isless(x,y);\n" "}"); ASSERT_EQUALS("", errout.str()); } void integerOverflow() { // 5895 // no signed integer overflow should happen check("void f(unsigned long long ull) {\n" " if (ull == 0x89504e470d0a1a0a || ull == 0x8a4d4e470d0a1a0a) ;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void redundantPointerOp() { check("int *f(int *x) {\n" " return &*x;\n" "}\n", nullptr, false, true); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'x' - it's already a pointer.\n", errout.str()); check("int *f(int *y) {\n" " return &(*y);\n" "}\n", nullptr, false, true); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'y' - it's already a pointer.\n", errout.str()); // no warning for bitwise AND check("void f(int *b) {\n" " int x = 0x20 & *b;\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); // No message for double pointers to structs check("void f(struct foo **my_struct) {\n" " char **pass_to_func = &(*my_struct)->buf;\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); // another double pointer to struct - with an array check("void f(struct foo **my_struct) {\n" " char **pass_to_func = &(*my_struct)->buf[10];\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); // double pointer to array check("void f(char **ptr) {\n" " int *x = &(*ptr)[10];\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); // function calls check("void f(Mutex *mut) {\n" " pthread_mutex_lock(&*mut);\n" "}\n", nullptr, false, false); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'mut' - it's already a pointer.\n", errout.str()); // make sure we got the AST match for "(" right check("void f(char *ptr) {\n" " if (&*ptr == NULL)\n" " return;\n" "}\n", nullptr, false, true); ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'ptr' - it's already a pointer.\n", errout.str()); // no warning for macros check("#define MUTEX_LOCK(m) pthread_mutex_lock(&(m))\n" "void f(struct mutex *mut) {\n" " MUTEX_LOCK(*mut);\n" "}\n", nullptr, false, true); ASSERT_EQUALS("", errout.str()); } void test_isSameExpression() { // see #5738 check("bool isInUnoIncludeFile(StringRef name) {" " return name.startswith(SRCDIR \"/com/\") || name.startswith(SRCDIR \"/uno/\");\n" "};", "test.cpp", false, false); ASSERT_EQUALS("", errout.str()); } void raceAfterInterlockedDecrement() { checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " whatever();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter)\n" " return;\n" " destroy();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (!counter)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter > 0)\n" " return;\n" " destroy();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (0 < counter)\n" " return;\n" " destroy();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter == 0)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (0 == counter)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (0 != counter)\n" " return;\n" " destroy()\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter != 0)\n" " return;\n" " destroy()\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (counter <= 0)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " InterlockedDecrement(&counter);\n" " if (0 >= counter)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount)\n" " return;\n" " destroy();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (!newCount)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount > 0)\n" " return;\n" " destroy();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (0 < newCount)\n" " return;\n" " destroy();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount == 0)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (0 == newCount)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (0 != newCount)\n" " return;\n" " destroy()\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount != 0)\n" " return;\n" " destroy()\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (newCount <= 0)\n" " destroy();\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("void f() {\n" " int counter = 0;\n" " int newCount = InterlockedDecrement(&counter);\n" " if (0 >= newCount)\n" " destroy;\n" "}\n"); ASSERT_EQUALS("", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " } else {\n" " return counter;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (::InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " } else {\n" " return counter;\n" " }\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " }\n" " return counter;\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (::InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " }\n" " return counter;\n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " } else\n" " return counter;\n" " \n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); checkInterlockedDecrement("int f() {\n" " int counter = 0;\n" " if (::InterlockedDecrement(&counter) == 0) {\n" " destroy();\n" " return 0;\n" " } else\n" " return counter;\n" " \n" "}\n"); ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout.str()); } void testUnusedLabel() { check("void f() {\n" " label:\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (style) Label 'label' is not used.\n", errout.str()); check("void f() {\n" " label:\n" " foo();\n" " goto label;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " label:\n" " foo();\n" " goto label;\n" "}\n" "void g() {\n" " label:\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (style) Label 'label' is not used.\n", errout.str()); check("void f() {\n" " switch(a) {\n" " default:\n" " }\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " class X {\n" " protected:\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " class X {\n" " my_protected:\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); check("int test(char art) {\n" " switch (art) {\n" " caseZERO:\n" " return 0;\n" " case1:\n" " return 1;\n" " case 2:\n" " return 2;\n" " }\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (warning) Label 'caseZERO' is not used. Should this be a 'case' of the enclosing switch()?\n" "[test.cpp:5]: (warning) Label 'case1' is not used. Should this be a 'case' of the enclosing switch()?\n", errout.str()); check("int test(char art) {\n" " switch (art) {\n" " case 2:\n" " return 2;\n" " }\n" " label:\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (style) Label 'label' is not used.\n", errout.str()); } void testEvaluationOrder() { check("void f() {\n" " int x = dostuff();\n" " return x + x++;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Expression 'x+x++' depends on order of evaluation of side effects\n", errout.str()); // #7226 check("long int f1(const char *exp) {\n" " return strtol(++exp, (char **)&exp, 10);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); check("long int f1(const char *exp) {\n" " return dostuff(++exp, exp, 10);\n" "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (error) Expression '++exp,exp' depends on order of evaluation of side effects\n", errout.str()); check("void f() {\n" " int a;\n" " while (a=x(), a==123) {}\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); // # 8717 check("void f(int argc, char *const argv[]) {\n" " char **local_argv = safe_malloc(sizeof (*local_argv));\n" " int local_argc = 0;\n" " local_argv[local_argc++] = argv[0];\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " int x = 0;\n" " return 0 + x++;\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f(int x, int y) {\n" " int a[10];\n" " a[x+y] = a[y+x]++;;\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Expression 'a[x+y]=a[y+x]++' depends on order of evaluation of side effects\n", errout.str()); } void testEvaluationOrderSelfAssignment() { // self assignment check("void f() {\n" " int x = x = y + 1;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:2]: (warning) Redundant assignment of 'x' to itself.\n", errout.str()); } void testEvaluationOrderMacro() { // macro, don't bailout (#7233) checkP("#define X x\n" "void f(int x) {\n" " return x + X++;\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Expression 'x+x++' depends on order of evaluation of side effects\n", errout.str()); } void testEvaluationOrderSequencePointsFunctionCall() { // FP check("void f(int id) {\n" " id = dostuff(id += 42);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); // FN check("void f(int id) {\n" " id = id + dostuff(id += 42);\n" "}", "test.c"); TODO_ASSERT_EQUALS("error", "", errout.str()); } void testEvaluationOrderSequencePointsComma() { check("int f(void) {\n" " int t;\n" " return (unsigned char)(t=1,t^c);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f(void) {\n" " int t;\n" " dostuff(t=1,t^c);\n" "}", "test.c"); ASSERT_EQUALS("[test.c:3]: (error) Expression 't=1,t^c' depends on order of evaluation of side effects\n", errout.str()); check("void f(void) {\n" " int t;\n" " dostuff((t=1,t),2);\n" "}", "test.c"); ASSERT_EQUALS("", errout.str()); // #8230 check("void hprf(const char* fp) {\n" " do\n" " ;\n" " while (++fp, (*fp) <= 0177);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void hprf(const char* fp) {\n" " do\n" " ;\n" " while (i++, ++fp, (*fp) <= 0177);\n" "}\n", "test.c"); ASSERT_EQUALS("", errout.str()); check("void f(const char* fp) {\n" " do\n" " ;\n" " while (f(++fp, (*fp) <= 7));\n" "}\n", "test.c"); ASSERT_EQUALS("[test.c:4]: (error) Expression '++fp,(*fp)<=7' depends on order of evaluation of side effects\n", errout.str()); } void testEvaluationOrderSizeof() { check("void f(char *buf) {\n" " dostuff(buf++, sizeof(*buf));" "}", "test.c"); ASSERT_EQUALS("", errout.str()); } void testUnsignedLessThanZero() { check("struct d {\n" " unsigned n;\n" "};\n" "void f(void) {\n" " struct d d;\n" " d.n = 3;\n" "\n" " if (d.n < 0) {\n" " return;\n" " }\n" "\n" " if (0 > d.n) {\n" " return;\n" " }\n" "}", "test.c"); ASSERT_EQUALS("[test.c:8]: (style) Checking if unsigned expression 'd.n' is less than zero.\n" "[test.c:12]: (style) Checking if unsigned expression 'd.n' is less than zero.\n", errout.str()); } void doubleMove1() { check("void g(A a);\n" "void f() {\n" " A a;\n" " g(std::move(a));\n" " g(std::move(a));\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n", errout.str()); } void doubleMoveMemberInitialization1() { check("class A\n" "{\n" " A(B && b)\n" " :b1(std::move(b))\n" " {\n" " b2 = std::move(b);\n" " }\n" " B b1;\n" " B b2;\n" "};"); ASSERT_EQUALS("[test.cpp:6]: (warning) Access of moved variable 'b'.\n", errout.str()); } void doubleMoveMemberInitialization2() { check("class A\n" "{\n" " A(B && b)\n" " :b1(std::move(b)),\n" " b2(std::move(b))\n" " {}\n" " B b1;\n" " B b2;\n" "};"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'b'.\n", errout.str()); } void moveAndAssign1() { check("A g(A a);\n" "void f() {\n" " A a;\n" " a = g(std::move(a));\n" " a = g(std::move(a));\n" "}"); ASSERT_EQUALS("", errout.str()); } void moveAndAssign2() { check("A g(A a);\n" "void f() {\n" " A a;\n" " B b = g(std::move(a));\n" " C c = g(std::move(a));\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAssignMoveAssign() { check("void h(A a);\n" "void f() {" " A a;\n" " g(std::move(a));\n" " h(a);\n" " a = b;\n" " h(a);\n" " g(std::move(a));\n" " h(a);\n" " a = b;\n" " h(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'a'.\n" "[test.cpp:8]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndReset1() { check("A g(A a);\n" "void f() {\n" " A a;\n" " a.reset(g(std::move(a)));\n" " a.reset(g(std::move(a)));\n" "}"); ASSERT_EQUALS("", errout.str()); } void moveAndReset2() { check("A g(A a);\n" "void f() {\n" " A a;\n" " A b;\n" " A c;\n" " b.reset(g(std::move(a)));\n" " c.reset(g(std::move(a)));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveResetMoveReset() { check("void h(A a);\n" "void f() {" " A a;\n" " g(std::move(a));\n" " h(a);\n" " a.reset(b);\n" " h(a);\n" " g(std::move(a));\n" " h(a);\n" " a.reset(b);\n" " h(a);\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'a'.\n" "[test.cpp:8]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndFunctionParameter() { check("void g(A a);\n" "void f() {\n" " A a;\n" " A b = std::move(a);\n" " g(a);\n" " A c = a;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n" "[test.cpp:6]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndFunctionParameterReference() { check("void g(A & a);\n" "void f() {\n" " A a;\n" " A b = std::move(a);\n" " g(a);\n" " A c = a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void moveAndFunctionParameterConstReference() { check("void g(A const & a);\n" "void f() {\n" " A a;\n" " A b = std::move(a);\n" " g(a);\n" " A c = a;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n" "[test.cpp:6]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndFunctionParameterUnknown() { check("void f() {\n" " A a;\n" " A b = std::move(a);\n" " g(a);\n" " A c = a;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Access of moved variable 'a'.\n" "[test.cpp:5]: (warning, inconclusive) Access of moved variable 'a'.\n", errout.str()); } void moveAndReturn() { check("int f(int i) {\n" " A a;\n" " A b;\n" " g(std::move(a));\n" " if (i)\n" " return g(std::move(b));\n" " return h(std::move(a),std::move(b));\n" "}"); ASSERT_EQUALS("[test.cpp:7]: (warning) Access of moved variable 'a'.\n", errout.str()); } void moveAndClear() { check("void f() {\n" " V v;\n" " g(std::move(v));\n" " v.clear();\n" " if (v.empty()) {}\n" "}"); ASSERT_EQUALS("", errout.str()); } void movedPointer() { check("void f() {\n" " P p;\n" " g(std::move(p));\n" " x = p->x;\n" " y = p->y;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'p'.\n" "[test.cpp:5]: (warning) Access of moved variable 'p'.\n", errout.str()); } void moveAndAddressOf() { check("void f() {\n" " std::string s1 = x;\n" " std::string s2 = std::move(s1);\n" " p = &s1;\n" "}"); ASSERT_EQUALS("", errout.str()); } void partiallyMoved() { check("void f() {\n" " A a;\n" " gx(std::move(a).x());\n" " gy(std::move(a).y());\n" "}"); ASSERT_EQUALS("", errout.str()); } void moveAndLambda() { check("void f() {\n" " A a;\n" " auto h = [a=std::move(a)](){return g(std::move(a));};" " b = a;\n" "}"); ASSERT_EQUALS("", errout.str()); } void forwardAndUsed() { check("template\n" "void f(T && t) {\n" " g(std::forward(t));\n" " T s = t;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (warning) Access of forwarded variable 't'.\n", errout.str()); } void funcArgNamesDifferent() { check("void func1(int a, int b, int c); \n" "void func1(int a, int b, int c) { }\n" "void func2(int a, int b, int c);\n" "void func2(int A, int B, int C) { }\n" "class Fred {\n" " void func1(int a, int b, int c); \n" " void func2(int a, int b, int c);\n" " void func3(int a = 0, int b = 0, int c = 0);\n" " void func4(int a = 0, int b = 0, int c = 0);\n" "};\n" "void Fred::func1(int a, int b, int c) { }\n" "void Fred::func2(int A, int B, int C) { }\n" "void Fred::func3(int a, int b, int c) { }\n" "void Fred::func4(int A, int B, int C) { }\n"); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 1 names different: declaration 'a' definition 'A'.\n" "[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 2 names different: declaration 'b' definition 'B'.\n" "[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 3 names different: declaration 'c' definition 'C'.\n" "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 1 names different: declaration 'a' definition 'A'.\n" "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 2 names different: declaration 'b' definition 'B'.\n" "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 3 names different: declaration 'c' definition 'C'.\n" "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 1 names different: declaration 'a' definition 'A'.\n" "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 2 names different: declaration 'b' definition 'B'.\n" "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 3 names different: declaration 'c' definition 'C'.\n", errout.str()); } void funcArgOrderDifferent() { check("void func1(int a, int b, int c);\n" "void func1(int a, int b, int c) { }\n" "void func2(int a, int b, int c);\n" "void func2(int c, int b, int a) { }\n" "void func3(int, int b, int c);\n" "void func3(int c, int b, int a) { }\n" "class Fred {\n" " void func1(int a, int b, int c);\n" " void func2(int a, int b, int c);\n" " void func3(int a = 0, int b = 0, int c = 0);\n" " void func4(int, int b = 0, int c = 0);\n" "};\n" "void Fred::func1(int a, int b, int c) { }\n" "void Fred::func2(int c, int b, int a) { }\n" "void Fred::func3(int c, int b, int a) { }\n" "void Fred::func4(int c, int b, int a) { }\n", nullptr, false, false); ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Function 'func2' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" "[test.cpp:5] -> [test.cpp:6]: (warning) Function 'func3' argument order different: declaration ', b, c' definition 'c, b, a'\n" "[test.cpp:9] -> [test.cpp:14]: (warning) Function 'func2' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" "[test.cpp:10] -> [test.cpp:15]: (warning) Function 'func3' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" "[test.cpp:11] -> [test.cpp:16]: (warning) Function 'func4' argument order different: declaration ', b, c' definition 'c, b, a'\n", errout.str()); } // #7846 - Syntax error when using C++11 braced-initializer in default argument void cpp11FunctionArgInit() { // syntax error is not expected ASSERT_NO_THROW(check("\n void foo(int declaration = {}) {" "\n for (int i = 0; i < 10; i++) {}\n" "\n }" "\n ")); ASSERT_EQUALS("", errout.str()); } void shadowVariables() { check("int x;\n" "void f() { int x; }\n"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable \'x\' shadows outer variable\n", errout.str()); check("int x();\n" "void f() { int x; }\n"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable \'x\' shadows outer function\n", errout.str()); check("struct C {\n" " C(int x) : x(x) {}\n" // <- we do not want a FP here " int x;\n" "};"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " if (cond) {int x;}\n" // <- not a shadow variable " int x;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int size() {\n" " int size;\n" // <- not a shadow variable "}\n"); ASSERT_EQUALS("", errout.str()); check("void f() {\n" // #8954 - lambda " int x;\n" " auto f = [](){ int x; }" "}"); ASSERT_EQUALS("", errout.str()); check("void f(int x) { int x; }"); ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Local variable 'x' shadows outer argument\n", errout.str()); } void constArgument() { check("void g(int);\n" "void f(int x) {\n" " g((x & 0x01) >> 7);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Argument '(x&1)>>7' to function g is always 0\n", errout.str()); check("void g(int);\n" "void f(int x) {\n" " g((int)((x & 0x01) >> 7));\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Argument '(int)((x&1)>>7)' to function g is always 0\n", errout.str()); check("void g(int);\n" "void f(int x) {\n" " g(0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void h() { return 1; }\n" "void f(int x) {\n" " g(h());\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f(int x) {\n" " g(std::strlen(\"a\"));\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f(int x) {\n" " g((int)0);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(Foo *);\n" "void f() {\n" " g(reinterpret_cast(0));\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f(int x) {\n" " x = 0;\n" " g(x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f() {\n" " const int x = 0;\n" " g(x + 1);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void g(int);\n" "void f() {\n" " char i = 1;\n" " g(static_cast(i));\n" "}\n"); ASSERT_EQUALS("", errout.str()); // #8986 check("void f(int);\n" "void g() {\n" " const int x[] = { 10, 10 };\n" " f(x[0]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("void f(int);\n" "void g() {\n" " int x[] = { 10, 10 };\n" " f(x[0]);\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A { int x; };" "void g(int);\n" "void f(int x) {\n" " A y;\n" " y.x = 1;\n" " g(y.x);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void checkComparePointers() { check("int f() {\n" " int foo[1] = {0};\n" " int bar[1] = {0};\n" " int diff = 0;\n" " if(foo > bar) {\n" " diff = 1;\n" " }\n" " return diff;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:5] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:5]: (error) Comparing pointers that point to different objects\n", errout.str()); check("bool f() {\n" " int x = 0;\n" " int y = 0;\n" " int* xp = &x;\n" " int* yp = &y;\n" " return xp > yp;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:6]: (error) Comparing pointers that point to different objects\n", errout.str()); check("bool f() {\n" " int x = 0;\n" " int y = 1;\n" " return &x > &y;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:4]: (error) Comparing pointers that point to different objects\n", errout.str()); check("struct A {int data;};\n" "bool f() {\n" " A x;\n" " A y;\n" " int* xp = &x.data;\n" " int* yp = &y.data;\n" " return xp > yp;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:5] -> [test.cpp:1] -> [test.cpp:6] -> [test.cpp:7]: (error) Comparing pointers that point to different objects\n", errout.str()); check("struct A {int data;};\n" "bool f(A ix, A iy) {\n" " A* x = &ix;\n" " A* y = &iy;\n" " int* xp = &x->data;\n" " int* yp = &y->data;\n" " return xp > yp;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:2] -> [test.cpp:4] -> [test.cpp:6] -> [test.cpp:7]: (error) Comparing pointers that point to different objects\n", errout.str()); check("bool f(int * xp, int* yp) {\n" " return &xp > &yp;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:1] -> [test.cpp:2] -> [test.cpp:2]: (error) Comparing pointers that point to different objects\n", errout.str()); check("int f() {\n" " int x = 0;\n" " int y = 1;\n" " return &x - &y;\n" "}\n"); ASSERT_EQUALS( "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:4]: (error) Subtracting pointers that point to different objects\n", errout.str()); check("bool f() {\n" " int x[2] = {1, 2}m;\n" " int* xp = &x[0];\n" " int* yp = &x[1];\n" " return xp > yp;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool f(int * xp, int* yp) {\n" " return xp > yp;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("bool f(const int & x, const int& y) {\n" " return &x > &y;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("int& g();\n" "bool f() {\n" " const int& x = g();\n" " const int& y = g();\n" " const int* xp = &x;\n" " const int* yp = &y;\n" " return xp > yp;\n" "}\n"); ASSERT_EQUALS("", errout.str()); check("struct A {int data;};\n" "bool f(A ix) {\n" " A* x = &ix;\n" " A* y = x;\n" " int* xp = &x->data;\n" " int* yp = &y->data;\n" " return xp > yp;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void unusedVariableValueTemplate() { check("#include \n" "class A\n" "{\n" "public:\n" " class Hash\n" " {\n" " public:\n" " std::size_t operator()(const A& a) const\n" " {\n" " (void)a;\n" " return 0;\n" " }\n" " };\n" "};\n" "namespace std\n" "{\n" " template <>\n" " struct hash\n" " {\n" " std::size_t operator()(const A& a) const noexcept\n" " {\n" " return A::Hash{}(a);\n" " }\n" " };\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestOther) cppcheck-1.90/test/testpath.cpp000066400000000000000000000126641357737443600165760ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "path.h" #include "testsuite.h" #include #include class TestPath : public TestFixture { public: TestPath() : TestFixture("TestPath") { } private: void run() OVERRIDE { TEST_CASE(removeQuotationMarks); TEST_CASE(acceptFile); TEST_CASE(getCurrentPath); TEST_CASE(isAbsolute); TEST_CASE(getRelative); TEST_CASE(is_c); TEST_CASE(is_cpp); TEST_CASE(get_path_from_filename); } void removeQuotationMarks() const { // Path::removeQuotationMarks() ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("index.cpp")); ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("\"index.cpp")); ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("index.cpp\"")); ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("\"index.cpp\"")); ASSERT_EQUALS("path to/index.cpp", Path::removeQuotationMarks("\"path to\"/index.cpp")); ASSERT_EQUALS("path to/index.cpp", Path::removeQuotationMarks("\"path to/index.cpp\"")); ASSERT_EQUALS("the/path to/index.cpp", Path::removeQuotationMarks("the/\"path to\"/index.cpp")); ASSERT_EQUALS("the/path to/index.cpp", Path::removeQuotationMarks("\"the/path to/index.cpp\"")); } void acceptFile() const { ASSERT(Path::acceptFile("index.cpp")); ASSERT(Path::acceptFile("index.invalid.cpp")); ASSERT(Path::acceptFile("index.invalid.Cpp")); ASSERT(Path::acceptFile("index.invalid.C")); ASSERT(Path::acceptFile("index.invalid.C++")); ASSERT(Path::acceptFile("index.")==false); ASSERT(Path::acceptFile("index")==false); ASSERT(Path::acceptFile("")==false); ASSERT(Path::acceptFile("C")==false); // don't accept any headers ASSERT_EQUALS(false, Path::acceptFile("index.h")); ASSERT_EQUALS(false, Path::acceptFile("index.hpp")); } void getCurrentPath() const { ASSERT_EQUALS(true, Path::isAbsolute(Path::getCurrentPath())); } void isAbsolute() const { #ifdef _WIN32 ASSERT_EQUALS(true, Path::isAbsolute("C:\\foo\\bar")); ASSERT_EQUALS(true, Path::isAbsolute("C:/foo/bar")); ASSERT_EQUALS(true, Path::isAbsolute("\\\\foo\\bar")); ASSERT_EQUALS(false, Path::isAbsolute("foo\\bar")); ASSERT_EQUALS(false, Path::isAbsolute("foo/bar")); ASSERT_EQUALS(false, Path::isAbsolute("foo.cpp")); ASSERT_EQUALS(false, Path::isAbsolute("C:foo.cpp")); ASSERT_EQUALS(false, Path::isAbsolute("C:foo\\bar.cpp")); ASSERT_EQUALS(false, Path::isAbsolute("bar.cpp")); TODO_ASSERT_EQUALS(true, false, Path::isAbsolute("\\")); #else ASSERT_EQUALS(true, Path::isAbsolute("/foo/bar")); ASSERT_EQUALS(true, Path::isAbsolute("/")); ASSERT_EQUALS(false, Path::isAbsolute("foo/bar")); ASSERT_EQUALS(false, Path::isAbsolute("foo.cpp")); #endif } void getRelative() const { const std::vector basePaths = { "", // Don't crash with empty paths "C:/foo", "C:/bar/", "C:/test.cpp" }; ASSERT_EQUALS("x.c", Path::getRelativePath("C:/foo/x.c", basePaths)); ASSERT_EQUALS("y.c", Path::getRelativePath("C:/bar/y.c", basePaths)); ASSERT_EQUALS("foo/y.c", Path::getRelativePath("C:/bar/foo/y.c", basePaths)); ASSERT_EQUALS("C:/test.cpp", Path::getRelativePath("C:/test.cpp", basePaths)); ASSERT_EQUALS("C:/foobar/test.cpp", Path::getRelativePath("C:/foobar/test.cpp", basePaths)); } void is_c() const { ASSERT(Path::isC("index.cpp")==false); ASSERT(Path::isC("")==false); ASSERT(Path::isC("c")==false); ASSERT(Path::isC("index.c")); ASSERT(Path::isC("C:\\foo\\index.c")); // In unix .C is considered C++ #ifdef _WIN32 ASSERT_EQUALS(true, Path::isC("C:\\foo\\index.C")); #else ASSERT_EQUALS(false, Path::isC("C:\\foo\\index.C")); #endif } void is_cpp() const { ASSERT(Path::isCPP("index.c")==false); // In unix .C is considered C++ #ifdef _WIN32 ASSERT_EQUALS(false, Path::isCPP("index.C")); #else ASSERT_EQUALS(true, Path::isCPP("index.C")); #endif ASSERT(Path::isCPP("index.cpp")); ASSERT(Path::isCPP("C:\\foo\\index.cpp")); ASSERT(Path::isCPP("C:\\foo\\index.Cpp")); } void get_path_from_filename() const { ASSERT_EQUALS("", Path::getPathFromFilename("index.h")); ASSERT_EQUALS("/tmp/", Path::getPathFromFilename("/tmp/index.h")); ASSERT_EQUALS("a/b/c/", Path::getPathFromFilename("a/b/c/index.h")); ASSERT_EQUALS("a/b/c/", Path::getPathFromFilename("a/b/c/")); } }; REGISTER_TEST(TestPath) cppcheck-1.90/test/testpathmatch.cpp000066400000000000000000000130651357737443600176070ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "pathmatch.h" #include "testsuite.h" #include #include class TestPathMatch : public TestFixture { public: TestPathMatch() : TestFixture("TestPathMatch") , emptyMatcher(std::vector()) , srcMatcher(std::vector(1, "src/")) , fooCppMatcher(std::vector(1, "foo.cpp")) , srcFooCppMatcher(std::vector(1, "src/foo.cpp")) { } private: const PathMatch emptyMatcher; const PathMatch srcMatcher; const PathMatch fooCppMatcher; const PathMatch srcFooCppMatcher; void run() OVERRIDE { TEST_CASE(emptymaskemptyfile); TEST_CASE(emptymaskpath1); TEST_CASE(emptymaskpath2); TEST_CASE(emptymaskpath3); TEST_CASE(onemaskemptypath); TEST_CASE(onemasksamepath); TEST_CASE(onemasksamepathdifferentcase); TEST_CASE(onemasksamepathwithfile); TEST_CASE(onemaskdifferentdir1); TEST_CASE(onemaskdifferentdir2); TEST_CASE(onemaskdifferentdir3); TEST_CASE(onemaskdifferentdir4); TEST_CASE(onemasklongerpath1); TEST_CASE(onemasklongerpath2); TEST_CASE(onemasklongerpath3); TEST_CASE(twomasklongerpath1); TEST_CASE(twomasklongerpath2); TEST_CASE(twomasklongerpath3); TEST_CASE(twomasklongerpath4); TEST_CASE(filemask1); TEST_CASE(filemaskdifferentcase); TEST_CASE(filemask2); TEST_CASE(filemask3); TEST_CASE(filemaskpath1); TEST_CASE(filemaskpath2); TEST_CASE(filemaskpath3); TEST_CASE(filemaskpath4); } // Test empty PathMatch void emptymaskemptyfile() const { ASSERT(!emptyMatcher.match("")); } void emptymaskpath1() const { ASSERT(!emptyMatcher.match("src/")); } void emptymaskpath2() const { ASSERT(!emptyMatcher.match("../src/")); } void emptymaskpath3() const { ASSERT(!emptyMatcher.match("/home/user/code/src/")); } // Test PathMatch containing "src/" void onemaskemptypath() const { ASSERT(!srcMatcher.match("")); } void onemasksamepath() const { ASSERT(srcMatcher.match("src/")); } void onemasksamepathdifferentcase() const { std::vector masks(1, "sRc/"); PathMatch match(masks, false); ASSERT(match.match("srC/")); } void onemasksamepathwithfile() const { ASSERT(srcMatcher.match("src/file.txt")); } void onemaskdifferentdir1() const { ASSERT(!srcMatcher.match("srcfiles/file.txt")); } void onemaskdifferentdir2() const { ASSERT(!srcMatcher.match("proj/srcfiles/file.txt")); } void onemaskdifferentdir3() const { ASSERT(!srcMatcher.match("proj/mysrc/file.txt")); } void onemaskdifferentdir4() const { ASSERT(!srcMatcher.match("proj/mysrcfiles/file.txt")); } void onemasklongerpath1() const { ASSERT(srcMatcher.match("/tmp/src/")); } void onemasklongerpath2() const { ASSERT(srcMatcher.match("src/module/")); } void onemasklongerpath3() const { ASSERT(srcMatcher.match("project/src/module/")); } void twomasklongerpath1() const { std::vector masks = { "src/", "module/" }; PathMatch match(masks); ASSERT(!match.match("project/")); } void twomasklongerpath2() const { std::vector masks = { "src/", "module/" }; PathMatch match(masks); ASSERT(match.match("project/src/")); } void twomasklongerpath3() const { std::vector masks = { "src/", "module/" }; PathMatch match(masks); ASSERT(match.match("project/module/")); } void twomasklongerpath4() const { std::vector masks = { "src/", "module/" }; PathMatch match(masks); ASSERT(match.match("project/src/module/")); } // Test PathMatch containing "foo.cpp" void filemask1() const { ASSERT(fooCppMatcher.match("foo.cpp")); } void filemaskdifferentcase() const { std::vector masks(1, "foo.cPp"); PathMatch match(masks, false); ASSERT(match.match("fOo.cpp")); } void filemask2() const { ASSERT(fooCppMatcher.match("../foo.cpp")); } void filemask3() const { ASSERT(fooCppMatcher.match("src/foo.cpp")); } // Test PathMatch containing "src/foo.cpp" void filemaskpath1() const { ASSERT(srcFooCppMatcher.match("src/foo.cpp")); } void filemaskpath2() const { ASSERT(srcFooCppMatcher.match("proj/src/foo.cpp")); } void filemaskpath3() const { ASSERT(!srcFooCppMatcher.match("foo.cpp")); } void filemaskpath4() const { ASSERT(!srcFooCppMatcher.match("bar/foo.cpp")); } }; REGISTER_TEST(TestPathMatch) cppcheck-1.90/test/testplatform.cpp000066400000000000000000000357211357737443600174650ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "errorlogger.h" #include "platform.h" #include "testsuite.h" #include class TestPlatform : public TestFixture { public: TestPlatform() : TestFixture("TestPlatform") { } private: void run() OVERRIDE { TEST_CASE(empty); TEST_CASE(valid_config_native_1); TEST_CASE(valid_config_native_2); TEST_CASE(valid_config_file_1); TEST_CASE(valid_config_file_2); TEST_CASE(valid_config_file_3); TEST_CASE(valid_config_file_4); TEST_CASE(invalid_config_file_1); // TEST_CASE(empty_elements); // TODO: Trac issue #8409 } static bool readPlatform(cppcheck::Platform& platform, const char* xmldata) { tinyxml2::XMLDocument doc; doc.Parse(xmldata); return platform.loadFromXmlDocument(&doc); } void empty() const { // An empty platform file does not change values, only the type. const char xmldata[] = "\n"; cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Win64)); ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(cppcheck::Platform::PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); } void valid_config_native_1() { // Verify if native Win32A platform is loaded correctly cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Win32A)); ASSERT_EQUALS(cppcheck::Platform::Win32A, platform.platformType); ASSERT(platform.isWindowsPlatform()); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); ASSERT_EQUALS(16, platform.short_bit); ASSERT_EQUALS(32, platform.int_bit); ASSERT_EQUALS(32, platform.long_bit); ASSERT_EQUALS(64, platform.long_long_bit); } void valid_config_native_2() { // Verify if native Unix64 platform is loaded correctly cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Unix64)); ASSERT_EQUALS(cppcheck::Platform::Unix64, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(32, platform.int_bit); ASSERT_EQUALS(8, platform.sizeof_long); ASSERT_EQUALS(64, platform.long_bit); } void valid_config_file_1() { // Valid platform configuration with all possible values specified. // Similar to the avr8 platform file. const char xmldata[] = "\n" "\n" " 8\n" " unsigned\n" " \n" " 1\n" " 2\n" " 2\n" " 4\n" " 8\n" " 4\n" " 4\n" " 4\n" " 2\n" " 2\n" " 2\n" " \n" " "; cppcheck::Platform platform; ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS('u', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(2, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); ASSERT_EQUALS(4, platform.sizeof_float); ASSERT_EQUALS(4, platform.sizeof_double); ASSERT_EQUALS(4, platform.sizeof_long_double); ASSERT_EQUALS(2, platform.sizeof_pointer); ASSERT_EQUALS(2, platform.sizeof_size_t); ASSERT_EQUALS(2, platform.sizeof_wchar_t); ASSERT_EQUALS(16, platform.short_bit); ASSERT_EQUALS(16, platform.int_bit); ASSERT_EQUALS(32, platform.long_bit); ASSERT_EQUALS(64, platform.long_long_bit); } void valid_config_file_2() { // Valid platform configuration with all possible values specified and // char_bit > 8. const char xmldata[] = "\n" "\n" " 20\n" " signed\n" " \n" " 1\n" " 2\n" " 3\n" " 4\n" " 5\n" " 6\n" " 7\n" " 8\n" " 9\n" " 10\n" " 11\n" " \n" " "; cppcheck::Platform platform; ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(20, platform.char_bit); ASSERT_EQUALS('s', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(3, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(5, platform.sizeof_long_long); ASSERT_EQUALS(6, platform.sizeof_float); ASSERT_EQUALS(7, platform.sizeof_double); ASSERT_EQUALS(8, platform.sizeof_long_double); ASSERT_EQUALS(9, platform.sizeof_pointer); ASSERT_EQUALS(10, platform.sizeof_size_t); ASSERT_EQUALS(11, platform.sizeof_wchar_t); ASSERT_EQUALS(40, platform.short_bit); ASSERT_EQUALS(60, platform.int_bit); ASSERT_EQUALS(80, platform.long_bit); ASSERT_EQUALS(100, platform.long_long_bit); } void valid_config_file_3() { // Valid platform configuration without any usable information. // Similar like an empty file. const char xmldata[] = "\n" "\n" " 8\n" " unsigned\n" " \n" " 1\n" " 2\n" " 3\n" " 4\n" " 5\n" " 6\n" " 7\n" " 8\n" " 9\n" " 10\n" " 11\n" " \n" " "; cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Win64)); ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); } void valid_config_file_4() { // Valid platform configuration with all possible values specified and // set to 0. const char xmldata[] = "\n" "\n" " 0\n" " z\n" " \n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " 0\n" " \n" " "; cppcheck::Platform platform; ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(0, platform.char_bit); ASSERT_EQUALS('z', platform.defaultSign); ASSERT_EQUALS(0, platform.sizeof_bool); ASSERT_EQUALS(0, platform.sizeof_short); ASSERT_EQUALS(0, platform.sizeof_int); ASSERT_EQUALS(0, platform.sizeof_long); ASSERT_EQUALS(0, platform.sizeof_long_long); ASSERT_EQUALS(0, platform.sizeof_float); ASSERT_EQUALS(0, platform.sizeof_double); ASSERT_EQUALS(0, platform.sizeof_long_double); ASSERT_EQUALS(0, platform.sizeof_pointer); ASSERT_EQUALS(0, platform.sizeof_size_t); ASSERT_EQUALS(0, platform.sizeof_wchar_t); ASSERT_EQUALS(0, platform.short_bit); ASSERT_EQUALS(0, platform.int_bit); ASSERT_EQUALS(0, platform.long_bit); ASSERT_EQUALS(0, platform.long_long_bit); } void invalid_config_file_1() { // Invalid XML file: mismatching elements "boolt" vs "bool". const char xmldata[] = "\n" "\n" " 8\n" " unsigned\n" " \n" " 1\n" " 2\n" " 2\n" " 4\n" " 8\n" " 4\n" " 4\n" " 4\n" " 2\n" " 2\n" " 2\n" " \n" " "; cppcheck::Platform platform; ASSERT(!readPlatform(platform, xmldata)); } #if 0 // @TODO: Enable when Trac issue #8409 has been fixed void empty_elements() { // Valid platform configuration without any usable information. // Similar like an empty file. const char xmldata[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " "; cppcheck::Platform platform; ASSERT(platform.platform(cppcheck::Platform::Win64)); ASSERT(readPlatform(platform, xmldata)); ASSERT_EQUALS(platform.PlatformFile, platform.platformType); ASSERT(!platform.isWindowsPlatform()); ASSERT_EQUALS(8, platform.char_bit); ASSERT_EQUALS('\0', platform.defaultSign); ASSERT_EQUALS(1, platform.sizeof_bool); ASSERT_EQUALS(2, platform.sizeof_short); ASSERT_EQUALS(4, platform.sizeof_int); ASSERT_EQUALS(4, platform.sizeof_long); ASSERT_EQUALS(8, platform.sizeof_long_long); } #endif }; REGISTER_TEST(TestPlatform) cppcheck-1.90/test/testpostfixoperator.cpp000066400000000000000000000313651357737443600211110ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "checkpostfixoperator.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" class TestPostfixOperator : public TestFixture { public: TestPostfixOperator() : TestFixture("TestPostfixOperator") { } private: Settings settings; void check(const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); // Check for postfix operators.. CheckPostfixOperator checkPostfixOperator(&tokenizer, &settings, this); checkPostfixOperator.postfixOperator(); } void run() OVERRIDE { settings.addEnabled("performance"); TEST_CASE(testsimple); TEST_CASE(testfor); TEST_CASE(testvolatile); TEST_CASE(testiterator); TEST_CASE(test2168); TEST_CASE(pointerSimplest); TEST_CASE(pointer); // #2321 - postincrement of pointer is OK TEST_CASE(testtemplate); // #4686 TEST_CASE(testmember); TEST_CASE(testcomma); TEST_CASE(testauto); // #8350 } void testsimple() { check("int main()\n" "{\n" " unsigned int k(0);\n" " std::cout << k << std::endl;\n" " k++;\n" " std::cout << k << std::endl;\n" " if(k) {\n" " k++;\n" " }\n" " std::cout << k << std::endl;\n" " k--;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(0);\n" " std::cout << k << std::endl;\n" " k++;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("struct K {};" "void foo()\n" "{\n" " K k(0);\n" " k++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("struct K {};\n" "void foo(K& k)\n" "{\n" " k++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("union K {};" "void foo()\n" "{\n" " K k(0);\n" " k++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(1);\n" " std::cout << k << std::endl;\n" " if(k) {\n" " k++;\n" " }\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(1);\n" " std::cout << k << std::endl;\n" " if(k) {\n" " ++k;\n" " }\n" " k++;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(0);\n" " std::cout << k << std::endl;\n" " k--;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:5]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(0);\n" " std::cout << k << std::endl;\n" " ++k;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class K {};" "int main()\n" "{\n" " K k(0);\n" " std::cout << k << std::endl;\n" " --k;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9042 check("template \n" "class c {\n" " int i = 0;\n" " c() { i--; }\n" "};\n" "template \n" "class s {};\n" "using BOOL = char;"); ASSERT_EQUALS("", errout.str()); } void testfor() { check("int main()\n" "{\n" " for ( unsigned int i=0; i <= 10; i++) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class K {};\n" "int main()\n" "{\n" " for ( K i(0); i <= 10; i++) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};\n" "int main()\n" "{\n" " for ( K i(0); i <= 10; ++i) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); check("class K {};\n" "int main()\n" "{\n" " for ( K i(10); i > 1; i--) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("class K {};\n" "int main(int argc, char *argv[])\n" "{\n" " for ( K i=10; i > 1; --i) {\n" " std::cout << i << std::endl;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("", errout.str()); } void testvolatile() { check("class K {};\n" "int main()\n" "{\n" " volatile K k(0);\n" " std::cout << k << std::endl;\n" " k++;\n" " std::cout << k << std::endl;\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); } void testiterator() { check("class Base {};\n" "int main() {\n" " std::vector v;\n" " v.push_back(new Base());\n" " v.push_back(new Base());\n" " for (std::vector::iterator i=v.begin(); i!=v.end(); i++) {\n" " ;;\n" " }\n" " v.clear();\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("int main() {\n" " std::vector v;\n" " std::vector::iterator it;\n" " for( int i=0; i < 10; ++i ) v.push_back(i);\n" " unsigned int total = 0;\n" " it = v.begin();\n" " while( it != v.end() ) {\n" " total += *it;\n" " it++;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("int main() {\n" " std::vector v;\n" " std::vector::const_iterator it;\n" " for( int i=0; i < 10; ++i ) v.push_back(i);\n" " unsigned int total = 0;\n" " it = v.begin();\n" " while( it != v.end() ) {\n" " it++;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:8]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("int main() {\n" " std::vector v;\n" " std::vector::iterator it;\n" " for( int i=0; i < 10; ++i ) v.push_back(i);\n" " unsigned int total = 0;\n" " std::vector::reverse_iterator rit;\n" " rit= v.rend();\n" " while( rit != v.rbegin() ) {\n" " rit--;\n" " }\n" " return 0;\n" "}"); ASSERT_EQUALS("[test.cpp:9]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); } void test2168() { check("--> declare allocator lock here\n" "int main(){}"); ASSERT_EQUALS("", errout.str()); } void pointerSimplest() { check("void f(int* p){\n" " p++;\n" " std::cout << *p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void pointer() { check("static struct class * ab;\n" "int * p;\n" "\n" "void f() {\n" " p++;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void testtemplate() { check("bool foo() {\n" " std::vector::iterator aIter(aImport.begin());\n" " aIter++;\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); } void testmember() { check("bool foo() {\n" " class A {}; class B {A a;};\n" " B b;\n" " b.a++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("bool foo() {\n" " class A {}; class B {A a;};\n" " B b;\n" " foo(b.a++);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testcomma() { check("bool foo(int i) {\n" " class A {};\n" " A a;\n" " i++, a++;\n" "}"); ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout.str()); check("bool foo(int i) {\n" " class A {};\n" " A a;\n" " foo(i, a++);\n" " foo(a++, i);\n" "}"); ASSERT_EQUALS("", errout.str()); } void testauto() { // #8350 check("enum class Color { Red = 0, Green = 1, };\n" "int fun(const Color color) {\n" " auto a = 0;\n" " for (auto i = static_cast(color); i < 10; i++) {\n" " a += i;\n" " }\n" " return a;\n" "}"); ASSERT_EQUALS("", errout.str()); } }; REGISTER_TEST(TestPostfixOperator) cppcheck-1.90/test/testpreprocessor.cpp000066400000000000000000002546631357737443600203770ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // The preprocessor that Cppcheck uses is a bit special. Instead of generating // the code for a known configuration, it generates the code for each configuration. #include "platform.h" #include "preprocessor.h" #include "settings.h" #include "testsuite.h" #include #include #include #include #include #include #include class ErrorLogger; class TestPreprocessor : public TestFixture { public: TestPreprocessor() : TestFixture("TestPreprocessor") , preprocessor0(settings0, this) { settings0.addEnabled("information"); } class OurPreprocessor : public Preprocessor { public: static std::string expandMacros(const char code[], ErrorLogger *errorLogger = nullptr) { std::istringstream istr(code); simplecpp::OutputList outputList; std::vector files; const simplecpp::TokenList tokens1 = simplecpp::TokenList(istr, files, "file.cpp", &outputList); std::map filedata; simplecpp::TokenList tokens2(files); simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI(), &outputList); if (errorLogger) { static Settings settings; Preprocessor p(settings, errorLogger); p.reportOutput(outputList, true); } return tokens2.stringify(); } }; private: Settings settings0; Preprocessor preprocessor0; void run() OVERRIDE { // The bug that started the whole work with the new preprocessor TEST_CASE(Bug2190219); TEST_CASE(error1); // #error => don't extract any code TEST_CASE(error2); // #error if symbol is not defined TEST_CASE(error3); TEST_CASE(error4); // #2919 - wrong filename is reported TEST_CASE(error5); TEST_CASE(error6); TEST_CASE(error7); TEST_CASE(setPlatformInfo); // Handling include guards (don't create extra configuration for it) TEST_CASE(includeguard1); TEST_CASE(includeguard2); TEST_CASE(if0); TEST_CASE(if1); TEST_CASE(elif); TEST_CASE(if_cond1); TEST_CASE(if_cond2); TEST_CASE(if_cond3); TEST_CASE(if_cond4); TEST_CASE(if_cond5); TEST_CASE(if_cond6); TEST_CASE(if_cond8); TEST_CASE(if_cond9); TEST_CASE(if_cond10); TEST_CASE(if_cond11); TEST_CASE(if_cond12); TEST_CASE(if_cond13); TEST_CASE(if_cond14); TEST_CASE(if_or_1); TEST_CASE(if_or_2); TEST_CASE(if_macro_eq_macro); // #3536 TEST_CASE(ticket_3675); TEST_CASE(ticket_3699); TEST_CASE(ticket_4922); // #4922 // Macros.. TEST_CASE(macro_simple1); TEST_CASE(macro_simple2); TEST_CASE(macro_simple3); TEST_CASE(macro_simple4); TEST_CASE(macro_simple5); TEST_CASE(macro_simple6); TEST_CASE(macro_simple7); TEST_CASE(macro_simple8); TEST_CASE(macro_simple9); TEST_CASE(macro_simple10); TEST_CASE(macro_simple11); TEST_CASE(macro_simple12); TEST_CASE(macro_simple13); TEST_CASE(macro_simple14); TEST_CASE(macro_simple15); TEST_CASE(macro_simple16); // #4703: Macro parameters not trimmed TEST_CASE(macro_simple17); // #5074: isExpandedMacro not set TEST_CASE(macro_simple18); // (1e-7) TEST_CASE(macroInMacro1); TEST_CASE(macroInMacro2); TEST_CASE(macro_linenumbers); TEST_CASE(macro_nopar); TEST_CASE(macro_incdec); // separate ++ and -- with space when expanding such macro: '#define M(X) A-X' TEST_CASE(macro_switchCase); TEST_CASE(macro_NULL); // skip #define NULL .. it is replaced in the tokenizer TEST_CASE(string1); TEST_CASE(string2); TEST_CASE(string3); TEST_CASE(preprocessor_undef); TEST_CASE(defdef); // Defined multiple times TEST_CASE(preprocessor_doublesharp); TEST_CASE(preprocessor_include_in_str); TEST_CASE(va_args_1); //TEST_CASE(va_args_2); invalid code TEST_CASE(va_args_3); TEST_CASE(va_args_4); TEST_CASE(va_args_5); TEST_CASE(multi_character_character); TEST_CASE(stringify); TEST_CASE(stringify2); TEST_CASE(stringify3); TEST_CASE(stringify4); TEST_CASE(stringify5); TEST_CASE(ifdefwithfile); TEST_CASE(pragma); TEST_CASE(pragma_asm_1); TEST_CASE(pragma_asm_2); TEST_CASE(endifsemicolon); TEST_CASE(missing_doublequote); TEST_CASE(handle_error); TEST_CASE(dup_defines); TEST_CASE(define_part_of_func); TEST_CASE(conditionalDefine); TEST_CASE(macro_parameters); TEST_CASE(newline_in_macro); TEST_CASE(ifdef_ifdefined); // define and then ifdef TEST_CASE(define_if1); TEST_CASE(define_if2); TEST_CASE(define_if3); TEST_CASE(define_if4); // #4079 - #define X +123 TEST_CASE(define_if5); // #4516 - #define B (A & 0x00f0) TEST_CASE(define_if6); // #4863 - #define B (A?-1:1) TEST_CASE(define_ifdef); TEST_CASE(define_ifndef1); TEST_CASE(define_ifndef2); TEST_CASE(ifndef_define); TEST_CASE(undef_ifdef); TEST_CASE(endfile); TEST_CASE(redundant_config); TEST_CASE(invalid_define_1); // #2605 - hang for: '#define =' TEST_CASE(invalid_define_2); // #4036 - hang for: '#define () {(int f(x) }' // inline suppression, missingInclude TEST_CASE(inline_suppression_for_missing_include); // Using -D to predefine symbols TEST_CASE(predefine1); TEST_CASE(predefine2); TEST_CASE(predefine3); TEST_CASE(predefine4); TEST_CASE(predefine5); // automatically define __cplusplus TEST_CASE(invalidElIf); // #2942 segfault // Preprocessor::getConfigs TEST_CASE(getConfigs1); TEST_CASE(getConfigs2); TEST_CASE(getConfigs3); TEST_CASE(getConfigs4); TEST_CASE(getConfigs5); TEST_CASE(getConfigs7); TEST_CASE(getConfigs7a); TEST_CASE(getConfigs7b); TEST_CASE(getConfigs7c); TEST_CASE(getConfigs7d); TEST_CASE(getConfigs7e); TEST_CASE(getConfigs8); // #if A==1 => cfg: A=1 TEST_CASE(getConfigs10); // #5139 TEST_CASE(getConfigsError); TEST_CASE(getConfigsD1); TEST_CASE(getConfigsU1); TEST_CASE(getConfigsU2); TEST_CASE(getConfigsU3); TEST_CASE(getConfigsU4); TEST_CASE(getConfigsU5); TEST_CASE(getConfigsU6); TEST_CASE(getConfigsU7); TEST_CASE(validateCfg1); TEST_CASE(validateCfg2); TEST_CASE(if_sizeof); TEST_CASE(invalid_ifs); // #5909 TEST_CASE(garbage); TEST_CASE(wrongPathOnErrorDirective); TEST_CASE(testDirectiveIncludeTypes); TEST_CASE(testDirectiveIncludeLocations); TEST_CASE(testDirectiveIncludeComments); } void preprocess(const char* code, std::map& actual, const char filename[] = "file.c") { errout.str(""); std::istringstream istr(code); simplecpp::OutputList outputList; std::vector files; simplecpp::TokenList tokens(istr, files, filename, &outputList); tokens.removeComments(); preprocessor0.simplifyPragmaAsm(&tokens); const std::set configs(preprocessor0.getConfigs(tokens)); preprocessor0.setDirectives(tokens); for (const std::string & config : configs) { try { const std::string &cfgcode = preprocessor0.getcode(tokens, config, files, std::string(code).find("#file") != std::string::npos); actual[config] = cfgcode; } catch (const simplecpp::Output &) { actual[config] = ""; } catch (...) { } } } std::string getConfigsStr(const char filedata[], const char *arg = nullptr) { Settings settings; if (arg && std::strncmp(arg,"-D",2)==0) settings.userDefines = arg + 2; if (arg && std::strncmp(arg,"-U",2)==0) settings.userUndefs.insert(arg+2); Preprocessor preprocessor(settings, this); std::vector files; std::istringstream istr(filedata); simplecpp::TokenList tokens(istr,files); tokens.removeComments(); const std::set configs = preprocessor.getConfigs(tokens); std::string ret; for (const std::string & config : configs) ret += config + '\n'; return ret; } void Bug2190219() { const char filedata[] = "#ifdef __cplusplus\n" "cpp\n" "#else\n" "c\n" "#endif"; { // Preprocess => actual result.. std::map actual; preprocess(filedata, actual, "file.cpp"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\ncpp", actual[""]); } { // Ticket #7102 - skip __cplusplus in C code // Preprocess => actual result.. std::map actual; preprocess(filedata, actual, "file.c"); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\nc", actual[""]); } } void error1() { const char filedata[] = "#ifdef A\n" ";\n" "#else\n" "#error abcd\n" "#endif\n"; ASSERT_EQUALS("\nA\n", getConfigsStr(filedata)); } void error2() { const char filedata1[] = "#ifndef A\n" "#error\n" "#endif\n"; ASSERT_EQUALS("A\n", getConfigsStr(filedata1)); const char filedata2[] = "#if !A\n" "#error\n" "#endif\n"; ASSERT_EQUALS("A\n", getConfigsStr(filedata2)); } void error3() { errout.str(""); Settings settings; settings.userDefines = "__cplusplus"; Preprocessor preprocessor(settings, this); const std::string code("#error hello world!\n"); preprocessor.getcode(code, "X", "test.c"); ASSERT_EQUALS("[test.c:1]: (error) #error hello world!\n", errout.str()); } // Ticket #2919 - wrong filename reported for #error void error4() { // In included file { errout.str(""); Settings settings; settings.userDefines = "TEST"; Preprocessor preprocessor(settings, this); const std::string code("#file \"ab.h\"\n#error hello world!\n#endfile"); preprocessor.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("[ab.h:1]: (error) #error hello world!\n", errout.str()); } // After including a file { errout.str(""); Settings settings; settings.userDefines = "TEST"; Preprocessor preprocessor(settings, this); const std::string code("#file \"ab.h\"\n\n#endfile\n#error aaa"); preprocessor.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("[test.c:2]: (error) #error aaa\n", errout.str()); } } void error5() { errout.str(""); Settings settings; settings.userDefines = "FOO"; settings.force = true; // No message if --force is given Preprocessor preprocessor(settings, this); const std::string code("#error hello world!\n"); preprocessor.getcode(code, "X", "test.c"); ASSERT_EQUALS("", errout.str()); } void error6() { const char filedata1[] = "#ifdef A\n" "#else\n" "#error 1\n" "#endif\n" "#ifdef B\n" "#else\n" "#error 2\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B\nB\n", getConfigsStr(filedata1)); const char filedata2[] = "#ifndef A\n" "#error 1\n" "#endif\n" "#ifndef B\n" "#error 2\n" "#endif\n"; ASSERT_EQUALS("A;B\n", getConfigsStr(filedata2)); const char filedata3[] = "#if !A\n" "#error 1\n" "#endif\n" "#if !B\n" "#error 2\n" "#endif\n"; ASSERT_EQUALS("A;B\n", getConfigsStr(filedata3)); } void error7() { // #8074 const char filedata[] = "#define A\n" "\n" "#if defined(B)\n" "#else\n" "#error \"1\"\n" "#endif\n" "\n" "#if defined(A)\n" "#else\n" "#error \"2\"\n" "#endif\n"; ASSERT_EQUALS("\nB\n", getConfigsStr(filedata)); } void setPlatformInfo() { Settings settings; Preprocessor preprocessor(settings, this); // read code with simplecpp.. const char filedata[] = "#if sizeof(long) == 4\n" "1\n" "#else\n" "2\n" "#endif\n"; std::istringstream istr(filedata); std::vector files; simplecpp::TokenList tokens(istr, files, "test.c"); // preprocess code with unix32 platform.. settings.platform(Settings::PlatformType::Unix32); preprocessor.setPlatformInfo(&tokens); ASSERT_EQUALS("\n1", preprocessor.getcode(tokens, "", files, false)); // preprocess code with unix64 platform.. settings.platform(Settings::PlatformType::Unix64); preprocessor.setPlatformInfo(&tokens); ASSERT_EQUALS("\n\n\n2", preprocessor.getcode(tokens, "", files, false)); } void includeguard1() { // Handling include guards.. const char filedata[] = "#file \"abc.h\"\n" "#ifndef abcH\n" "#define abcH\n" "#endif\n" "#endfile\n" "#ifdef ABC\n" "#endif"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void includeguard2() { // Handling include guards.. const char filedata[] = "#file \"abc.h\"\n" "foo\n" "#ifdef ABC\n" "\n" "#endif\n" "#endfile\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void ifdefwithfile() { // Handling include guards.. const char filedata[] = "#ifdef ABC\n" "#file \"abc.h\"\n" "class A{};/*\n\n\n\n\n\n\n*/\n" "#endfile\n" "#endif\n" "int main() {}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Expected configurations: "" and "ABC" ASSERT_EQUALS(2, actual.size()); ASSERT_EQUALS("\n\n\nint main ( ) { }", actual[""]); ASSERT_EQUALS("\n#line 1 \"abc.h\"\nclass A { } ;\n#line 4 \"file.c\"\n int main ( ) { }", actual["ABC"]); } void if0() { const char filedata[] = " # if /* comment */ 0 // comment\n" "#ifdef WIN32\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if1() { const char filedata[] = " # if /* comment */ 1 // comment\n" "ABC\n" " # endif \n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void elif() { { const char filedata[] = "#if DEF1\n" "ABC\n" "#elif DEF2\n" "DEF\n" "#endif\n"; ASSERT_EQUALS("\nDEF1\nDEF2\n", getConfigsStr(filedata)); } { const char filedata[] = "#if(defined DEF1)\n" "ABC\n" "#elif(defined DEF2)\n" "DEF\n" "#else\n" "GHI\n" "#endif\n"; ASSERT_EQUALS("\nDEF1\nDEF2\n", getConfigsStr(filedata)); } } void if_cond1() { const char filedata[] = "#if LIBVER>100\n" " A\n" "#else\n" " B\n" "#endif\n"; TODO_ASSERT_EQUALS("\nLIBVER=101\n", "\n", getConfigsStr(filedata)); } void if_cond2() { const char filedata[] = "#ifdef A\n" "a\n" "#endif\n" "#if defined(A) && defined(B)\n" "ab\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B\n", getConfigsStr(filedata)); if_cond2b(); if_cond2c(); if_cond2d(); if_cond2e(); } void if_cond2b() { const char filedata[] = "#ifndef A\n" "!a\n" "#ifdef B\n" "b\n" "#endif\n" "#else\n" "a\n" "#endif\n"; TODO_ASSERT_EQUALS("\nA;B\n", "\nA\nB\n", getConfigsStr(filedata)); } void if_cond2c() { const char filedata[] = "#ifndef A\n" "!a\n" "#ifdef B\n" "b\n" "#else\n" "!b\n" "#endif\n" "#else\n" "a\n" "#endif\n"; TODO_ASSERT_EQUALS("\nA\nA;B\n", "\nA\nB\n", getConfigsStr(filedata)); } void if_cond2d() { const char filedata[] = "#ifndef A\n" "!a\n" "#ifdef B\n" "b\n" "#else\n" "!b\n" "#endif\n" "#else\n" "a\n" "#ifdef B\n" "b\n" "#else\n" "!b\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B\nB\n", getConfigsStr(filedata)); } void if_cond2e() { const char filedata[] = "#if !defined(A)\n" "!a\n" "#elif !defined(B)\n" "!b\n" "#endif\n"; ASSERT_EQUALS("\nA\nB\n", getConfigsStr(filedata)); } void if_cond3() { const char filedata[] = "#ifdef A\n" "a\n" "#if defined(B) && defined(C)\n" "abc\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B;C\n", getConfigsStr(filedata)); } void if_cond4() { { const char filedata[] = "#define A\n" "#define B\n" "#if defined A || defined B\n" "ab\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } { const char filedata[] = "#if A\n" "{\n" "#if (defined(B))\n" "foo();\n" "#endif\n" "}\n" "#endif\n"; ASSERT_EQUALS("\nA\nA;B\n", getConfigsStr(filedata)); } { const char filedata[] = "#define A\n" "#define B\n" "#if (defined A) || defined (B)\n" "ab\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } { const char filedata[] = "#if (A)\n" "foo();\n" "#endif\n"; ASSERT_EQUALS("\nA\n", getConfigsStr(filedata)); } { const char filedata[] = "#if! A\n" "foo();\n" "#endif\n"; ASSERT_EQUALS("\nA=0\n", getConfigsStr(filedata)); } } void if_cond5() { const char filedata[] = "#if defined(A) && defined(B)\n" "ab\n" "#endif\n" "cd\n" "#if defined(B) && defined(A)\n" "ef\n" "#endif\n"; ASSERT_EQUALS("\nA;B\n", getConfigsStr(filedata)); } void if_cond6() { const char filedata[] = "\n" "#if defined(A) && defined(B))\n" "#endif\n"; ASSERT_EQUALS("\nA;B\n", getConfigsStr(filedata)); } void if_cond8() { const char filedata[] = "#if defined(A) + defined(B) + defined(C) != 1\n" "#endif\n"; TODO_ASSERT_EQUALS("\nA\n", "\nA;B;C\n", getConfigsStr(filedata)); } void if_cond9() { const char filedata[] = "#if !defined _A\n" "abc\n" "#endif\n"; ASSERT_EQUALS("\n_A\n", getConfigsStr(filedata)); } void if_cond10() { const char filedata[] = "#if !defined(a) && !defined(b)\n" "#if defined(and)\n" "#endif\n" "#endif\n"; // Preprocess => don't crash.. std::map actual; preprocess(filedata, actual); } void if_cond11() { const char filedata[] = "#if defined(L_fixunssfdi) && LIBGCC2_HAS_SF_MODE\n" "#if LIBGCC2_HAS_DF_MODE\n" "#elif FLT_MANT_DIG < W_TYPE_SIZE\n" "#endif\n" "#endif\n"; std::map actual; preprocess(filedata, actual); ASSERT_EQUALS("", errout.str()); } void if_cond12() { const char filedata[] = "#define A (1)\n" "#if A == 1\n" ";\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_cond13() { const char filedata[] = "#if ('A' == 0x41)\n" "123\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_cond14() { const char filedata[] = "#if !(A)\n" "123\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void if_or_1() { const char filedata[] = "#if defined(DEF_10) || defined(DEF_11)\n" "a1;\n" "#endif\n"; ASSERT_EQUALS("\nDEF_10;DEF_11\n", getConfigsStr(filedata)); } void if_or_2() { const char filedata[] = "#if X || Y\n" "a1;\n" "#endif\n"; TODO_ASSERT_EQUALS("\nX;Y\n", "\n", getConfigsStr(filedata)); } void if_macro_eq_macro() { const char *code = "#define A B\n" "#define B 1\n" "#define C 1\n" "#if A == C\n" "Wilma\n" "#else\n" "Betty\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(code)); } void ticket_3675() { const char* code = "#ifdef YYSTACKSIZE\n" "#define YYMAXDEPTH YYSTACKSIZE\n" "#else\n" "#define YYSTACKSIZE YYMAXDEPTH\n" "#endif\n" "#if YYDEBUG\n" "#endif\n"; std::map actual; preprocess(code, actual); // There's nothing to assert. It just needs to not hang. } void ticket_3699() { const char* code = "#define INLINE __forceinline\n" "#define inline __forceinline\n" "#define __forceinline inline\n" "#if !defined(_WIN32)\n" "#endif\n" "INLINE inline __forceinline\n"; std::map actual; preprocess(code, actual); // First, it must not hang. Second, inline must becomes inline, and __forceinline must become __forceinline. ASSERT_EQUALS("\n\n\n\n\n$__forceinline $inline $__forceinline", actual[""]); } void ticket_4922() { // #4922 const char* code = "__asm__ \n" "{ int extern __value) 0; (double return (\"\" } extern\n" "__typeof __finite (__finite) __finite __inline \"__GI___finite\");"; std::map actual; preprocess(code, actual); } void macro_simple1() const { { const char filedata[] = "#define AAA(aa) f(aa)\n" "AAA(5);\n"; ASSERT_EQUALS("\nf ( 5 ) ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define AAA(aa) f(aa)\n" "AAA (5);\n"; ASSERT_EQUALS("\nf ( 5 ) ;", OurPreprocessor::expandMacros(filedata)); } } void macro_simple2() const { const char filedata[] = "#define min(x,y) x 0 ) return 1 ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple5() const { const char filedata[] = "#define ABC if( temp > 0 ) return 1;\n" "\n" "void foo()\n" "{\n" " int temp = 0;\n" " ABC\n" "}\n"; ASSERT_EQUALS("\n\nvoid foo ( )\n{\nint temp = 0 ;\nif ( temp > 0 ) return 1 ;\n}", OurPreprocessor::expandMacros(filedata)); } void macro_simple6() const { const char filedata[] = "#define ABC (a+b+c)\n" "ABC\n"; ASSERT_EQUALS("\n( a + b + c )", OurPreprocessor::expandMacros(filedata)); } void macro_simple7() const { const char filedata[] = "#define ABC(str) str\n" "ABC(\"(\")\n"; ASSERT_EQUALS("\n\"(\"", OurPreprocessor::expandMacros(filedata)); } void macro_simple8() const { const char filedata[] = "#define ABC 123\n" "#define ABCD 1234\n" "ABC ABCD\n"; ASSERT_EQUALS("\n\n123 1234", OurPreprocessor::expandMacros(filedata)); } void macro_simple9() const { const char filedata[] = "#define ABC(a) f(a)\n" "ABC( \"\\\"\" );\n" "ABC( \"g\" );\n"; ASSERT_EQUALS("\nf ( \"\\\"\" ) ;\nf ( \"g\" ) ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple10() const { const char filedata[] = "#define ABC(t) t x\n" "ABC(unsigned long);\n"; ASSERT_EQUALS("\nunsigned long x ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple11() const { const char filedata[] = "#define ABC(x) delete x\n" "ABC(a);\n"; ASSERT_EQUALS("\ndelete a ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple12() const { const char filedata[] = "#define AB ab.AB\n" "AB.CD\n"; ASSERT_EQUALS("\nab . AB . CD", OurPreprocessor::expandMacros(filedata)); } void macro_simple13() const { const char filedata[] = "#define TRACE(x)\n" "TRACE(;if(a))\n"; ASSERT_EQUALS("", OurPreprocessor::expandMacros(filedata)); } void macro_simple14() const { const char filedata[] = "#define A \" a \"\n" "printf(A);\n"; ASSERT_EQUALS("\nprintf ( \" a \" ) ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple15() const { const char filedata[] = "#define FOO\"foo\"\n" "FOO\n"; ASSERT_EQUALS("\n\"foo\"", OurPreprocessor::expandMacros(filedata)); } void macro_simple16() const { // # 4703 const char filedata[] = "#define MACRO( A, B, C ) class A##B##C##Creator {};\n" "MACRO( B\t, U , G )"; ASSERT_EQUALS("\nclass BUGCreator { } ;", OurPreprocessor::expandMacros(filedata)); } void macro_simple17() const { // # 5074 - the Token::isExpandedMacro() doesn't always indicate properly if token comes from macro // It would probably be OK if the generated code was // "\n123+$123" since the first 123 comes from the source code const char filedata[] = "#define MACRO(A) A+123\n" "MACRO(123)"; ASSERT_EQUALS("\n123 + 123", OurPreprocessor::expandMacros(filedata)); } void macro_simple18() const { // (1e-7) const char filedata1[] = "#define A (1e-7)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1e-7 ) ;", OurPreprocessor::expandMacros(filedata1)); const char filedata2[] = "#define A (1E-7)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1E-7 ) ;", OurPreprocessor::expandMacros(filedata2)); const char filedata3[] = "#define A (1e+7)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1e+7 ) ;", OurPreprocessor::expandMacros(filedata3)); const char filedata4[] = "#define A (1.e+7)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1.e+7 ) ;", OurPreprocessor::expandMacros(filedata4)); const char filedata5[] = "#define A (1.7f)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1.7f ) ;", OurPreprocessor::expandMacros(filedata5)); const char filedata6[] = "#define A (.1)\n" "a=A;"; ASSERT_EQUALS("\na = ( .1 ) ;", OurPreprocessor::expandMacros(filedata6)); const char filedata7[] = "#define A (1.)\n" "a=A;"; ASSERT_EQUALS("\na = ( 1. ) ;", OurPreprocessor::expandMacros(filedata7)); const char filedata8[] = "#define A (8.0E+007)\n" "a=A;"; ASSERT_EQUALS("\na = ( 8.0E+007 ) ;", OurPreprocessor::expandMacros(filedata8)); } void macroInMacro1() const { { const char filedata[] = "#define A(m) long n = m; n++;\n" "#define B(n) A(n)\n" "B(0)\n"; ASSERT_EQUALS("\n\nlong n = 0 ; n ++ ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A B\n" "#define B 3\n" "A\n"; ASSERT_EQUALS("\n\n3", OurPreprocessor::expandMacros(filedata)); } { /* TODO: What to do here? since there are syntax error simplecpp outputs "" const char filedata[] = "#define BC(b, c...) 0##b * 0##c\n" "#define ABC(a, b...) a + BC(b)\n" "\n" "ABC(1);\n" // <- too few parameters "ABC(2,3);\n" "ABC(4,5,6);\n"; ASSERT_EQUALS("\n\n\n1 + 0 * 0;\n2 + 03 * 0;\n4 + 05 * 06;", OurPreprocessor::expandMacros(filedata)); */ } { const char filedata[] = "#define A 4\n" "#define B(a) a,A\n" "B(2);\n"; ASSERT_EQUALS("\n\n2 , 4 ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A(x) (x)\n" "#define B )A(\n" "#define C )A(\n"; ASSERT_EQUALS("", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A(x) (x*2)\n" "#define B A(\n" "foo B(i));\n"; ASSERT_EQUALS("\n\nfoo ( ( i ) * 2 ) ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define foo foo\n" "foo\n"; ASSERT_EQUALS("\nfoo", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define B(A1, A2) } while (0)\n" "#define A(name) void foo##name() { do { B(1, 2); }\n" "A(0)\n" "A(1)\n"; ASSERT_EQUALS("\n\nvoid foo0 ( ) { do { } while ( 0 ) ; }\nvoid foo1 ( ) { do { } while ( 0 ) ; }", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define B(x) (\n" "#define A() B(xx)\n" "B(1) A() ) )\n"; ASSERT_EQUALS("\n\n( ( ) )", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define PTR1 (\n" "#define PTR2 PTR1 PTR1\n" "int PTR2 PTR2 foo )))) = 0;\n"; ASSERT_EQUALS("\n\nint ( ( ( ( foo ) ) ) ) = 0 ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define PTR1 (\n" "PTR1 PTR1\n"; ASSERT_EQUALS("\n( (", OurPreprocessor::expandMacros(filedata)); } } void macroInMacro2() const { const char filedata[] = "#define A(x) a##x\n" "#define B 0\n" "A(B)\n"; ASSERT_EQUALS("\n\naB", OurPreprocessor::expandMacros(filedata)); } void macro_linenumbers() const { const char filedata[] = "#define AAA(a)\n" "AAA(5\n" "\n" ")\n" "int a;\n"; ASSERT_EQUALS("\n" "\n" "\n" "\n" "int a ;", OurPreprocessor::expandMacros(filedata)); } void macro_nopar() const { const char filedata[] = "#define AAA( ) { NULL }\n" "AAA()\n"; ASSERT_EQUALS("\n{ NULL }", OurPreprocessor::expandMacros(filedata)); } void macro_incdec() const { const char filedata[] = "#define M1(X) 1+X\n" "#define M2(X) 2-X\n" "M1(+1) M2(-1)\n"; ASSERT_EQUALS("\n\n1 + + 1 2 - - 1", OurPreprocessor::expandMacros(filedata)); } void macro_switchCase() const { { // Make sure "case 2" doesn't become "case2" const char filedata[] = "#define A( b ) " "switch( a ){ " "case 2: " " break; " "}\n" "A( 5 );\n"; ASSERT_EQUALS("\nswitch ( a ) { case 2 : break ; } ;", OurPreprocessor::expandMacros(filedata)); } { // Make sure "2 BB" doesn't become "2BB" const char filedata[] = "#define A() AA : 2 BB\n" "A();\n"; ASSERT_EQUALS("\nAA : 2 BB ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A }\n" "#define B() A\n" "#define C( a ) B() break;\n" "{C( 2 );\n"; ASSERT_EQUALS("\n\n\n{ } break ; ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A }\n" "#define B() A\n" "#define C( a ) B() _break;\n" "{C( 2 );\n"; ASSERT_EQUALS("\n\n\n{ } _break ; ;", OurPreprocessor::expandMacros(filedata)); } { const char filedata[] = "#define A }\n" "#define B() A\n" "#define C( a ) B() 5;\n" "{C( 2 );\n"; ASSERT_EQUALS("\n\n\n{ } 5 ; ;", OurPreprocessor::expandMacros(filedata)); } } void macro_NULL() const { // Let the tokenizer handle NULL. // See ticket #4482 - UB when passing NULL to variadic function ASSERT_EQUALS("\n0", OurPreprocessor::expandMacros("#define null 0\nnull")); // TODO ASSERT_EQUALS("\nNULL", OurPreprocessor::expandMacros("#define NULL 0\nNULL")); } void string1() { const char filedata[] = "int main()" "{" " const char *a = \"#define A\";" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("int main ( ) { const char * a = \"#define A\" ; }", actual[""]); } void string2() const { const char filedata[] = "#define AAA 123\n" "str = \"AAA\"\n"; // Compare results.. ASSERT_EQUALS("\nstr = \"AAA\"", OurPreprocessor::expandMacros(filedata)); } void string3() const { const char filedata[] = "str(\";\");\n"; // Compare results.. ASSERT_EQUALS("str ( \";\" ) ;", OurPreprocessor::expandMacros(filedata)); } void preprocessor_undef() { { const char filedata[] = "#define AAA int a;\n" "#undef AAA\n" "#define AAA char b=0;\n" "AAA\n"; // Compare results.. ASSERT_EQUALS("\n\n\nchar b = 0 ;", OurPreprocessor::expandMacros(filedata)); } { // ticket #403 const char filedata[] = "#define z p[2]\n" "#undef z\n" "int z;\n" "z = 0;\n"; ASSERT_EQUALS("\n\nint z ;\nz = 0 ;", preprocessor0.getcode(filedata, "", "")); } } void defdef() const { const char filedata[] = "#define AAA 123\n" "#define AAA 456\n" "#define AAA 789\n" "AAA\n"; // Compare results.. ASSERT_EQUALS("\n\n\n789", OurPreprocessor::expandMacros(filedata)); } void preprocessor_doublesharp() const { // simple testcase without ## const char filedata1[] = "#define TEST(var,val) var = val\n" "TEST(foo,20);\n"; ASSERT_EQUALS("\nfoo = 20 ;", OurPreprocessor::expandMacros(filedata1)); // simple testcase with ## const char filedata2[] = "#define TEST(var,val) var##_##val = val\n" "TEST(foo,20);\n"; ASSERT_EQUALS("\nfoo_20 = 20 ;", OurPreprocessor::expandMacros(filedata2)); // concat macroname const char filedata3[] = "#define ABCD 123\n" "#define A(B) A##B\n" "A(BCD)\n"; ASSERT_EQUALS("\n\n123", OurPreprocessor::expandMacros(filedata3)); // Ticket #1802 - inner ## must be expanded before outer macro const char filedata4[] = "#define A(B) A##B\n" "#define a(B) A(B)\n" "a(A(B))\n"; ASSERT_EQUALS("\n\nAAB", OurPreprocessor::expandMacros(filedata4)); // Ticket #1802 - inner ## must be expanded before outer macro const char filedata5[] = "#define AB(A,B) A##B\n" "#define ab(A,B) AB(A,B)\n" "ab(a,AB(b,c))\n"; ASSERT_EQUALS("\n\nabc", OurPreprocessor::expandMacros(filedata5)); // Ticket #1802 const char filedata6[] = "#define AB_(A,B) A ## B\n" "#define AB(A,B) AB_(A,B)\n" "#define ab(suf) AB(X, AB_(_, suf))\n" "#define X x\n" "ab(y)\n"; ASSERT_EQUALS("\n\n\n\nx_y", OurPreprocessor::expandMacros(filedata6)); } void preprocessor_include_in_str() { const char filedata[] = "int main()\n" "{\n" "const char *a = \"#include \";\n" "return 0;\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("int main ( )\n{\nconst char * a = \"#include \" ;\nreturn 0 ;\n}", actual[""]); } void va_args_1() const { const char filedata[] = "#define DBG(fmt...) printf(fmt)\n" "DBG(\"[0x%lx-0x%lx)\", pstart, pend);\n"; // Preprocess.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\nprintf ( \"[0x%lx-0x%lx)\" , pstart , pend ) ;", actual); } /* void va_args_2() const { const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" "DBG(\"hello\");\n"; // Preprocess.. std::string actual = OurPreprocessor::expandMacros(filedata); // invalid code ASSERT_EQUALS("\nprintf ( \"hello\" ) ;", actual); } */ void va_args_3() const { const char filedata[] = "#define FRED(...) { fred(__VA_ARGS__); }\n" "FRED(123)\n"; ASSERT_EQUALS("\n{ fred ( 123 ) ; }", OurPreprocessor::expandMacros(filedata)); } void va_args_4() const { const char filedata[] = "#define FRED(name, ...) name (__VA_ARGS__)\n" "FRED(abc, 123)\n"; ASSERT_EQUALS("\nabc ( 123 )", OurPreprocessor::expandMacros(filedata)); } void va_args_5() const { const char filedata1[] = "#define A(...) #__VA_ARGS__\n" "A(123)\n"; ASSERT_EQUALS("\n\"123\"", OurPreprocessor::expandMacros(filedata1)); const char filedata2[] = "#define A(X,...) X(#__VA_ARGS__)\n" "A(f,123)\n"; ASSERT_EQUALS("\nf ( \"123\" )", OurPreprocessor::expandMacros(filedata2)); } void multi_character_character() { const char filedata[] = "#define FOO 'ABCD'\n" "int main()\n" "{\n" "if( FOO == 0 );\n" "return 0;\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("\nint main ( )\n{\nif ( $'ABCD' == 0 ) ;\nreturn 0 ;\n}", actual[""]); } void stringify() const { const char filedata[] = "#define STRINGIFY(x) #x\n" "STRINGIFY(abc)\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\n\"abc\"", actual); } void stringify2() const { const char filedata[] = "#define A(x) g(#x)\n" "A(abc);\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\ng ( \"abc\" ) ;", actual); } void stringify3() const { const char filedata[] = "#define A(x) g(#x)\n" "A( abc);\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\ng ( \"abc\" ) ;", actual); } void stringify4() const { const char filedata[] = "#define A(x) #x\n" "1 A(\n" "abc\n" ") 2\n"; // expand macros.. std::string actual = OurPreprocessor::expandMacros(filedata); ASSERT_EQUALS("\n1 \"abc\"\n\n2", actual); } void stringify5() const { const char filedata[] = "#define A(x) a(#x,x)\n" "A(foo(\"\\\"\"))\n"; ASSERT_EQUALS("\na ( \"foo(\\\"\\\\\\\"\\\")\" , foo ( \"\\\"\" ) )", OurPreprocessor::expandMacros(filedata)); } void pragma() { const char filedata[] = "#pragma once\n" "void f()\n" "{\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("\nvoid f ( )\n{\n}", actual[""]); } void pragma_asm_1() { const char filedata[] = "#pragma asm\n" " mov r1, 11\n" "#pragma endasm\n" "aaa\n" "#pragma asm foo\n" " mov r1, 11\n" "#pragma endasm bar\n" "bbb"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("asm ( )\n;\n\naaa\nasm ( ) ;\n\n\nbbb", actual[""]); } void pragma_asm_2() { const char filedata[] = "#pragma asm\n" " mov @w1, 11\n" "#pragma endasm ( temp=@w1 )\n" "bbb"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("asm ( )\n;\n\nbbb", actual[""]); } void endifsemicolon() { const char filedata[] = "void f() {\n" "#ifdef A\n" "#endif;\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(2, actual.size()); const std::string expected("void f ( ) {\n\n\n}"); ASSERT_EQUALS(expected, actual[""]); ASSERT_EQUALS(expected, actual["A"]); } void handle_error() { { const char filedata[] = "#define A \n" "#define B don't want to \\\n" "more text\n" "void f()\n" "{\n" " char a = 'a'; // '\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); ASSERT_EQUALS("", actual[""]); ASSERT_EQUALS("", errout.str()); } } void missing_doublequote() { { const char filedata[] = "#define a\n" "#ifdef 1\n" "\"\n" "#endif\n"; // expand macros.. errout.str(""); const std::string actual(OurPreprocessor::expandMacros(filedata, this)); ASSERT_EQUALS("", actual); ASSERT_EQUALS("[file.cpp:3]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } { const char filedata[] = "#file \"abc.h\"\n" "#define a\n" "\"\n" "#endfile\n"; // expand macros.. errout.str(""); const std::string actual(OurPreprocessor::expandMacros(filedata, this)); ASSERT_EQUALS("", actual); ASSERT_EQUALS("[abc.h:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } { const char filedata[] = "#file \"abc.h\"\n" "#define a\n" "#endfile\n" "\"\n"; // expand macros.. errout.str(""); const std::string actual(OurPreprocessor::expandMacros(filedata, this)); ASSERT_EQUALS("", actual); ASSERT_EQUALS("[file.cpp:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } { const char filedata[] = "#define A 1\n" "#define B \"\n" "int a = A;\n"; // expand macros.. errout.str(""); const std::string actual(OurPreprocessor::expandMacros(filedata, this)); ASSERT_EQUALS("", actual); ASSERT_EQUALS("[file.cpp:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } { const char filedata[] = "void foo()\n" "{\n" "\n" "\n" "\n" "int a = 0;\n" "printf(Text\");\n" "}\n"; // expand macros.. errout.str(""); OurPreprocessor::expandMacros(filedata, this); ASSERT_EQUALS("[file.cpp:7]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout.str()); } } void define_part_of_func() { const char filedata[] = "#define A g(\n" "void f() {\n" " A );\n" " }\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("\nvoid f ( ) {\n$g $( ) ;\n}", actual[""]); ASSERT_EQUALS("", errout.str()); } void conditionalDefine() { const char filedata[] = "#ifdef A\n" "#define N 10\n" "#else\n" "#define N 20\n" "#endif\n" "N"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(2, actual.size()); ASSERT_EQUALS("\n\n\n\n\n$20", actual[""]); ASSERT_EQUALS("\n\n\n\n\n$10", actual["A"]); ASSERT_EQUALS("", errout.str()); } void macro_parameters() { errout.str(""); const char filedata[] = "#define BC(a, b, c, arg...) \\\n" "AB(a, b, c, ## arg)\n" "\n" "void f()\n" "{\n" " BC(3);\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("", actual[""]); ASSERT_EQUALS("[file.c:6]: (error) failed to expand 'BC', Wrong number of parameters for macro 'BC'.\n", errout.str()); } void newline_in_macro() { const char filedata[] = "#define ABC(str) printf( str )\n" "void f()\n" "{\n" " ABC(\"\\n\");\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, actual.size()); ASSERT_EQUALS("\nvoid f ( )\n{\n$printf $( \"\\n\" $) ;\n}", actual[""]); ASSERT_EQUALS("", errout.str()); } void ifdef_ifdefined() { const char filedata[] = "#ifdef ABC\n" "A\n" "#endif\t\n" "#if defined ABC\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS("", actual[""]); ASSERT_EQUALS("\nA\n\n\nA", actual["ABC"]); ASSERT_EQUALS(2, actual.size()); } void define_if1() { { const char filedata[] = "#define A 0\n" "#if A\n" "FOO\n" "#endif"; ASSERT_EQUALS("", preprocessor0.getcode(filedata,"","")); } { const char filedata[] = "#define A 1\n" "#if A==1\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO", preprocessor0.getcode(filedata,"","")); } } void define_if2() { const char filedata[] = "#define A 22\n" "#define B A\n" "#if (B==A) || (B==C)\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\n\nFOO", preprocessor0.getcode(filedata,"","")); } void define_if3() { const char filedata[] = "#define A 0\n" "#if (A==0)\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO", preprocessor0.getcode(filedata,"","")); } void define_if4() { const char filedata[] = "#define X +123\n" "#if X==123\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO", preprocessor0.getcode(filedata,"","")); } void define_if5() { // #4516 - #define B (A & 0x00f0) { const char filedata[] = "#define A 0x0010\n" "#define B (A & 0x00f0)\n" "#if B==0x0010\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\n\nFOO", preprocessor0.getcode(filedata,"","")); } { const char filedata[] = "#define A 0x00f0\n" "#define B (16)\n" "#define C (B & A)\n" "#if C==0x0010\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\n\n\nFOO", preprocessor0.getcode(filedata,"","")); } { const char filedata[] = "#define A (1+A)\n" // don't hang for recursive macros "#if A==1\n" "FOO\n" "#endif"; ASSERT_EQUALS("\n\nFOO", preprocessor0.getcode(filedata,"","")); } } void define_if6() { // #4516 - #define B (A?1:-1) const char filedata[] = "#ifdef A\n" "#define B (A?1:-1)\n" "#endif\n" "\n" "#if B < 0\n" "123\n" "#endif\n" "\n" "#if B >= 0\n" "456\n" "#endif\n"; const std::string actualA0 = preprocessor0.getcode(filedata, "A=0", "test.c"); ASSERT_EQUALS(true, actualA0.find("123") != std::string::npos); ASSERT_EQUALS(false, actualA0.find("456") != std::string::npos); const std::string actualA1 = preprocessor0.getcode(filedata, "A=1", "test.c"); ASSERT_EQUALS(false, actualA1.find("123") != std::string::npos); ASSERT_EQUALS(true, actualA1.find("456") != std::string::npos); } void define_ifdef() { { const char filedata[] = "#define ABC\n" "#ifndef ABC\n" "A\n" "#else\n" "B\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n\n\nB", actual[""]); } { const char filedata[] = "#define A 1\n" "#ifdef A\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n$1", actual[""]); } { const char filedata[] = "#define A 1\n" "#if A==1\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n$1", actual[""]); } { const char filedata[] = "#define A 1\n" "#if A>0\n" "A\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n$1", actual[""]); } { const char filedata[] = "#define A 1\n" "#if 0\n" "#undef A\n" "#endif\n" "A\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS("\n\n\n\n$1", actual[""]); } } void define_ifndef1() { const char filedata[] = "#define A(x) (x)\n" "#ifndef A\n" ";\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("", actual[""]); } void define_ifndef2() { const char filedata[] = "#ifdef A\n" "#define B char\n" "#endif\n" "#ifndef B\n" "#define B int\n" "#endif\n" "B me;\n"; // Preprocess => actual result.. ASSERT_EQUALS("\n\n\n\n\n\n$int me ;", preprocessor0.getcode(filedata, "", "a.cpp")); ASSERT_EQUALS("\n\n\n\n\n\n$char me ;", preprocessor0.getcode(filedata, "A", "a.cpp")); } void ifndef_define() { const char filedata[] = "#ifndef A\n" "#define A(x) x\n" "#endif\n" "A(123);"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); ASSERT_EQUALS(1U, actual.size()); ASSERT_EQUALS("\n\n\n123 ;", actual[""]); } void undef_ifdef() { const char filedata[] = "#undef A\n" "#ifdef A\n" "123\n" "#endif\n"; // Preprocess => actual result.. ASSERT_EQUALS("", preprocessor0.getcode(filedata, "", "a.cpp")); ASSERT_EQUALS("", preprocessor0.getcode(filedata, "A", "a.cpp")); } void redundant_config() { const char filedata[] = "int main() {\n" "#ifdef FOO\n" "#ifdef BAR\n" " std::cout << 1;\n" "#endif\n" "#endif\n" "\n" "#ifdef BAR\n" "#ifdef FOO\n" " std::cout << 2;\n" "#endif\n" "#endif\n" "}\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS(4, (int)actual.size()); ASSERT(actual.find("") != actual.end()); ASSERT(actual.find("BAR") != actual.end()); ASSERT(actual.find("FOO") != actual.end()); ASSERT(actual.find("BAR;FOO") != actual.end()); } void endfile() { const char filedata[] = "char a[] = \"#endfile\";\n" "char b[] = \"#endfile\";\n" "#include \"notfound.h\"\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // Compare results.. ASSERT_EQUALS("char a [ ] = \"#endfile\" ;\nchar b [ ] = \"#endfile\" ;", actual[""]); ASSERT_EQUALS(1, (int)actual.size()); } void dup_defines() { const char filedata[] = "#ifdef A\n" "#define B\n" "#if defined(B) && defined(A)\n" "a\n" "#else\n" "b\n" "#endif\n" "#endif\n"; // Preprocess => actual result.. std::map actual; preprocess(filedata, actual); // B will always be defined if A is defined; the following test // cases should be fixed whenever this other bug is fixed ASSERT_EQUALS(2U, actual.size()); if (actual.find("A") == actual.end()) { ASSERT_EQUALS("A is checked", "failed"); } else { ASSERT_EQUALS("A is checked", "A is checked"); } if (actual.find("A;A;B") != actual.end()) { ASSERT_EQUALS("A;A;B is NOT checked", "failed"); } else { ASSERT_EQUALS("A;A;B is NOT checked", "A;A;B is NOT checked"); } } void invalid_define_1() { std::map actual; preprocess("#define =\n", actual); // don't hang } void invalid_define_2() { // #4036 std::map actual; preprocess("#define () {(int f(x) }\n", actual); // don't hang } void inline_suppression_for_missing_include() { Preprocessor::missingIncludeFlag = false; Settings settings; settings.inlineSuppressions = true; settings.addEnabled("all"); Preprocessor preprocessor(settings, this); std::istringstream src("// cppcheck-suppress missingInclude\n" "#include \"missing.h\"\n" "int x;"); std::string processedFile; std::list cfg; std::list paths; // Don't report that the include is missing errout.str(""); preprocessor.preprocess(src, processedFile, cfg, "test.c", paths); ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS(false, Preprocessor::missingIncludeFlag); } void predefine1() { const std::string src("#if defined X || Y\n" "Fred & Wilma\n" "#endif\n"); std::string actual = preprocessor0.getcode(src, "X=1", "test.c"); ASSERT_EQUALS("\nFred & Wilma", actual); } void predefine2() { const std::string src("#if defined(X) && Y\n" "Fred & Wilma\n" "#endif\n"); { std::string actual = preprocessor0.getcode(src, "X=1", "test.c"); ASSERT_EQUALS("", actual); } { std::string actual = preprocessor0.getcode(src, "X=1;Y=2", "test.c"); ASSERT_EQUALS("\nFred & Wilma", actual); } } void predefine3() { // #2871 - define in source is not used if -D is used const char code[] = "#define X 1\n" "#define Y X\n" "#if (X == Y)\n" "Fred & Wilma\n" "#endif\n"; const std::string actual = preprocessor0.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("\n\n\nFred & Wilma", actual); } void predefine4() { // #3577 const char code[] = "char buf[X];\n"; const std::string actual = preprocessor0.getcode(code, "X=123", "test.c"); ASSERT_EQUALS("char buf [ $123 ] ;", actual); } void predefine5() { // #3737, #5119 - automatically define __cplusplus // #3737... const char code[] = "#ifdef __cplusplus\n123\n#endif"; ASSERT_EQUALS("", preprocessor0.getcode(code, "X=123", "test.c")); ASSERT_EQUALS("\n123", preprocessor0.getcode(code, "X=123", "test.cpp")); } void invalidElIf() { // #2942 - segfault const char code[] = "#elif (){\n"; const std::string actual = preprocessor0.getcode(code, "TEST", "test.c"); ASSERT_EQUALS("", actual); } void getConfigs1() { const char filedata[] = "#ifdef WIN32 \n" " abcdef\n" "#else \n" " qwerty\n" "#endif \n"; ASSERT_EQUALS("\nWIN32\n", getConfigsStr(filedata)); } void getConfigs2() { const char filedata[] = "# ifndef WIN32\n" " \" # ifdef WIN32\" // a comment\n" " # else \n" " qwerty\n" " # endif \n"; ASSERT_EQUALS("\nWIN32\n", getConfigsStr(filedata)); } void getConfigs3() { const char filedata[] = "#ifdef ABC\n" "a\n" "#ifdef DEF\n" "b\n" "#endif\n" "c\n" "#endif\n"; ASSERT_EQUALS("\nABC\nABC;DEF\n", getConfigsStr(filedata)); } void getConfigs4() { const char filedata[] = "#ifdef ABC\n" "A\n" "#endif\t\n" "#ifdef ABC\n" "A\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs5() { const char filedata[] = "#ifdef ABC\n" "A\n" "#else\n" "B\n" "#ifdef DEF\n" "C\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\nDEF\n", getConfigsStr(filedata)); } void getConfigs7() { const char filedata[] = "#ifdef ABC\n" "A\n" "#ifdef ABC\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs7a() { const char filedata[] = "#ifndef ABC\n" "A\n" "#ifndef ABC\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void getConfigs7b() { const char filedata[] = "#ifndef ABC\n" "A\n" "#ifdef ABC\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs7c() { const char filedata[] = "#ifdef ABC\n" "A\n" "#ifndef ABC\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs7d() { const char filedata[] = "#if defined(ABC)\n" "A\n" "#if defined(ABC)\n" "B\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs7e() { const char filedata[] = "#ifdef ABC\n" "#file \"test.h\"\n" "#ifndef test_h\n" "#define test_h\n" "#ifdef ABC\n" "#endif\n" "#endif\n" "#endfile\n" "#endif\n"; ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); } void getConfigs8() { const char filedata[] = "#if A == 1\n" "1\n" "#endif\n"; ASSERT_EQUALS("\nA=1\n", getConfigsStr(filedata)); } void getConfigs10() { // Ticket #5139 const char filedata[] = "#define foo a.foo\n" "#define bar foo\n" "#define baz bar+0\n" "#if 0\n" "#endif"; ASSERT_EQUALS("\n", getConfigsStr(filedata)); } void getConfigsError() { const char filedata1[] = "#ifndef X\n" "#error \"!X\"\n" "#endif\n"; ASSERT_EQUALS("X\n", getConfigsStr(filedata1)); const char filedata2[] = "#ifdef X\n" "#ifndef Y\n" "#error \"!Y\"\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\nX\nY\n", getConfigsStr(filedata2)); } void getConfigsD1() { const char filedata[] = "#ifdef X\n" "#else\n" "#ifdef Y\n" "#endif\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-DX")); ASSERT_EQUALS("\nX\nY\n", getConfigsStr(filedata)); } void getConfigsU1() { const char filedata[] = "#ifdef X\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX\n", getConfigsStr(filedata)); } void getConfigsU2() { const char filedata[] = "#ifndef X\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\n", getConfigsStr(filedata)); // no #else } void getConfigsU3() { const char filedata[] = "#ifndef X\n" "Fred & Wilma\n" "#else\n" "Barney & Betty\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX\n", getConfigsStr(filedata)); } void getConfigsU4() { const char filedata[] = "#if defined(X) || defined(Y) || defined(Z)\n" "#else\n" "#endif\n"; ASSERT_EQUALS("\nY;Z\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX;Y;Z\n", getConfigsStr(filedata)); } void getConfigsU5() { const char filedata[] = "#if X==1\n" "#endif\n"; ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX=1\n", getConfigsStr(filedata)); } void getConfigsU6() { const char filedata[] = "#if X==0\n" "#endif\n"; ASSERT_EQUALS("\nX=0\n", getConfigsStr(filedata, "-UX")); ASSERT_EQUALS("\nX=0\n", getConfigsStr(filedata)); } void getConfigsU7() { const char code[] = "#ifndef Y\n" "#else\n" "#endif\n"; ASSERT_EQUALS("\nY\n", getConfigsStr(code, "-DX")); } void validateCfg1() { Preprocessor preprocessor(settings0, this); std::vector files(1, "test.c"); simplecpp::MacroUsage macroUsage(files, false); macroUsage.useLocation.fileIndex = 0; macroUsage.useLocation.line = 1; macroUsage.macroName = "X"; std::list macroUsageList(1, macroUsage); ASSERT_EQUALS(true, preprocessor.validateCfg("", macroUsageList)); ASSERT_EQUALS(false, preprocessor.validateCfg("X",macroUsageList)); ASSERT_EQUALS(false, preprocessor.validateCfg("A=42;X", macroUsageList)); ASSERT_EQUALS(true, preprocessor.validateCfg("X=1", macroUsageList)); ASSERT_EQUALS(true, preprocessor.validateCfg("Y", macroUsageList)); macroUsageList.front().macroValueKnown = true; // #8404 ASSERT_EQUALS(true, preprocessor.validateCfg("X", macroUsageList)); } void validateCfg2() { const char filedata[] = "#ifdef ABC\n" "#endif\n" "int i = ABC;"; std::map actual; preprocess(filedata, actual, "file.cpp"); ASSERT_EQUALS("[file.cpp:3]: (information) Skipping configuration 'ABC' since the value of 'ABC' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.\n", errout.str()); } void if_sizeof() { // #4071 static const char* code = "#if sizeof(unsigned short) == 2\n" "Fred & Wilma\n" "#elif sizeof(unsigned short) == 4\n" "Fred & Wilma\n" "#else\n" "#endif"; std::map actual; preprocess(code, actual); ASSERT_EQUALS("\nFred & Wilma", actual[""]); } void invalid_ifs() { const char filedata[] = "#ifdef\n" "#endif\n" "#ifdef !\n" "#endif\n" "#if defined\n" "#endif\n" "#define f(x) x\n" "#if f(2\n" "#endif\n" "int x;\n"; // Preprocess => don't crash.. std::map actual; preprocess(filedata, actual); } void garbage() { const char filedata[] = "V\n" "#define X b #endif #line 0 \"x\" ;\n" "#if ! defined ( Y ) #endif"; // Preprocess => don't crash.. std::map actual; preprocess(filedata, actual); } void wrongPathOnErrorDirective() { errout.str(""); Settings settings; settings.userDefines = "foo"; Preprocessor preprocessor(settings, this); const std::string code("#error hello world!\n"); preprocessor.getcode(code, "X", "./././test.c"); ASSERT_EQUALS("[test.c:1]: (error) #error hello world!\n", errout.str()); } void testDirectiveIncludeTypes() { const char filedata[] = "#define macro some definition\n" "#undef macro\n" "#ifdef macro\n" "#elif some (complex) condition\n" "#else\n" "#endif\n" "#if some other condition\n" "#pragma some proprietary content\n" "#\n" /* may appear in old C code */ "#ident some text\n" /* may appear in old C code */ "#unknownmacro some unpredictable text\n" "#warning some warning message\n" "#error some error message\n"; const char dumpdata[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; std::ostringstream ostr; Preprocessor preprocessor(settings0, this); preprocessor.getcode(filedata, "", "test.c"); preprocessor.dump(ostr); ASSERT_EQUALS(dumpdata, ostr.str()); } void testDirectiveIncludeLocations() { const char filedata[] = "#define macro1 val\n" "#file \"inc1.h\"\n" "#define macro2 val\n" "#file \"inc2.h\"\n" "#define macro3 val\n" "#endfile\n" "#define macro4 val\n" "#endfile\n" "#define macro5 val\n"; const char dumpdata[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; std::ostringstream ostr; Preprocessor preprocessor(settings0, this); preprocessor.getcode(filedata, "", "test.c"); preprocessor.dump(ostr); ASSERT_EQUALS(dumpdata, ostr.str()); } void testDirectiveIncludeComments() { const char filedata[] = "#ifdef macro2 /* this will be removed */\n" "#else /* this will be removed too */\n" "#endif /* this will also be removed */\n"; const char dumpdata[] = " \n" " \n" " \n" " \n" " \n"; std::ostringstream ostr; Preprocessor preprocessor(settings0, this); preprocessor.getcode(filedata, "", "test.c"); preprocessor.dump(ostr); ASSERT_EQUALS(dumpdata, ostr.str()); } }; REGISTER_TEST(TestPreprocessor) cppcheck-1.90/test/testrunner.cpp000066400000000000000000000035011357737443600171410ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "options.h" #include "preprocessor.h" #include "testsuite.h" #include #include int main(int argc, char *argv[]) { // MS Visual C++ memory leak debug tracing #if defined(_MSC_VER) && defined(_DEBUG) _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); #endif #ifdef NDEBUG try { #endif Preprocessor::macroChar = '$'; // While macroChar is char(1) per default outside test suite, we require it to be a human-readable character here. options args(argc, argv); if (args.help()) { TestFixture::printHelp(); return EXIT_SUCCESS; } const std::size_t failedTestsCount = TestFixture::runTests(args); return (failedTestsCount == 0) ? EXIT_SUCCESS : EXIT_FAILURE; #ifdef NDEBUG } catch (const InternalError& e) { std::cout << e.errorMessage << std::endl; } catch (const std::exception& error) { std::cout << error.what() << std::endl; } catch (...) { std::cout << "Unknown exception" << std::endl; } return EXIT_FAILURE; #endif } cppcheck-1.90/test/testrunner.vcxproj000077500000000000000000000436051357737443600200660ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {c183db5b-ad6c-423d-80ca-1f9549555a1a} Create Create Create Create {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4} testrunner 10.0 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 Application Unicode false v142 ..\bin\debug\ ..\bin\debug\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ testrunner testrunner true true ..\bin\ ..\bin\ temp\$(Configuration)_$(PlatformName)\ temp\$(Configuration)_$(PlatformName)\ testrunner testrunner true true true true ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4482;4512 true Use testsuite.h testsuite.h true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true true 8000000 8000000 ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) true ProgramDatabase Disabled CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDebugDLL true Level4 4251;4482;4512 true Use testsuite.h testsuite.h true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true Console true 8000000 8000000 ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions) MultiThreadedDLL true Level4 AnySuitable true Speed true true true 4251;4482;4512 true Use testsuite.h testsuite.h /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) false Console true true true true true 8000000 8000000 ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml;%(AdditionalIncludeDirectories) false MaxSpeed CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) MultiThreadedDLL true Level4 AnySuitable true Speed true true true 4251;4482;4512 true Use testsuite.h testsuite.h /Zc:inline /Zc:throwingNew %(AdditionalOptions) true stdcpplatest shlwapi.lib;%(AdditionalDependencies) ../externals;%(AdditionalLibraryDirectories) true true true Console true true true true 8000000 8000000 cppcheck-1.90/test/testrunner.vcxproj.filters000066400000000000000000000203741357737443600215300ustar00rootroot00000000000000 {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files cppcheck-1.90/test/testsamples.cpp000066400000000000000000000142431357737443600173010ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cppcheckexecutor.h" #include "errorlogger.h" #include "cppcheck.h" #include "filelister.h" #include "path.h" #include "pathmatch.h" #include "redirect.h" #include "testsuite.h" #include #include #include #include #include #include #include #include #include #include class TestSamples : public TestFixture { public: TestSamples() : TestFixture("TestSamples") { } private: void run() OVERRIDE { TEST_CASE(runSamples); TEST_CASE(runConsoleCodePageTranslationOnWindows); } void runSamples() const { REDIRECT; std::map files; const std::vector masks; const PathMatch matcher(masks); #ifdef _WIN32 FileLister::recursiveAddFiles(files, "..\\samples", matcher); #else FileLister::recursiveAddFiles(files, "samples", matcher); #endif for (std::map::const_iterator i = files.begin(); i != files.end(); ++i) { if (i->first.find("memleak") != std::string::npos) continue; CLEAR_REDIRECT_ERROUT; const char* const path = i->first.c_str(); const char * const argv[] = { #ifdef _WIN32 ".\\..\\testrunner", #else "./testrunner", #endif "--enable=style,warning,performance,portability", "--inconclusive", "-rp", "-f", "-q", path }; std::string filename = i->first.substr(i->first.find_last_of("/\\")+1); if (filename == "good.cpp" || filename == "good.c") { CppCheckExecutor exec; exec.check(7, argv); ASSERT_EQUALS_MSG("", GET_REDIRECT_ERROUT, i->first); } else if (filename == "bad.cpp" || filename == "bad.c") { CppCheckExecutor exec; exec.check(7, argv); std::string expected_filename = Path::getPathFromFilename(i->first) + "out.txt"; std::ifstream ifs(expected_filename); std::string expected((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); std::string actual = GET_REDIRECT_ERROUT; // We need some uniformization to make this work on Unix and Windows std::replace(actual.begin(), actual.end(), '/', '\\'); // Uniformize slashes. while (actual.find("..\\") != std::string::npos) actual.erase(actual.find("..\\"), 3); // Remove '..\' ASSERT_EQUALS_MSG(expected, actual, i->first); } } } class CppCheckExecutor2 : public CppCheckExecutor { public: void settings(const Settings &set) { setSettings(set); } }; void runConsoleCodePageTranslationOnWindows() const { REDIRECT; std::vector msgs = { "ASCII", // first entry should be using only ASCII "kääk", "Português" // "日本語", // "한국어", // "Русский", // "中文", }; Settings set1; Settings setXML; setXML.xml = true; setXML.xml_version = 2; CppCheckExecutor2 exec; exec.settings(set1); CppCheckExecutor2 execXML; execXML.settings(setXML); for (std::vector::const_iterator i = msgs.begin(); i != msgs.end(); ++i) { CLEAR_REDIRECT_OUTPUT; CLEAR_REDIRECT_ERROUT; exec.reportOut(*i); ErrorLogger::ErrorMessage errMessage; errMessage.setmsg(*i); // no xml option exec.reportInfo(errMessage); #ifdef _WIN32 // expect changes through code page translation except for the 'ASCII' case if (i == msgs.begin()) { ASSERT_EQUALS(*i + "\n", GET_REDIRECT_OUTPUT); ASSERT_EQUALS(*i + "\n", GET_REDIRECT_ERROUT); } else { ASSERT(*i + "\n" != GET_REDIRECT_OUTPUT); ASSERT(*i + "\n" != GET_REDIRECT_ERROUT); } #else // do not expect any code page translation ASSERT_EQUALS(*i + "\n", GET_REDIRECT_OUTPUT); ASSERT_EQUALS(*i + "\n", GET_REDIRECT_ERROUT); #endif CLEAR_REDIRECT_ERROUT; // possible change of msg for xml option // with ErrorLogger::ErrorMessage::fixInvalidChars(), plus additional XML formatting execXML.reportInfo(errMessage); // undo the effects of "ErrorLogger::ErrorMessage::fixInvalidChars()" // replacing octal constants with characters std::string myErr; std::string myErrOrg = GET_REDIRECT_ERROUT; std::string::const_iterator from = myErrOrg.begin(); while (from != myErrOrg.end()) { if (*from == '\\') { ++from; unsigned c; // expect three digits std::istringstream es(std::string(from, from + 3)); es >> std::oct >> c; ++from; ++from; myErr.push_back(c); } else { myErr.push_back(*from); } ++from; } ASSERT(std::string::npos != myErr.find(*i)); } } }; REGISTER_TEST(TestSamples) cppcheck-1.90/test/testsimplifytemplate.cpp000066400000000000000000007505221357737443600212340ustar00rootroot00000000000000/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2019 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include "platform.h" #include "settings.h" #include "templatesimplifier.h" #include "testsuite.h" #include "token.h" #include "tokenize.h" struct InternalError; class TestSimplifyTemplate : public TestFixture { public: TestSimplifyTemplate() : TestFixture("TestSimplifyTemplate") { } private: Settings settings; void run() OVERRIDE { settings.addEnabled("portability"); TEST_CASE(template1); TEST_CASE(template2); TEST_CASE(template3); TEST_CASE(template4); TEST_CASE(template5); TEST_CASE(template6); TEST_CASE(template7); TEST_CASE(template8); TEST_CASE(template9); TEST_CASE(template10); TEST_CASE(template11); TEST_CASE(template12); TEST_CASE(template13); TEST_CASE(template14); TEST_CASE(template15); // recursive templates TEST_CASE(template16); TEST_CASE(template17); TEST_CASE(template18); TEST_CASE(template19); TEST_CASE(template20); TEST_CASE(template21); TEST_CASE(template22); TEST_CASE(template23); TEST_CASE(template24); // #2648 - using sizeof in template parameter TEST_CASE(template25); // #2648 - another test for sizeof template parameter TEST_CASE(template26); // #2721 - passing 'char[2]' as template parameter TEST_CASE(template27); // #3350 - removing unused template in macro call TEST_CASE(template28); TEST_CASE(template30); // #3529 - template < template < .. TEST_CASE(template31); // #4010 - reference type TEST_CASE(template32); // #3818 - mismatching template not handled well TEST_CASE(template33); // #3818,#4544 - inner templates in template instantiation not handled well TEST_CASE(template34); // #3706 - namespace => hang TEST_CASE(template35); // #4074 - A<'x'> a; TEST_CASE(template36); // #4310 - passing unknown template instantiation as template argument TEST_CASE(template37); // #4544 - A a; TEST_CASE(template38); // #4832 - crash on C++11 right angle brackets TEST_CASE(template39); // #4742 - freeze TEST_CASE(template40); // #5055 - template specialization outside struct TEST_CASE(template41); // #4710 - const in instantiation not handled perfectly TEST_CASE(template42); // #4878 - variadic templates TEST_CASE(template43); // #5097 - assert due to '>>' not treated as end of template instantiation TEST_CASE(template44); // #5297 - TemplateSimplifier::simplifyCalculations not eager enough TEST_CASE(template45); // #5814 - syntax error reported for valid code TEST_CASE(template46); // #5816 - syntax error reported for valid code TEST_CASE(template47); // #6023 - syntax error reported for valid code TEST_CASE(template48); // #6134 - 100% CPU upon invalid code TEST_CASE(template49); // #6237 - template instantiation TEST_CASE(template50); // #4272 - simple partial specialization TEST_CASE(template51); // #6172 - crash upon valid code TEST_CASE(template52); // #6437 - crash upon valid code TEST_CASE(template53); // #4335 - bail out for valid code TEST_CASE(template54); // #6587 - memory corruption upon valid code TEST_CASE(template55); // #6604 - simplify "const const" to "const" in template instantiations TEST_CASE(template56); // #7117 - const ternary operator simplification as template parameter TEST_CASE(template57); // #7891 TEST_CASE(template58); // #6021 - use after free (deleted tokens in simplifyCalculations) TEST_CASE(template59); // #8051 - TemplateSimplifier::simplifyTemplateInstantiation failure TEST_CASE(template60); // handling of methods outside template definition TEST_CASE(template61); // daca2, kodi TEST_CASE(template62); // #8314 - inner template instantiation TEST_CASE(template63); // #8576 - qualified type TEST_CASE(template64); // #8683 TEST_CASE(template65); // #8321 TEST_CASE(template66); // #8725 TEST_CASE(template67); // #8122 TEST_CASE(template68); // union TEST_CASE(template69); // #8791 TEST_CASE(template70); // #5289 TEST_CASE(template71); // #8821 TEST_CASE(template72); TEST_CASE(template73); TEST_CASE(template74); TEST_CASE(template75); TEST_CASE(template76); TEST_CASE(template77); TEST_CASE(template78); TEST_CASE(template79); // #5133 TEST_CASE(template80); TEST_CASE(template81); TEST_CASE(template82); // #8603 TEST_CASE(template83); // #8867 TEST_CASE(template84); // #8880 TEST_CASE(template85); // #8902 crash TEST_CASE(template86); // crash TEST_CASE(template87); TEST_CASE(template88); // #6183 TEST_CASE(template89); // #8917 TEST_CASE(template90); // crash TEST_CASE(template91); TEST_CASE(template92); TEST_CASE(template93); // crash TEST_CASE(template94); // #8927 crash TEST_CASE(template95); // #7417 TEST_CASE(template96); // #7854 TEST_CASE(template97); TEST_CASE(template98); // #8959 TEST_CASE(template99); // #8960 TEST_CASE(template100); // #8967 TEST_CASE(template101); // #8968 TEST_CASE(template102); // #9005 TEST_CASE(template103); TEST_CASE(template104); // #9021 TEST_CASE(template105); // #9076 TEST_CASE(template106); TEST_CASE(template107); // #8663 TEST_CASE(template108); // #9109 TEST_CASE(template109); // #9144 TEST_CASE(template110); TEST_CASE(template111); // crash TEST_CASE(template112); // #9146 syntax error TEST_CASE(template113); TEST_CASE(template114); // #9155 TEST_CASE(template115); // #9153 TEST_CASE(template116); // #9178 TEST_CASE(template117); TEST_CASE(template118); TEST_CASE(template119); // #9186 TEST_CASE(template120); TEST_CASE(template121); // #9193 TEST_CASE(template122); // #9147 TEST_CASE(template123); // #9183 TEST_CASE(template124); // #9197 TEST_CASE(template125); TEST_CASE(template126); // #9217 TEST_CASE(template127); // #9225 TEST_CASE(template128); // #9224 TEST_CASE(template129); TEST_CASE(template130); // #9246 TEST_CASE(template131); // #9249 TEST_CASE(template132); // #9250 TEST_CASE(template133); TEST_CASE(template134); TEST_CASE(template135); TEST_CASE(template136); // #9287 TEST_CASE(template137); // #9288 TEST_CASE(template138); TEST_CASE(template139); TEST_CASE(template140); TEST_CASE(template141); // #9337 TEST_CASE(template142); // #9338 TEST_CASE(template143); TEST_CASE(template144); // #9046 TEST_CASE(template145); // syntax error TEST_CASE(template146); // syntax error TEST_CASE(template147); // syntax error TEST_CASE(template148); // syntax error TEST_CASE(template149); // unknown macro TEST_CASE(template150); // syntax error TEST_CASE(template151); // crash TEST_CASE(template152); // #9467 TEST_CASE(template153); // #9483 TEST_CASE(template154); // #9495 TEST_CASE(template155); // #9539 TEST_CASE(template_specialization_1); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) TEST_CASE(template_unhandled); TEST_CASE(template_default_parameter); TEST_CASE(template_forward_declared_default_parameter); TEST_CASE(template_default_type); TEST_CASE(template_typename); TEST_CASE(template_constructor); // #3152 - template constructor is removed TEST_CASE(syntax_error_templates_1); TEST_CASE(template_member_ptr); // Ticket #5786 - crash upon valid code TEST_CASE(template_namespace_1); TEST_CASE(template_namespace_2); TEST_CASE(template_namespace_3); TEST_CASE(template_namespace_4); TEST_CASE(template_namespace_5); TEST_CASE(template_namespace_6); TEST_CASE(template_namespace_7); // #8768 TEST_CASE(template_namespace_8); TEST_CASE(template_namespace_9); TEST_CASE(template_namespace_10); TEST_CASE(template_namespace_11); // #7145 // Test TemplateSimplifier::templateParameters TEST_CASE(templateParameters); TEST_CASE(templateNamePosition); TEST_CASE(findTemplateDeclarationEnd); TEST_CASE(expandSpecialized1); TEST_CASE(expandSpecialized2); TEST_CASE(expandSpecialized3); // #8671 TEST_CASE(expandSpecialized4); TEST_CASE(templateAlias1); TEST_CASE(templateAlias2); TEST_CASE(templateAlias3); // #8315 TEST_CASE(templateAlias4); // #9070 TEST_CASE(templateAlias5); // Test TemplateSimplifier::instantiateMatch TEST_CASE(instantiateMatch); TEST_CASE(templateParameterWithoutName); // #8602 Template default parameter without name yields syntax error TEST_CASE(templateTypeDeduction1); // #8962 TEST_CASE(templateTypeDeduction2); TEST_CASE(simplifyTemplateArgs1); TEST_CASE(simplifyTemplateArgs2); TEST_CASE(template_variadic_1); // #9144 TEST_CASE(template_variable_1); TEST_CASE(template_variable_2); TEST_CASE(template_variable_3); TEST_CASE(template_variable_4); TEST_CASE(simplifyDecltype); } std::string tok(const char code[], bool debugwarnings = false, Settings::PlatformType type = Settings::Native) { errout.str(""); settings.debugwarnings = debugwarnings; settings.platform(type); Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); return tokenizer.tokens()->stringifyList(nullptr, true); } void template1() { const char code[] = "template T f(T val) { T a; }\n" "f(10);"; const char expected[] = "int f ( int val ) ; " "f ( 10 ) ; " "int f ( int val ) { int a ; }"; ASSERT_EQUALS(expected, tok(code)); } void template2() { const char code[] = "template class Fred { T a; };\n" "Fred fred;"; const char expected[] = "class Fred ; " "Fred fred ; " "class Fred { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template3() { const char code[] = "template class Fred { T data[sz]; };\n" "Fred fred;"; const char expected[] = "class Fred ; " "Fred fred ; " "class Fred { float data [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template4() { const char code[] = "template class Fred { Fred(); };\n" "Fred fred;"; const char expected[] = "class Fred ; " "Fred fred ; " "class Fred { Fred ( ) ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template5() { const char code[] = "template class Fred { };\n" "template Fred::Fred() { }\n" "Fred fred;"; const char expected[] = "class Fred ; " "Fred fred ; " "class Fred { } ; " "Fred :: Fred ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template6() { const char code[] = "template class Fred { };\n" "Fred fred1;\n" "Fred fred2;"; const char expected[] = "class Fred ; " "Fred fred1 ; " "Fred fred2 ; " "class Fred { } ;"; ASSERT_EQUALS(expected, tok(code)); } void template7() { // A template class that is not used => no simplification { const char code[] = "template \n" "class ABC\n" "{\n" "public:\n" " typedef ABC m;\n" "};\n"; const char expected[] = "template < class T > class ABC { public: } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template class ABC {\n" "public:\n" " typedef std::vector type;\n" "};\n" "int main() {\n" " ABC::type v;\n" " v.push_back(4);\n" " return 0;\n" "}\n"; const char wanted[] = "class ABC ; " "int main ( ) { " "std :: vector < int > v ; " "v . push_back ( 4 ) ; " "return 0 ; " "} " "class ABC { public: } ;"; const char current[] = "class ABC ; " "int main ( ) { " "ABC :: type v ; " "v . push_back ( 4 ) ; " "return 0 ; " "} " "class ABC { public: } ;"; TODO_ASSERT_EQUALS(wanted, current, tok(code)); } { const char code[] = "template class ABC {\n" "public:\n" " typedef std::vector type;\n" " void f()\n" " {\n" " ABC::type v;\n" " v.push_back(4);\n" " }\n" "};\n"; const char expected[] = "template < typename T > class ABC { " "public: void f ( ) { " "ABC < int > :: type v ; " "v . push_back ( 4 ) ; " "} " "} ;"; ASSERT_EQUALS(expected, tok(code)); } } // Template definitions but no usage => no expansion void template8() { const char code[] = "template class A;\n" "template class B;\n" "\n" "typedef A x;\n" "typedef B y;\n" "\n" "template class A {\n" " void f() {\n" " B a = B::g();\n" " T b = 0;\n" " if (b)\n" " b = 0;\n" " }\n" "};\n" "\n" "template inline B h() { return B(); }\n"; ASSERT_EQUALS("template < typename T > class A ; " "template < typename T > class B ; " "template < typename T > class A { void f ( ) { B < T > a ; a = B < T > :: g ( ) ; T b ; b = 0 ; if ( b ) { b = 0 ; } } } ; " "template < typename T > B < T > h ( ) { return B < T > ( ) ; }", tok(code)); ASSERT_EQUALS("class A { template < typename T > int foo ( T d ) ; } ;", tok("class A{ template int foo(T d);};")); } void template9() { const char code[] = "template < typename T > class A { } ;\n" "\n" "void f ( ) {\n" " A < int > a ;\n" "}\n" "\n" "template < typename T >\n" "class B {\n" " void g ( ) {\n" " A < T > b = A < T > :: h ( ) ;\n" " }\n" "} ;\n"; // The expected result.. const char expected[] = "class A ; " "void f ( ) { A a ; } ; " "class A { } ;"; ASSERT_EQUALS(expected, tok(code)); } void template10() { const char code[] = "template T * foo()\n" "{ return new T[ui]; }\n" "\n" "void f ( )\n" "{\n" " foo<3,int>();\n" "}\n"; // The expected result.. const char expected[] = "int * foo<3,int> ( ) ; " "void f ( ) " "{" " foo<3,int> ( ) ; " "} " "int * foo<3,int> ( ) { return new int [ 3 ] ; }"; ASSERT_EQUALS(expected, tok(code)); } void template11() { const char code[] = "template T * foo()\n" "{ return new T[ui]; }\n" "\n" "void f ( )\n" "{\n" " char * p = foo<3,char>();\n" "}\n"; // The expected result.. const char expected[] = "char * foo<3,char> ( ) ; " "void f ( ) " "{" " char * p ; p = foo<3,char> ( ) ; " "} " "char * foo<3,char> ( ) { return new char [ 3 ] ; }"; ASSERT_EQUALS(expected, tok(code)); } void template12() { const char code[] = "template \n" "class A : public B\n" "{ };\n" "\n" "void f()\n" "{\n" " A<12,12,11> a;\n" "}\n"; const char expected[] = "class A<12,12,11> ; " "void f ( ) " "{" " A<12,12,11> a ; " "} " "class A<12,12,11> : public B < 12 , 12 , 0 > " "{ } ;"; ASSERT_EQUALS(expected, tok(code)); } void template13() { const char code[] = "class BB {};\n" "\n" "template \n" "class AA {\n" "public:\n" " static AA create(T* newObject);\n" " static int size();\n" "};\n" "\n" "class CC { public: CC(AA, int) {} };\n" "\n" "class XX {\n" " AA y;\n" "public:\n" " XX();\n" "};\n" "\n" "XX::XX():\n" " y(AA::create(new CC(AA(), 0)))\n" " {}\n" "\n" "int yy[AA::size()];"; const char expected[] = "class BB { } ; " "class AA ; " "class AA ; " "class CC { public: CC ( AA , int ) { } } ; " "class XX { " "AA y ; " "public: " "XX ( ) ; " "} ; " "XX :: XX ( ) : " "y ( AA :: create ( new CC ( AA ( ) , 0 ) ) ) " "{ } " "int yy [ AA :: size ( ) ] ; " "class AA { " "public: " "static AA create ( BB * newObject ) ; " "static int size ( ) ; " "} ; " "class AA { " "public: " "static AA create ( CC * newObject ) ; " "static int size ( ) ; " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template14() { const char code[] = "template <> void foo()\n" "{ x(); }\n" "\n" "int main()\n" "{\n" "foo();\n" "}\n"; const char expected[] = "void foo ( ) ; " "void foo ( ) " "{ x ( ) ; } " "int main ( ) " "{ foo ( ) ; }"; ASSERT_EQUALS(expected, tok(code)); } void template15() { // recursive templates #3130 etc const char code[] = "template void a()\n" "{\n" " a();\n" "}\n" "\n" "template <> void a<0>()\n" "{ }\n" "\n" "int main()\n" "{\n" " a<2>();\n" " return 0;\n" "}\n"; // The expected result.. const char expected[] = "void a<0> ( ) ; " "void a<2> ( ) ; " "void a<1> ( ) ; " "void a<0> ( ) { } " "int main ( ) " "{ a<2> ( ) ; return 0 ; } " "void a<2> ( ) { a<1> ( ) ; } " "void a<1> ( ) { a<0> ( ) ; }"; ASSERT_EQUALS(expected, tok(code)); // #3130 const char code2[] = "template struct vec {\n" " vec() {}\n" " vec(const vec& v) {}\n" // <- never used don't instantiate "};\n" "\n" "vec<4> v;"; const char expected2[] = "struct vec<4> ; " "vec<4> v ; " "struct vec<4> { " "vec<4> ( ) { } " "vec<4> ( const vec < 4 - 1 > & v ) { } " "} ;"; ASSERT_EQUALS(expected2, tok(code2)); } void template16() { const char code[] = "template void a()\n" "{ }\n" "\n" "template void b()\n" "{ a(); }\n" "\n" "int main()\n" "{\n" " b<2>();\n" " return 0;\n" "}\n"; const char expected[] = "void a<2> ( ) ; " "void b<2> ( ) ; " "int main ( ) { b<2> ( ) ; return 0 ; } " "void b<2> ( ) { a<2> ( ) ; } " "void a<2> ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template17() { const char code[] = "template\n" "class Fred\n" "{\n" " template\n" " static shared_ptr< Fred > CreateFred()\n" " {\n" " }\n" "};\n" "\n" "shared_ptr i;\n"; const char expected[] = "template < class T > " "class Fred " "{ " "template < class T > " "static shared_ptr < Fred < T > > CreateFred ( ) " "{ " "} " "} ; " "shared_ptr < int > i ;"; ASSERT_EQUALS(expected, tok(code)); } void template18() { const char code[] = "template class foo { T a; };\n" "foo *f;"; const char expected[] = "class foo ; " "foo * f ; " "class foo { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template19() { const char code[] = "template T & foo()\n" "{ static T temp; return temp; }\n" "\n" "void f ( )\n" "{\n" " char p = foo();\n" "}\n"; // The expected result.. const char expected[] = "char & foo ( ) ; " "void f ( ) " "{" " char p ; p = foo ( ) ; " "} " "char & foo ( ) { static char temp ; return temp ; }"; ASSERT_EQUALS(expected, tok(code)); } void template20() { // Ticket #1788 - the destructor implementation is lost const char code[] = "template class A { public: ~A(); };\n" "template A::~A() {}\n" "A a;\n"; // The expected result.. const char expected[] = "class A ; " "A a ; " "class A { public: ~ A ( ) ; } ; " "A :: ~ A ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template21() { { const char code[] = "template struct Fred { T a; };\n" "Fred fred;"; const char expected[] = "struct Fred ; " "Fred fred ; " "struct Fred { int a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { T data[sz]; };\n" "Fred fred;"; const char expected[] = "struct Fred ; " "Fred fred ; " "struct Fred { float data [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { Fred(); };\n" "Fred fred;"; const char expected[] = "struct Fred ; " "Fred fred ; " "struct Fred { Fred ( ) ; } ;"; ASSERT_EQUALS(expected, tok(code)); } { const char code[] = "template struct Fred { };\n" "Fred fred1;\n" "Fred fred2;"; const char expected[] = "struct Fred ; " "Fred fred1 ; " "Fred fred2 ; " "struct Fred { } ;"; ASSERT_EQUALS(expected, tok(code)); } } void template22() { const char code[] = "template struct Fred { T a; };\n" "Fred fred;"; const char expected[] = "struct Fred ; " "Fred fred ; " "struct Fred { std :: string a ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template23() { const char code[] = "template void foo() { }\n" "void bar() {\n" " std::cout << (foo());\n" "}"; const char expected[] = "void foo ( ) ; " "void bar ( ) {" " std :: cout << ( foo ( ) ) ; " "} " "void foo ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template24() { // #2648 const char code[] = "template struct B\n" "{\n" " int a[n];\n" "};\n" "\n" "template class bitset: B\n" "{};\n" "\n" "bitset<1> z;"; const char expected[] = "struct B<4> ; " "class bitset<1> ; " "bitset<1> z ; " "class bitset<1> : B<4> { } ; " "struct B<4> { int a [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template25() { const char code[] = "template struct B\n" "{\n" " int a[n];\n" "};\n" "\n" "template class bitset: B<((sizeof(int)) ? : 1)>\n" "{};\n" "\n" "bitset<1> z;"; const char expected[] = "struct B<4> ; " "class bitset<1> ; " "bitset<1> z ; " "class bitset<1> : B<4> { } ; " "struct B<4> { int a [ 4 ] ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template26() { // #2721 const char code[] = "template\n" "class A { public: T x; };\n" "\n" "template\n" "class C: public A {};\n" "\n" "C<2> a;\n"; // TODO: expand A also ASSERT_EQUALS("template < class T > class A { public: T x ; } ; class C<2> ; C<2> a ; class C<2> : public A < char [ 2 ] > { } ;", tok(code)); } void template27() { // #3350 - template inside macro call const char code[] = "X(template class Fred);"; ASSERT_THROW(tok(code), InternalError); } void template28() { // #3226 - inner template const char code[] = "template class Fred {};\n" "Fred > x;\n"; ASSERT_EQUALS("class Fred ; " "class Fred> ; " "Fred> x ; " "class Fred { } ; " "class Fred> { } ;", tok(code)); } void template30() { // #3529 - template < template < .. const char code[] = "template class A, class B> void f(){}"; ASSERT_EQUALS("template < template < class > class A , class B > void f ( ) { }", tok(code)); } void template31() { // #4010 - template reference type const char code[] = "template struct A{}; A a;"; ASSERT_EQUALS("struct A ; " "A a ; " "struct A { } ;", tok(code)); // #7409 - rvalue const char code2[] = "template struct A{}; A a;"; ASSERT_EQUALS("struct A ; " "A a ; " "struct A { } ;", tok(code2)); } void template32() { // #3818 - mismatching template not handled well const char code[] = "template struct A { };\n" "\n" "template \n" "struct B\n" "{\n" " public:\n" " A < int, Pair, int > a;\n" // mismatching parameters => don't instantiate "};\n" "\n" "B b;\n"; ASSERT_EQUALS("template < class T1 , class T2 , class T3 , class T4 > struct A { } ; " "struct B ; " "B b ; " "struct B { public: A < int , Pair < int , int > , int > a ; } ;", tok(code)); } void template33() { { // #3818 - inner templates in template instantiation not handled well const char code[] = "template struct A { };\n" "template struct B { };\n" "template struct C { A > > ab; };\n" "C c;"; ASSERT_EQUALS("struct A>> ; " "struct B> ; " "struct C ; " "C c ; " "struct C { A>> ab ; } ; " "struct B> { } ; " // <- redundant.. but nevermind "struct A>> { } ;", tok(code)); } { // #4544 const char code[] = "struct A { };\n" "template struct B { };\n" "template struct C { };\n" "C< B > c;"; ASSERT_EQUALS("struct A { } ; " "template < class T > struct B { } ; " // <- redundant.. but nevermind "struct C> ; " "C> c ; " "struct C> { } ;", tok(code)); } } void template34() { // #3706 - namespace => hang const char code[] = "namespace abc {\n" "template struct X { void f(X &x) {} };\n" "}\n" "template <> int X::Y(0);"; tok(code); } void template35() { // #4074 - "A<'x'> a;" is not recognized as template instantiation const char code[] = "template class A {};\n" "A <'x'> a;"; ASSERT_EQUALS("class A<'x'> ; " "A<'x'> a ; " "class A<'x'> { } ;", tok(code)); } void template36() { // #4310 - Passing unknown template instantiation as template argument const char code[] = "template struct X { T t; };\n" "template struct Y { Foo < X< Bar > > _foo; };\n" // <- Bar is unknown "Y bar;"; ASSERT_EQUALS("struct X> ; " "struct Y ; " "Y bar ; " "struct Y { Foo < X> > _foo ; } ; " "struct X> { Bar < int > t ; } ;", tok(code)); } void template37() { // #4544 - A a; { const char code[] = "class A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("class A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } { const char code[] = "struct A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("struct A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } { const char code[] = "enum A { };\n" "template class B {};\n" "B b1;\n" "B b2;"; ASSERT_EQUALS("enum A { } ; class B ; B b1 ; B b2 ; class B { } ;", tok(code)); } } void template_unhandled() { // An unhandled template usage should be simplified.. ASSERT_EQUALS("x ( ) ;", tok("x();")); } void template38() { // #4832 - Crash on C++11 right angle brackets const char code[] = "template class A {\n" " T mT;\n" "public:\n" " void foo() {}\n" "};\n" "\n" "int main() {\n" " A> gna1;\n" " A gna2;\n" "}\n"; const char expected[] = "class A ; " "class A> ; " "int main ( ) { " "A> gna1 ; " "A gna2 ; " "} " "class A { " "BLA mT ; " "public: " "void foo ( ) { } " "} ; " "class A> { " "A mT ; " "public: " "void foo ( ) { } " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template39() { // #4742 - Used to freeze in 1.60 const char code[] = "template struct vector {" " operator T() const;" "};" "void f() {" " vector> v;" " const vector vi = static_cast>(v);" "}"; tok(code); } void template40() { // #5055 - false negatives when there is template specialization outside struct const char code[] = "struct A {" " template struct X { T t; };" "};" "template<> struct A::X { int *t; };"; const char expected[] = "struct A { " "struct X ; " "template < typename T > struct X { T t ; } ; " "} ; " "struct A :: X { int * t ; } ;"; ASSERT_EQUALS(expected, tok(code)); } void template41() { // #4710 - const in template instantiation not handled perfectly const char code1[] = "template struct X { };\n" "void f(const X x) { }"; ASSERT_EQUALS("struct X ; " "void f ( const X x ) { } " "struct X { } ;", tok(code1)); const char code2[] = "template T f(T t) { return t; }\n" "int x() { return f(123); }"; ASSERT_EQUALS("int f ( int t ) ; " "int x ( ) { return f ( 123 ) ; } " "int f ( int t ) { return t ; }", tok(code2)); } void template42() { // #4878 cppcheck aborts in ext-blocks.cpp (clang testcode) const char code[] = "template\n" "int f0(Args ...args) {\n" " return ^ {\n" " return sizeof...(Args);\n" " }() + ^ {\n" " return sizeof...(args);\n" " }();\n" "}"; ASSERT_THROW(tok(code), InternalError); } void template43() { // #5097 - Assert due to '>>' in 'B>' not being treated as end of template instantiation const char code[] = "template struct E { typedef int Int; };\n" "template struct C { };\n" "template struct D { static int f() { return C::f(); } };\n" "template inline int f2() { return D::f(); }\n" "template int f1 (int x, T *) { int id = f2(); return id; }\n" "template struct B { void f3(B & other) { } };\n" "struct A { };\n" "template <> struct C> {\n" " static int f() { return f1>(0, reinterpret_cast*>(E::Int(-1))); }\n" "};\n" "int main(void) {\n" " C ca;\n" " return 0;\n" "}"; const char expected[] = "struct E ; " "struct C> ; " "struct C ; " "struct D> ; " "int f2> ( ) ; " "int f1> ( int x , B * ) ; " "struct B ; " "struct A { } ; " "struct C> { " "static int f ( ) { " "return f1> ( 0 , reinterpret_cast < B * > ( E :: Int ( -1 ) ) ) ; " "} " "} ; " "int main ( ) { " "C ca ; " "return 0 ; " "} " "struct B { " "void f3 ( B & other ) { } " "} ; " "int f1> ( int x , B * ) { " "int id ; id = f2> ( ) ; " "return id ; " "} " "int f2> ( ) { " "return D> :: f ( ) ; " "} " "struct D> { " "static int f ( ) { " "return C> :: f ( ) ; " "} " "} ; " "struct C { } ; struct E { " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template44() { // #5297 const char code[] = "template struct StackContainer {" " void foo(int i) {" " if (0 >= 1 && i<0) {}" " }" "};" "template class ZContainer : public StackContainer {};" "struct FGSTensor {};" "class FoldedZContainer : public ZContainer {};"; const char expected[] = "struct StackContainer ; " "class ZContainer ; " "struct FGSTensor { } ; " "class FoldedZContainer : public ZContainer { } ; " "class ZContainer : public StackContainer { } ; " "struct StackContainer { " "void foo ( int i ) { " "if ( 0 >= 1 && i < 0 ) { } " "} " "} ;"; ASSERT_EQUALS(expected, tok(code)); } void template45() { // #5814 const char code[] = "namespace Constants { const int fourtytwo = 42; } " "template struct TypeMath { " " static const int mult = sizeof(T) * U; " "}; " "template struct FOO { " " enum { value = TypeMath::mult }; " "}; " "FOO foo;"; const char expected[] = "namespace Constants { const int fourtytwo = 42 ; } " "struct TypeMath ; " "struct FOO ; " "FOO foo ; " "struct FOO { " "enum Anonymous0 { value = TypeMath :: mult } ; " "} ; " "struct TypeMath { " "static const int mult = sizeof ( int ) * Constants :: fourtytwo ; " "} ;"; ASSERT_EQUALS(expected, tok(code, true)); ASSERT_EQUALS("", errout.str()); } void template46() { // #5816 tok("template struct A { static const int value = 0; }; " "template struct B { " " enum { value = A::value }; " "};"); ASSERT_EQUALS("", errout.str()); tok("template struct A {}; " "enum { e = sizeof(A) }; " "template struct B {};"); ASSERT_EQUALS("", errout.str()); tok("template struct A { static const int value = 0; }; " "template struct B { typedef int type; }; " "template struct C { " " enum { value = A::type, int>::value }; " "};"); ASSERT_EQUALS("", errout.str()); } void template47() { // #6023 tok("template > class C1 {}; " "class C2 : public C1 {};"); ASSERT_EQUALS("", errout.str()); } void template48() { // #6134 tok("template int f( { } ); " "int foo = f<1>(0);"); ASSERT_EQUALS("", errout.str()); } void template49() { // #6237 const char code[] = "template class Fred { void f(); void g(); };\n" "template void Fred::f() { }\n" "template void Fred::g() { }\n" "template void Fred::f();\n" "template void Fred::g();\n"; const char expected[] = "class Fred ; " "class Fred ; " "class Fred { void f ( ) ; void g ( ) ; } ; " "void Fred :: f ( ) { } " "void Fred :: g ( ) { } " "class Fred { void f ( ) ; void g ( ) ; } ; " "void Fred :: f ( ) { } " "void Fred :: g ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template50() { // #4272 const char code[] = "template class Fred { void f(); };\n" "template void Fred::f() { }\n" "template<> void Fred::f() { }\n" "template<> void Fred::f() { }\n"; const char expected[] = "class Fred ; " "class Fred ; " "template < > void Fred :: f ( ) { } " "template < > void Fred :: f ( ) { } " "class Fred { void f ( ) ; } ; " "void Fred :: f ( ) { } " "class Fred { void f ( ) ; } ; " "void Fred :: f ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template51() { // #6172 tok("template struct A { " " static void foo() { " " int i = N; " " } " "}; " "void bar() { " " A<0>::foo(); " "}"); } void template52() { // #6437 const char code[] = "template int sum() { " " return value + sum(); " "} " "template int calculate_value() { " " if (x != y) { " " return sum(); " " } else { " " return 0; " " } " "} " "int value = calculate_value<1,1>();"; const char expected[] = "int sum<0> ( ) ; " "int calculate_value<1,1> ( ) ; " "int value ; value = calculate_value<1,1> ( ) ; " "int calculate_value<1,1> ( ) { " "if ( 1 != 1 ) { " "return sum<0> ( ) ; " "} else { " "return 0 ; " "} " "} " "int sum<0> ( ) { " "return 0 + sum<0> ( ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); } void template53() { // #4335 const char code[] = "template struct Factorial { " " enum { value = N * Factorial::value }; " "};" "template <> struct Factorial<0> { " " enum { value = 1 }; " "};" "const int x = Factorial<4>::value;"; const char expected[] = "struct Factorial<0> ; " "struct Factorial<4> ; " "struct Factorial<3> ; " "struct Factorial<2> ; " "struct Factorial<1> ; " "struct Factorial<0> { " "enum Anonymous1 { value = 1 } ; " "} ; " "const int x = Factorial<4> :: value ; " "struct Factorial<4> { " "enum Anonymous0 { value = 4 * Factorial<3> :: value } ; " "} ; " "struct Factorial<3> { " "enum Anonymous0 { value = 3 * Factorial<2> :: value } ; " "} ; " "struct Factorial<2> { " "enum Anonymous0 { value = 2 * Factorial<1> :: value } ; " "} ; " "struct Factorial<1> { " "enum Anonymous0 { value = 1 * Factorial<0> :: value } ; " "} ;"; ASSERT_EQUALS(expected, tok(code, true)); ASSERT_EQUALS("", errout.str()); } void template54() { // #6587 tok("template _Tp* fn(); " "template struct A { " " template ())> " " struct B { }; " "}; " "A a;"); } void template55() { // #6604 // Avoid constconstconst in macro instantiations ASSERT_EQUALS( "template < class T > class AtSmartPtr : public ConstCastHelper < AtSmartPtr < const T > , T > { " "friend struct ConstCastHelper < AtSmartPtr < const T > , T > ; " "AtSmartPtr ( const AtSmartPtr < T > & r ) ; " "} ;", tok("template class AtSmartPtr : public ConstCastHelper, T>\n" "{\n" " friend struct ConstCastHelper, T>;\n" " AtSmartPtr(const AtSmartPtr& r);\n" "};")); // Similar problem can also happen with ... ASSERT_EQUALS( "struct A ; " "struct A ; " "A a ( 0 ) ; " "struct A { " "A ( int * p ) { ( A * ) ( p ) ; } " "} ; " "struct A { " "A ( int * p ) { " "( A * ) ( p ) ; " "} } ;", tok("template struct A\n" "{\n" " A(T* p) {\n" " (A*)(p);\n" " }\n" "};\n" "A a(0);")); } void template56() { // #7117 const char code[] = "template struct Foo { " " std::array mfoo; " "}; " "void foo() { " " Foo myFoo; " "}"; const char expected[] = "struct Foo ; " "void foo ( ) { " "Foo myFoo ; " "} struct Foo { " "std :: array < int , 1 > mfoo ; " "} ;"; ASSERT_EQUALS(expected, tok(code, true)); ASSERT_EQUALS("", errout.str()); } void template57() { // #7891 const char code[] = "template struct Test { Test(T); };\n" "Test test( 0 );"; const char exp [] = "struct Test ; " "Test test ( 0 ) ; " "struct Test { Test ( unsigned long ) ; } ;"; ASSERT_EQUALS(exp, tok(code)); } void template58() { // #6021 const char code[] = "template \n" "void TestArithmetic() {\n" " x(1 * CheckedNumeric());\n" "}\n" "void foo() {\n" " TestArithmetic();\n" "}"; const char exp[] = "void TestArithmetic ( ) ; " "void foo ( ) {" " TestArithmetic ( ) ; " "} " "void TestArithmetic ( ) {" " x ( 1 * CheckedNumeric < int > ( ) ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template59() { // #8051 const char code[] = "template\n" "struct Factorial {\n" " enum FacHelper { value = N * Factorial::value };\n" "};\n" "template <>\n" "struct Factorial<0> {\n" " enum FacHelper { value = 1 };\n" "};\n" "template\n" "int diagonalGroupTest() {\n" " return Factorial::value;\n" "}\n" "int main () {\n" " return diagonalGroupTest<4>();\n" "}"; const char exp[] = "struct Factorial<0> ; " "struct Factorial<4> ; " "struct Factorial<3> ; " "struct Factorial<2> ; " "struct Factorial<1> ; " "struct Factorial<0> { enum FacHelper { value = 1 } ; } ; " "int diagonalGroupTest<4> ( ) ; " "int main ( ) { return diagonalGroupTest<4> ( ) ; } " "int diagonalGroupTest<4> ( ) { return Factorial<4> :: value ; } " "struct Factorial<4> { enum FacHelper { value = 4 * Factorial<3> :: value } ; } ; " "struct Factorial<3> { enum FacHelper { value = 3 * Factorial<2> :: value } ; } ; " "struct Factorial<2> { enum FacHelper { value = 2 * Factorial<1> :: value } ; } ; " "struct Factorial<1> { enum FacHelper { value = 1 * Factorial<0> :: value } ; } ;"; ASSERT_EQUALS(exp, tok(code)); } void template60() { // Extracted from Clang testfile const char code[] = "template struct S { typedef int type; };\n" "template void f() {}\n" "template void h() { f::type(0)>(); }\n" "\n" "void j() { h(); }"; const char exp[] = "struct S ; " "void f::type(0)> ( ) ; " "void h ( ) ; " "void j ( ) { h ( ) ; } " "void h ( ) { f::type(0)> ( ) ; } " "struct S { } ; " "void f::type(0)> ( ) { }"; const char act[] = "template < typename T > struct S { } ; " "void f::type(0)> ( ) ; " "void h ( ) ; " "void j ( ) { h ( ) ; } " "void h ( ) { f::type(0)> ( ) ; } " "void f::type(0)> ( ) { }"; TODO_ASSERT_EQUALS(exp, act, tok(code)); } void template61() { // hang in daca, code extracted from kodi const char code[] = "template struct Foo {};\n" "template struct Bar {\n" " void f1(Bar x) {}\n" " Foo> f2() { }\n" "};\n" "Bar c;"; const char exp[] = "struct Foo> ; " "struct Bar ; " "Bar c ; " "struct Bar {" " void f1 ( Bar x ) { }" " Foo> f2 ( ) { } " "} ; " "struct Foo> { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template62() { // #8314 const char code[] = "template struct C1 {};\n" "template void f() { x = y ? C1::allocate(1) : 0; }\n" "template class C3 {};\n" "template C3::C3(const C3 &v) { C1 c1; }\n" "C3 c3;"; const char exp[] = "struct C1 ; " "class C3 ; " "C3 c3 ; " "class C3 { } ; " "C3 :: C3 ( const C3 & v ) { C1 c1 ; } " "struct C1 { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template63() { // #8576 const char code[] = "template struct TestClass { T m_hi; };" "TestClass> objTest3;"; const char exp[] = "struct TestClass> ; " "TestClass> objTest3 ; " "struct TestClass> { std :: auto_ptr < v > m_hi ; } ;"; ASSERT_EQUALS(exp, tok(code)); } void template64() { // #8683 const char code[] = "template \n" "bool foo(){return true;}\n" "struct A {\n" "template\n" "void t_func()\n" "{\n" " if( n != 0 || foo());\n" "}\n" "void t_caller()\n" "{\n" " t_func<0>();\n" " t_func<1>();\n" "}\n" "};"; const char exp [] = "bool foo ( ) ; " "struct A { " "void t_func<0> ( ) ; " "void t_func<1> ( ) ; " "void t_caller ( ) " "{ " "t_func<0> ( ) ; " "t_func<1> ( ) ; " "} " "} ; " "void A :: t_func<0> ( ) " "{ " "if ( 0 != 0 || foo ( ) ) { ; } " "} " "void A :: t_func<1> ( ) " "{ " "if ( 1 != 0 || foo ( ) ) { ; } " "} " "bool foo ( ) { return true ; }"; ASSERT_EQUALS(exp, tok(code)); } void template65() { // #8321 (crash) const char code[] = "namespace bpp\n" "{\n" "template\n" "class AssociationDAGraphImplObserver :\n" " public AssociationGraphImplObserver\n" "{};\n" "template\n" "using AssociationDAGlobalGraphObserver = AssociationDAGraphImplObserver;\n" "}\n" "using namespace bpp;\n" "using namespace std;\n" "int main() {\n" " AssociationDAGlobalGraphObserver grObs;\n" " return 1;\n" "}"; const char exp [] = "namespace bpp " "{ " "class AssociationDAGraphImplObserver ; " "} " "using namespace bpp ; " "int main ( ) { " "bpp :: AssociationDAGraphImplObserver grObs ; " "return 1 ; " "} class bpp :: AssociationDAGraphImplObserver : " "public AssociationGraphImplObserver < std :: string , unsigned int , DAGlobalGraph > " "{ } ;"; ASSERT_EQUALS(exp, tok(code)); } void template66() { // #8725 const char code[] = "template struct Fred {\n" " const int ** foo();\n" "};\n" "template const int ** Fred::foo() { return nullptr; }\n" "Fred fred;"; const char exp [] = "struct Fred ; " "Fred fred ; " "struct Fred { " "const int * * foo ( ) ; " "} ; " "const int * * Fred :: foo ( ) { return nullptr ; }"; ASSERT_EQUALS(exp, tok(code)); } void template67() { // ticket #8122 const char code[] = "template struct Container {\n" " Container();\n" " Container(const Container &);\n" " Container & operator = (const Container &);\n" " ~Container();\n" " T* mElements;\n" " const Container * c;\n" "};\n" "template Container::Container() : mElements(nullptr), c(nullptr) {}\n" "template Container::Container(const Container & x) { nElements = x.nElements; c = x.c; }\n" "template Container & Container::operator = (const Container & x) { mElements = x.mElements; c = x.c; return *this; }\n" "template Container::~Container() {}\n" "Container intContainer;"; const char expected[] = "struct Container ; " "Container intContainer ; " "struct Container { " "Container ( ) ; " "Container ( const Container & ) ; " "Container & operator= ( const Container & ) ; " "~ Container ( ) ; " "int * mElements ; " "const Container * c ; " "} ; " "Container :: Container ( ) : mElements ( nullptr ) , c ( nullptr ) { } " "Container :: Container ( const Container & x ) { nElements = x . nElements ; c = x . c ; } " "Container & Container :: operator= ( const Container & x ) { mElements = x . mElements ; c = x . c ; return * this ; } " "Container :: ~ Container ( ) { }"; ASSERT_EQUALS(expected, tok(code)); } void template68() { const char code[] = "template union Fred {\n" " char dummy[sizeof(T)];\n" " T value;\n" "};\n" "Fred fred;"; const char exp [] = "union Fred ; " "Fred fred ; " "union Fred { " "char dummy [ sizeof ( int ) ] ; " "int value ; " "} ;"; ASSERT_EQUALS(exp, tok(code)); } void template69() { // #8791 const char code[] = "class Test {\n" " int test;\n" " template T lookup() { return test; }\n" " int Fun() { return lookup(); }\n" "};"; const char exp [] = "class Test { " "int test ; " "int lookup ( ) ; " "int Fun ( ) { return lookup ( ) ; } " "} ; " "int Test :: lookup ( ) { return test ; }"; ASSERT_EQUALS(exp, tok(code)); } void template70() { // #5289 const char code[] = "template class Bar;\n" "template<>\n" "class Bar {\n" "};\n" "template\n" "class Bar : private Bar {\n" " void foo() { }\n" "};"; const char exp [] = "template < typename T , typename V , int KeySize = 0 > class Bar ; " "class Bar ; " "class Bar { " "} ; " "template < typename K , typename V , int KeySize = 0 > " "class Bar : private Bar { " "void foo ( ) { } " "} ;"; const char act [] = "template < typename T , typename V , int KeySize = 0 > class Bar ; " "class Bar { " "} ; " "class Bar ; " "template < typename K , typename V , int KeySize = 0 > " "class Bar : private Bar { " "void foo ( ) { } " "} ;"; TODO_ASSERT_EQUALS(exp, act, tok(code)); } void template71() { // #8821 const char code[] = "int f1(int * pInterface, int x) { return 0; }\n" "\n" "template< class interface_type > class Reference {\n" " template< class interface_type > int i();\n" " int *pInterface;\n" "};\n" "\n" "template< class interface_type > int Reference< interface_type >::i() {\n" " return f1(pInterface, interface_type::static_type());\n" "}\n" "\n" "Reference< class XPropertyList > dostuff();"; const char exp [] = "int f1 ( int * pInterface , int x ) { return 0 ; } " "class Reference ; " "Reference dostuff ( ) ; " "class Reference { template < class XPropertyList > int i ( ) ; int * pInterface ; } ; " "int Reference :: i ( ) { return f1 ( pInterface , XPropertyList :: static_type ( ) ) ; }"; ASSERT_EQUALS(exp, tok(code)); } void template72() { const char code[] = "template class Tokenizer;\n" "const Tokenizer *tokenizer() const;\n" "template \n" "Tokenizer::Tokenizer() { }"; const char exp [] = "template < typename N , typename P > class Tokenizer ; " "const Tokenizer < Node , Path > * tokenizer ( ) const ; " "template < typename N , typename P > " "Tokenizer < N , P > :: Tokenizer ( ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template73() { const char code[] = "template\n" "void keep_range(T& value, const T mini, const T maxi){}\n" "template void keep_range(float& v, const float l, const float u);\n" "template void keep_range(int& v, const int l, const int u);"; const char exp[] = "void keep_range ( float & value , const float mini , const float maxi ) ; " "void keep_range ( int & value , const int mini , const int maxi ) ; " "void keep_range ( float & value , const float mini , const float maxi ) { } " "void keep_range ( int & value , const int mini , const int maxi ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template74() { const char code[] = "template class BTlist { };\n" "class PushBackStreamBuf {\n" "public:\n" " void pushBack(const BTlist &vec);\n" "};"; const char exp[] = "class BTlist ; " "class PushBackStreamBuf { " "public: " "void pushBack ( const BTlist & vec ) ; " "} ; " "class BTlist { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template75() { const char code[] = "template\n" "T foo(T& value){ return value; }\n" "template std::vector> foo>>(std::vector>& v);"; const char exp[] = "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) ; " "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) { return value ; }"; ASSERT_EQUALS(exp, tok(code)); } void template76() { const char code[] = "namespace NS {\n" " template T foo(T& value) { return value; }\n" " template std::vector> foo>>(std::vector>& v);\n" "}\n" "std::vector> v;\n" "v = foo>>(v);\n"; const char exp[] = "namespace NS { " "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) ; " "} " "std :: vector < std :: vector < int > > v ; " "v = foo>> ( v ) ; " "std :: vector < std :: vector < int > > NS :: foo>> ( std :: vector < std :: vector < int > > & value ) { return value ; }"; ASSERT_EQUALS(exp, tok(code)); } void template77() { const char code[] = "template\n" "struct is_void : std::false_type { };\n" "template<>\n" "struct is_void : std::true_type { };\n" "int main() {\n" " std::cout << is_void::value << std::endl;\n" " std::cout << is_void::value << std::endl;\n" "}"; const char exp[] = "struct is_void ; " "struct is_void ; " "struct is_void : std :: true_type { } ; " "int main ( ) { " "std :: cout << is_void :: value << std :: endl ; " "std :: cout << is_void :: value << std :: endl ; " "} " "struct is_void : std :: false_type { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template78() { const char code[] = "template \n" "struct Base { };\n" "struct S : Base ::Type { };"; const char exp[] = "struct Base ; " "struct S : Base :: Type { } ; " "struct Base { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template79() { // #5133 const char code[] = "class Foo {\n" "public:\n" " template void foo() { bar(); }\n" "private:\n" " template void bar() { bazz(); }\n" " void bazz() { }\n" "};\n" "void some_func() {\n" " Foo x;\n" " x.foo();\n" "}"; const char exp[] = "class Foo { " "public: " "void foo ( ) ; " "private: " "void bar ( ) ; " "void bazz ( ) { } " "} ; " "void some_func ( ) { " "Foo x ; " "x . foo ( ) ; " "} " "void Foo :: foo ( ) { bar ( ) ; } " "void Foo :: bar ( ) { bazz ( ) ; }"; ASSERT_EQUALS(exp, tok(code)); } void template80() { const char code[] = "class Fred {\n" " template T foo(T t) const { return t; }\n" "};\n" "const void * p = Fred::foo(nullptr);"; const char exp[] = "class Fred { " "const void * foo ( const void * t ) const ; " "} ; " "const void * p ; p = Fred :: foo ( nullptr ) ; " "const void * Fred :: foo ( const void * t ) const { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } void template81() { const char code[] = "template \n" "struct SortWith {\n" " SortWith(Type);\n" "};\n" "template \n" "SortWith::SortWith(Type) {}\n" "int main() {\n" " SortWith(0);\n" "}"; const char exp[] = "template < typename Type > " "struct SortWith { " "SortWith ( Type ) ; " "} ; " "SortWith :: SortWith ( int ) ; " "int main ( ) { " "SortWith ( 0 ) ; " "} " "SortWith :: SortWith ( int ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template82() { // 8603 const char code[] = "typedef int comp;\n" "const int f16=16;\n" "template\n" "class tvec2 {};\n" "template\n" "class tvec3 {};\n" "namespace swizzle {\n" "template void swizzle(tvec2 v) { }\n" "template void swizzle(tvec3 v) { }\n" "}\n" "void foo() {\n" " using namespace swizzle;\n" " tvec2 tt2;\n" " swizzle<1>(tt2);\n" " tvec3 tt3;\n" " swizzle<2,3>(tt3);\n" "}"; const char exp[] = "const int f16 = 16 ; " "class tvec2 ; " "class tvec3 ; " "namespace swizzle { " "void swizzle<1> ( tvec2 v ) ; " "void swizzle<2,3> ( tvec3 v ) ; " "} " "void foo ( ) { " "using namespace swizzle ; " "tvec2 tt2 ; " "swizzle :: swizzle<1> ( tt2 ) ; " "tvec3 tt3 ; " "swizzle :: swizzle<2,3> ( tt3 ) ; " "} " "void swizzle :: swizzle<2,3> ( tvec3 v ) { } " "void swizzle :: swizzle<1> ( tvec2 v ) { } " "class tvec3 { } ; " "class tvec2 { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template83() { // #8867 const char code[] = "template\n" "class MultiConsumer {\n" " MultiConsumer();\n" "};\n" "template\n" "MultiConsumer::MultiConsumer() : sizeBuffer(0) {}\n" "MultiReads::MultiReads() {\n" " mc = new MultiConsumer();\n" "}"; const char exp[] = "template < typename Task > " // TODO: this should be expanded "class MultiConsumer { " "MultiConsumer ( ) ; " "} ; " "MultiConsumer :: MultiConsumer ( ) ; " "MultiReads :: MultiReads ( ) { " "mc = new MultiConsumer ( ) ; " "} " "MultiConsumer :: MultiConsumer ( ) : sizeBuffer ( 0 ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template84() { // #8880 { const char code[] = "template \n" "auto d() -> typename a::e {\n" " d();\n" "}"; const char exp[] = "template < class b , int c , class > " "auto d ( ) . a < decltype ( b { } ) > :: e { " "d < int , c , int > ( ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "template \n" "auto d() -> typename a::e {\n" " d();\n" "}" "void foo() { d(); }"; const char exp[] = "auto d ( ) . a < char > :: e ; " "auto d ( ) . a < int > :: e ; " "void foo ( ) { d ( ) ; } " "auto d ( ) . a < char > :: e { " "d ( ) ; " "} " "auto d ( ) . a < int > :: e { " "d ( ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } } void template85() { // #8902 - crash const char code[] = "template\n" "struct C\n" "{\n" " template::value)>::type* = nullptr>\n" " void foo();\n" "};\n" "extern template void C::foo();\n" "template\n" "template::value)>::type>\n" "void C::foo() {}"; // @todo the output is very wrong but we are only worried about the crash for now tok(code); } void template86() { // crash const char code[] = "struct S {\n" " S();\n" "};\n" "template \n" "struct U {\n" " static S u;\n" "};\n" "template \n" "S U::u;\n" "template S U::u;\n" "S &i = U::u;"; tok(code); } void template87() { const char code[] = "template\n" "T f1(T t) { return t; }\n" "template const char * f1();\n" "template const char & f1();"; const char exp[] = "const char * f1 ( const char * t ) ; " "const char & f1 ( const char & t ) ; " "const char * f1 ( const char * t ) { return t ; } " "const char & f1 ( const char & t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } void template88() { // #6183.cpp const char code[] = "class CTest {\n" "public:\n" " template \n" " static void Greeting(T val) {\n" " std::cout << val << std::endl;\n" " }\n" "private:\n" " static void SayHello() {\n" " std::cout << \"Hello World!\" << std::endl;\n" " }\n" "};\n" "template<>\n" "void CTest::Greeting(bool) {\n" " CTest::SayHello();\n" "}\n" "int main() {\n" " CTest::Greeting(true);\n" " return 0;\n" "}"; const char exp[] = "class CTest { " "public: " "static void Greeting ( bool ) ; " "template < typename T > " "static void Greeting ( T val ) { " "std :: cout << val << std :: endl ; " "} " "private: " "static void SayHello ( ) { " "std :: cout << \"Hello World!\" << std :: endl ; " "} " "} ; " "void CTest :: Greeting ( bool ) { " "CTest :: SayHello ( ) ; " "} " "int main ( ) { " "CTest :: Greeting ( true ) ; " "return 0 ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template89() { // #8917 const char code[] = "struct Fred {\n" " template static void foo() { }\n" "};\n" "template void Fred::foo();\n" "template void Fred::foo();\n" "template <> void Fred::foo() { }\n" "template <> void Fred::foo() { }"; const char exp[] = "struct Fred { " "static void foo ( ) ; " "static void foo ( ) ; " "static void foo ( ) ; " "static void foo ( ) ; " "} ; " "void Fred :: foo ( ) { } " "void Fred :: foo ( ) { } " "void Fred :: foo ( ) { } " "void Fred :: foo ( ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template90() { // crash const char code[] = "template struct S1 {};\n" "void f(S1) {}\n" "template \n" "decltype(S1().~S1()) fun1() {};"; const char exp[] = "struct S1 ; " "void f ( S1 ) { } " "template < typename T > " "decltype ( S1 < T > ( ) . ~ S1 < T > ( ) ) fun1 ( ) { } ; " "struct S1 { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template91() { { const char code[] = "template T foo(T t) { return t; }\n" "template<> char foo(char a) { return a; }\n" "template<> int foo(int a) { return a; }\n" "template float foo(float);\n" "template double foo(double);"; const char exp[] = "int foo ( int a ) ; " "char foo ( char a ) ; " "float foo ( float t ) ; " "double foo ( double t ) ; " "char foo ( char a ) { return a ; } " "int foo ( int a ) { return a ; } " "float foo ( float t ) { return t ; } " "double foo ( double t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "struct Fred {\n" " template T foo(T t) { return t; }\n" " template<> char foo(char a) { return a; }\n" " template<> int foo(int a) { return a; }\n" "};\n" "template float Fred::foo(float);\n" "template double Fred::foo(double);"; const char exp[] = "struct Fred { " "int foo ( int a ) ; " "char foo ( char a ) ; " "float foo ( float t ) ; " "double foo ( double t ) ; " "char foo ( char a ) { return a ; } " "int foo ( int a ) { return a ; } " "} ; " "float Fred :: foo ( float t ) { return t ; } " "double Fred :: foo ( double t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "namespace NS1 {\n" " namespace NS2 {\n" " template T foo(T t) { return t; }\n" " template<> char foo(char a) { return a; }\n" " template<> int foo(int a) { return a; }\n" " }\n" " template float NS2::foo(float);\n" "}\n" "template double NS1::NS2::foo(double);"; const char exp[] = "namespace NS1 { " "namespace NS2 { " "int foo ( int a ) ; " "char foo ( char a ) ; " "float foo ( float t ) ; " "double foo ( double t ) ; " "char foo ( char a ) { return a ; } " "int foo ( int a ) { return a ; } " "} " "} " "float NS1 :: NS2 :: foo ( float t ) { return t ; } " "double NS1 :: NS2 :: foo ( double t ) { return t ; }"; ASSERT_EQUALS(exp, tok(code)); } } void template92() { const char code[] = "template void foo(T const& t) { }\n" "template<> void foo(double const& d) { }\n" "template void foo(float const& f);\n" "int main() {\n" " foo(2);\n" " foo(3.14);\n" " foo(3.14f);\n" "}"; const char exp[] = "void foo ( const double & d ) ; " "void foo ( const float & t ) ; " "void foo ( const int & t ) ; " "void foo ( const double & d ) { } " "int main ( ) { " "foo ( 2 ) ; " "foo ( 3.14 ) ; " "foo ( 3.14f ) ; " "} " "void foo ( const float & t ) { } " "void foo ( const int & t ) { }"; ASSERT_EQUALS(exp, tok(code)); } void template93() { // crash const char code[] = "template \n" "void ForEach() { }\n" "template \n" "class Vector2 : public Vector {\n" " template \n" " void ForEach();\n" "public:\n" " void process();\n" "};\n" "template \n" "void Vector2::process() {\n" " ForEach();\n" "}\n" "Vector2 c;"; const char exp[] = "void ForEach ( ) ; " "class Vector2 ; " "Vector2 c ; " "class Vector2 : public Vector { " "template < typename Iterator > " "void ForEach ( ) ; " "public: " "void process ( ) ; " "} ; " "void Vector2 :: process ( ) { " "ForEach ( ) ; " "} " "void ForEach ( ) { " "}"; ASSERT_EQUALS(exp, tok(code)); } void template94() { // #8927 crash const char code[] = "template \n" "class Array { };\n" "template\n" "Array foo() {};\n" "template <> Array foo() { }\n" "template <> Array> foo>() { }\n" "template <> Array foo() { }\n" "template < typename T >\n" "Array matmul() {\n" " return foo( );\n" "}\n" "template Array> matmul>();"; const char exp[] = "class Array ; " "class Array> ; " "class Array ; " "Array foo ( ) ; " "Array> foo> ( ) ; " "Array foo ( ) ; " "template < typename T > " "Array < T > foo ( ) { } ; " "Array foo ( ) { } " "Array> foo> ( ) { } " "Array foo ( ) { } " "Array> matmul> ( ) ; " "Array> matmul> ( ) { " "return foo> ( ) ; " "} " "class Array { } ; " "class Array> { } ; " "class Array { } ;"; ASSERT_EQUALS(exp, tok(code)); } void template95() { // #7417 const char code[] = "template \n" "T Value = 123;\n" "template<>\n" "int Value = 456;\n" "float f = Value;\n" "int i = Value;"; const char exp[] = "float Value ; Value = 123 ; " "int Value ; Value = 456 ; " "float f ; f = Value ; " "int i ; i = Value ;"; ASSERT_EQUALS(exp, tok(code)); } void template96() { // #7854 { const char code[] = "template\n" " constexpr long fib = fib + fib;\n" "template<>\n" " constexpr long fib<0> = 0;\n" "template<>\n" " constexpr long fib<1> = 1;\n" "long f0 = fib<0>;\n" "long f1 = fib<1>;\n" "long f2 = fib<2>;\n" "long f3 = fib<3>;"; const char exp[] = "const long fib<2> = fib<1> + fib<0> ; " "const long fib<3> = fib<2> + fib<1> ; " "const long fib<0> = 0 ; " "const long fib<1> = 1 ; " "long f0 ; f0 = fib<0> ; " "long f1 ; f1 = fib<1> ; " "long f2 ; f2 = fib<2> ; " "long f3 ; f3 = fib<3> ;"; ASSERT_EQUALS(exp, tok(code)); } { const char code[] = "template\n" " constexpr long fib = fib + fib;\n" "template<>\n" " constexpr long fib<0> = 0;\n" "template<>\n" " constexpr long fib<1> = 1;\n" "long f5 = fib<5>;\n"; const char exp[] = "const long fib<5> = fib<4> + fib<3> ; " "const long fib<4> = fib<3> + fib<2> ; " "const long fib<3> = fib<2> + fib<1> ; " "const long fib<2> = fib<1> + fib<0> ; " "const long fib<0> = 0 ; " "const long fib<1> = 1 ; " "long f5 ; f5 = fib<5> ;"; ASSERT_EQUALS(exp, tok(code)); } } void template97() { const char code[] ="namespace NS1 {\n" " namespace NS2 {\n" " namespace NS3 {\n" " namespace NS4 {\n" " template\n" " class Fred {\n" " T * t;\n" " public:\n" " Fred() : t(nullptr) {}\n" " };\n" " }\n" " using namespace NS4;\n" " Fred fred_bool;\n" " NS4::Fred fred_char;\n" " }\n" " using namespace NS3;\n" " NS4::Fred fred_short;\n" " using namespace NS3::NS4;\n" " Fred fred_int;\n" " NS3::NS4::Fred fred_long;\n" " NS2::NS3::NS4::Fred fred_float;\n" " NS1::NS2::NS3::NS4::Fred fred_double;\n" " }\n" " using namespace NS2;\n" " NS3::NS4::Fred fred_float1;\n" " NS2::NS3::NS4::Fred fred_double1;\n" "}\n" "using namespace NS1::NS2::NS3::NS4;\n" "Fred fred_bool1;\n" "NS1::NS2::NS3::NS4::Fred fred_int1;"; const char exp[] = "namespace NS1 { " "namespace NS2 { " "namespace NS3 { " "namespace NS4 { " "class Fred ; " "class Fred ; " "class Fred ; " "class Fred ; " "class Fred ; " "class Fred ; " "class Fred ; " "} " "using namespace NS4 ; " "NS4 :: Fred fred_bool ; " "NS4 :: Fred fred_char ; " "} " "using namespace NS3 ; " "NS3 :: NS4 :: Fred fred_short ; " "using namespace NS3 :: NS4 ; " "NS3 :: NS4 :: Fred fred_int ; " "NS3 :: NS4 :: Fred fred_long ; " "NS2 :: NS3 :: NS4 :: Fred fred_float ; " "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_double ; " "} " "using namespace NS2 ; " "NS2 :: NS3 :: NS4 :: Fred fred_float1 ; " "NS2 :: NS3 :: NS4 :: Fred fred_double1 ; " "} " "using namespace NS1 :: NS2 :: NS3 :: NS4 ; " "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_bool1 ; " "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_int1 ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "bool * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "char * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "short * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "int * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "long * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "float * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ; " "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " "double * t ; " "public: " "Fred ( ) : t ( nullptr ) { } " "} ;"; ASSERT_EQUALS(exp, tok(code)); } void template98() { // #8959 const char code[] = "template \n" "using unique_ptr_with_deleter = std::unique_ptr>;\n" "class A {};\n" "static void func() {\n" " unique_ptr_with_deleter tmp(new A(), [](A* a) {\n" " delete a;\n" " });\n" "}"; const char exp[] = "class A { } ; " "static void func ( ) { " "std :: unique_ptr < A , std :: function < void ( A * ) > > tmp ( new A ( ) , [ ] ( A * a ) { " "delete a ; " "} ) ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template99() { // #8960 const char code[] = "template \n" "class Base {\n" "public:\n" " using ArrayType = std::vector>;\n" "};\n" "using A = Base;\n" "static A::ArrayType array;\n"; const char exp[] = "class Base ; " "static std :: vector < Base > array ; " "class Base { " "public: " "} ;"; ASSERT_EQUALS(exp, tok(code)); } void template100() { // #8967 const char code[] = "enum class Device { I2C0, I2C1 };\n" "template \n" "const char* deviceFile;\n" "template <>\n" "const char* deviceFile = \"/tmp/i2c-0\";\n"; const char exp[] = "enum class Device { I2C0 , I2C1 } ; " "template < Device D > " "const char * deviceFile ; " "const char * deviceFile ; deviceFile = \"/tmp/i2c-0\" ;"; ASSERT_EQUALS(exp, tok(code)); } void template101() { // #8968 const char code[] = "class A {\n" "public:\n" " using ArrayType = std::vector;\n" " void func(typename ArrayType::size_type i) {\n" " }\n" "};"; const char exp[] = "class A { " "public: " "void func ( std :: vector < int > :: size_type i ) { " "} " "} ;"; ASSERT_EQUALS(exp, tok(code)); ASSERT_EQUALS("", errout.str()); } void template102() { // #9005 const char code[] = "namespace ns {\n" "template \n" "struct is_floating_point \n" ": std::integral_constant::value || true>\n" "{};\n" "}\n" "void f() {\n" " if(std::is_floating_point::value) {}\n" "}"; const char exp[] = "namespace ns { " "template < class T > " "struct is_floating_point " ": std :: integral_constant < bool , std :: is_floating_point < T > :: value || true > " "{ } ; " "} " "void f ( ) { " "if ( std :: is_floating_point < float > :: value ) { } " "}"; ASSERT_EQUALS(exp, tok(code)); } void template103() { const char code[] = "namespace sample {\n" " template \n" " class Sample {\n" " public:\n" " T function(T t);\n" " };\n" " template \n" " T Sample::function(T t) {\n" " return t;\n" " }\n" "}\n" "sample::Sample s1;"; const char exp[] = "namespace sample { " "class Sample ; " "} " "sample :: Sample s1 ; " "class sample :: Sample { " "public: " "int function ( int t ) ; " "} ; " "int sample :: Sample :: function ( int t ) { " "return t ; " "}"; ASSERT_EQUALS(exp, tok(code)); } void template104() { // #9021 const char code[] = "template < int i >\n" "auto key ( ) { return hana :: test :: ct_eq < i > { } ; }\n" "template < int i >\n" "auto val ( ) { return hana :: test :: ct_eq < - i > { } ; }\n" "template < int i , int j >\n" "auto p ( ) { return :: minimal_product ( key < i > ( ) , val < j > ( ) ) ; }\n" "int main ( ) {\n" " BOOST_HANA_CONSTANT_CHECK ( hana :: equal (\n" " hana :: at_key ( hana :: make_map ( p < 0 , 0 > ( ) ) , key < 0 > ( ) ) ,\n" " val < 0 > ( ) ) ) ;\n" "}"; const char exp[] = "auto key<0> ( ) ; " "auto val<0> ( ) ; " "auto p<0,0> ( ) ; " "int main ( ) { " "BOOST_HANA_CONSTANT_CHECK ( hana :: equal ( " "hana :: at_key ( hana :: make_map ( p<0,0> ( ) ) , key<0> ( ) ) , " "val<0> ( ) ) ) ; " "} " "auto p<0,0> ( ) { return :: minimal_product ( key<0> ( ) , val<0> ( ) ) ; } " "auto val<0> ( ) { return hana :: test :: ct_eq < - 0 > { } ; } " "auto key<0> ( ) { return hana :: test :: ct_eq < 0 > { } ; }"; ASSERT_EQUALS(exp, tok(code)); } void template105() { // #9076 const char code[] = "template